├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .vscode └── settings.json ├── 404.html ├── Baselink.js ├── Contributors.md ├── IndexPage.js ├── LICENSE ├── README.md ├── add-topics.html ├── addcomment.js ├── contact.html ├── css ├── 404.css ├── Scroll.css ├── add-topics.css ├── bootstrap-icons.css ├── bootstrap.min.css ├── contact.css ├── edit-insight.css ├── edit-profile.css ├── profile.css ├── publicprofile.css ├── style.css ├── topics-detail.css └── topics-listing.css ├── edit-insight.html ├── edit-profile.html ├── feedbackform.css ├── feedbackform.html ├── feedbackform.js ├── fetchcomment.js ├── fonts ├── bootstrap-icons.woff └── bootstrap-icons.woff2 ├── images ├── OIP (5).jpeg ├── OIP (7).jpeg ├── OIP (8).jpeg ├── Screenshot1.png ├── Screenshot2.png ├── Screenshot3.png ├── businesswoman-using-tablet-analysis.jpg ├── colleagues-working-cozy-office-medium-shot.jpg ├── faq_graphic.jpg ├── favicon.png ├── rear-view-young-college-student.jpg ├── robot-state-3.png └── topics │ ├── colleagues-working-cozy-office-medium-shot.png │ ├── undraw_Compose_music_re_wpiw.png │ ├── undraw_Educator_re_ju47.png │ ├── undraw_Finance_re_gnv2.png │ ├── undraw_Graduation_re_gthn.png │ ├── undraw_Group_video_re_btu7.png │ ├── undraw_Podcast_audience_re_4i5q.png │ ├── undraw_Redesign_feedback_re_jvm0.png │ ├── undraw_Remote_design_team_re_urdx.png │ ├── undraw_happy_music_g6wc.png │ ├── undraw_online_ad_re_ol62.png │ └── undraw_viral_tweet_gndb.png ├── index.html ├── indexhtmlbackup.txt ├── js ├── add-topics.js ├── bootstrap.bundle.min.js ├── click-scroll.js ├── contacts.js ├── custom.js ├── edit-insight.js ├── edit-profile.js ├── jquery.min.js ├── jquery.sticky.js ├── login.js ├── profile.js ├── publicprofile.js ├── signup.js ├── topics-detail.js └── topics-listing.js ├── package-lock.json ├── package.json ├── profile.html ├── puplicprofile.html ├── script.js ├── server ├── .env.sample ├── .gitignore ├── api │ ├── app.js │ ├── dbconnect.js │ └── index.js ├── controller │ ├── authController.js │ ├── comment.controller.js │ ├── insight.controller.js │ └── profile.controller.js ├── deplyment.md ├── middleware │ └── auth.middleware.js ├── models │ ├── User.js │ ├── comment.model.js │ ├── contact.js │ └── insight.model.js ├── package-lock.json ├── package.json ├── routes │ ├── authRoutes.js │ ├── comment.router.js │ ├── contact.route.js │ ├── insight.route.js │ └── profile.routes.js ├── utils │ └── cloudinary.utils.js └── vercel.json ├── summary.js ├── testimonial.css ├── topics-detail.html ├── topics-listing.html └── trending topics_and_footer.txt /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 🐛Describe the bug 11 | 12 | 13 | 14 | ## 📌 Expected behavior 15 | 16 | 17 | ## 📷 Screenshots 18 | 19 | 20 | 21 | **Additional context(if any)** 22 | 23 | ## 🏆Are you contributing under any open-source program ? 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "✨ Feature request" 3 | about: Add features to the website 4 | title: Feature_Request 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## ✨ Describe the feature you are requesting 11 | 12 | 13 | 14 | ## 📷 Screenshots 15 | 16 | 17 | 18 | **Additional context(if any)** 19 | 20 | ## 🏆Are you contributing under any open-source program ? 21 | 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PULL REQUEST 2 | 3 | #### Closes # 4 | 5 | 6 | 7 | ## 📌 Description 8 | 9 | 10 | 11 | ## 📷 Screenshots 12 | 13 | 14 | 15 | **Additional context(if any)** 16 | 17 | ## 🏆Are you contributing under any open-source program ? 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .vercel 3 | /node_modules -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 404 - Page Not Found 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | error-404-img 24 |
25 |

Oops!

26 |

We couldn't find the page you're looking for.

27 | 28 | Go Home 29 | 30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Baselink.js: -------------------------------------------------------------------------------- 1 | const link = "https://insight-sync-u1bq.vercel.app" 2 | // const link = "http://localhost:3000" 3 | export {link} -------------------------------------------------------------------------------- /Contributors.md: -------------------------------------------------------------------------------- 1 | # Contributors Guide 2 | 3 | Welcome to the repository! We're excited to have you contribute. Please follow the guidelines below to ensure smooth collaboration and project development. 4 | 5 | --- 6 | 7 | ## How to Contribute 8 | 9 | 1. **Raising Issues:** 10 | 11 | - If you identify a bug or have an enhancement idea, feel free to raise an issue. 12 | - Wait for the issue to be reviewed and assigned by the maintainers. 13 | 14 | 2. **Looking for Issues to Work On:** 15 | 16 | - Check out the issues labeled with **`help wanted`**. These are open for contributors. 17 | - Make sure to comment on the issue you'd like to work on before starting, so it can be assigned to you. 18 | 19 | 3. **Submitting Pull Requests (PRs):** 20 | 21 | - After being assigned an issue, fork the repository, make your changes, and submit a PR. 22 | - Clearly describe the changes you've made in the PR description. 23 | 24 | --- 25 | 26 | ## Labels for apertre2.0 Contributors 27 | 28 | We use the following labels to help contributors navigate through the repository: 29 | 30 | - **`Easy`**: Beginner-friendly issues to help you get started. 31 | - **`Medium`**: Issues with moderate complexity. 32 | - **`Hard`**: Issues for experienced contributors. 33 | - **`help needed`**: Issues actively looking for contributors. 34 | - **`apertre2.0`**: Issues specifically marked for apertre2.0 participants. 35 | 36 | --- 37 | 38 | ## Discussions and Commenting Guidelines 39 | 40 | To maintain a clear history of all issues and discussions: 41 | 42 | 1. **Under Each Issue:** 43 | 44 | - Use the comment section for progress updates, queries, and suggestions related to the issue. 45 | - Avoid off-topic discussions to keep the issue tracker organized. 46 | 47 | 2. **Starting a Discussion:** 48 | 49 | - Use the Discussions tab for general questions, ideas, or feedback not directly related to an issue. 50 | - Always be respectful and constructive in your comments. 51 | 52 | --- 53 | 54 | Thank you for your interest in contributing to this project. Happy contributing! 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Dishika Vaishkiyar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InsightSync - Exploring Ideas, Expanding Horizons 2 | 3 | InsightSync is a platform designed to bring ideas to life through meaningful discussions. It connects curious minds, allowing users to share perspectives, explore diverse topics, and gain valuable insights. Whether you want to contribute your thoughts or learn from others, InsightSync makes knowledge-sharing effortless and engaging. 4 | 5 | ## 🌟 Open Source Recognition 6 | 7 | InsightSync has been a part of **various open-source programs**, providing contributors a platform to **collaborate, learn, and build impactful solutions** together. 8 | 9 | ### 🚀 Programs We Participated In 10 | 11 | | Program | Description | Logo | 12 | |--------------|------------|------| 13 | | **SWoC Season 5** | InsightSync was part of SWoC Season 5, providing a space for contributors to collaborate, learn, and grow while building meaningful solutions together. | | 14 | | **Aperture 2.0** | InsightSync is currently part of Aperture 2.0, bringing developers together to build, innovate, and make an impact in the open-source community. | | 15 | 16 | 17 | ### 🚀 Want to Contribute? 18 | 19 | We welcome contributions! If you're interested in contributing, please check out the [`Contributors.md`](./Contributors.md) file for guidelines on how to get started. 20 | 21 | Happy coding! 💻✨ 22 | 23 | --- 24 | 25 | ### ✨ Features 26 | 27 | - **Curated Content:** Access a wide range of articles and resources on diverse topics. 28 | - **User-Friendly Interface:** Easy navigation and an engaging design enhance the user experience. 29 | - **Community Discussions:** Connect with other users, share insights, and participate in discussions. 30 | - **Free Access:** All features and content are available for free, promoting inclusive learning. 31 | 32 | 48 | 49 | ## 🛠 Getting Started 50 | 51 | ### To get started with InsightSync: 52 | 53 | 1. **Clone the Repository:** 54 | Clone the project to your local machine using the following command: 55 | 56 | ```bash 57 | git clone https://github.com/yourusername/insightsync.git 58 | 2. **Navigate to the Project Directory:** 59 | 60 | ```bash 61 | cd insightsync 62 | 3. **Create a New Branch:** 63 | Create a new branch for your feature or bug fix. Use a descriptive name: 64 | 65 | ```bash 66 | git checkout -b feature/YourFeatureName 67 | 4. **Perform Your Tasks:** 68 | Make the necessary changes and implement your feature or fix. 69 | 70 | 5. **Commit Your Changes:** 71 | Add and commit your changes with a meaningful message: 72 | 73 | ```bash 74 | git add . 75 | git commit -m "Add a brief description of your changes" 76 | 6. **Push Your Changes:** 77 | Push your branch to the remote repository: 78 | 79 | ```bash 80 | git push origin feature/YourFeatureName 81 | 7. **Create a Pull Request:** 82 | Once your changes are pushed, submit a pull request for review. 83 | 84 | ## ⚙️ Setting Up the Backend 85 | 86 | 1. Create a `.env` File 87 | 88 | To store your environment variables securely, navigate to the `server` folder and create a `.env` file. This file will store your MongoDB connection string. 89 | 90 | #### Format of `.env` File: 91 | ```bash 92 | MONGO_URI="your_mongodb_connection_url" 93 | ``` 94 | 95 | Make sure to replace `your_mongodb_connection_url` with your actual MongoDB URL. Keep this file private and do not share it publicly. 96 | 97 | --- 98 | 99 | 2. Navigate to the `server` Folder 100 | 101 | Before running the server, ensure that you are inside the `server` directory. You can check your current directory using: 102 | ```bash 103 | pwd # For macOS/Linux 104 | cd # For Windows (shows current directory) 105 | ``` 106 | If you are not in the `server` folder, navigate to it using: 107 | ```bash 108 | cd server 109 | ``` 110 | 111 | --- 112 | 113 | 3. Start the Server 114 | 115 | Once inside the `server` folder, you can start the server using `nodemon`. Run the following command: 116 | ```bash 117 | nodemon api/index.js 118 | ``` 119 | This will start your backend server, ensuring that any code changes will automatically restart the server. 120 | 121 | If `nodemon` is not installed, you can install it globally using: 122 | ```bash 123 | npm install -g nodemon 124 | ``` 125 | 126 | Now if you want to deploy the backend check [Deployment.md](./server/deplyment.md) 127 | 128 | --- 129 | 130 | 131 | ## 🎉 Meet the Contributors 132 | 133 | 134 | 135 | --- 136 | 137 | ### 🔥 We're always open to more contributions! Join us and be a part of our journey. 🚀 138 | -------------------------------------------------------------------------------- /addcomment.js: -------------------------------------------------------------------------------- 1 | import { link } from "./Baselink.js"; 2 | import { fetchComments } from "./fetchcomment.js"; 3 | const params = new URLSearchParams(window.location.search); 4 | const insightId = params.get('id'); 5 | 6 | document.getElementById("comment-form").addEventListener("submit", async function (event) { 7 | event.preventDefault(); 8 | 9 | const commentText = document.getElementById("comment-text").value.trim(); 10 | const token = localStorage.getItem("authToken"); // Assuming token is stored here 11 | 12 | if (!commentText) { 13 | alert("Please enter a comment."); 14 | return; 15 | } 16 | 17 | if (!token) { 18 | alert("You need to be logged in to comment."); 19 | return; 20 | } 21 | 22 | try { 23 | await axios.post(`${link}/api/v1/comment/addcomment`, 24 | { 25 | i_id: insightId, 26 | comment: commentText 27 | }, 28 | { 29 | headers: { Authorization: `Bearer ${token}` } 30 | } 31 | ); 32 | 33 | document.getElementById("comment-text").value = ""; // Clear input field 34 | fetchComments(); // Refresh comments 35 | } catch (error) { 36 | console.error("Error adding comment:", error); 37 | alert("Failed to add comment. Please try again."); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /css/404.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | background-image: linear-gradient(15deg, #13547a 0%, #80d0c7 100%); 4 | color: #ffffff; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: center; 9 | text-align: center; 10 | height: 100vh; 11 | } 12 | .container { 13 | width: fit-content; 14 | height: fit-content; 15 | display: flex; 16 | flex-direction: column; 17 | justify-content: center; 18 | align-items: center; 19 | gap: 1rem; 20 | } 21 | p { 22 | line-height: 1rem; 23 | } 24 | 25 | .heading { 26 | font-size: 2.2rem; 27 | font-weight: 600; 28 | margin: 2px 0; 29 | } 30 | .subheading { 31 | font-size: 1.2rem; 32 | } 33 | a { 34 | text-decoration: none; 35 | padding: 10px 20px; 36 | color: #000000; 37 | background-color: #71bfbd; 38 | border-radius: 5px; 39 | font-weight: bold; 40 | transition: background-color 0.3s; 41 | } 42 | a:hover { 43 | background-color: #13547A; 44 | color: #fff; 45 | 46 | } 47 | 48 | @media screen and (max-width: 750px) { 49 | .illustration { 50 | width: 19rem; 51 | } 52 | } 53 | @media screen and (min-width: 751px) { 54 | .illustration { 55 | width: 30rem; 56 | } 57 | } 58 | .illustration img { 59 | width: 100%; 60 | } -------------------------------------------------------------------------------- /css/Scroll.css: -------------------------------------------------------------------------------- 1 | /* Scroll */ 2 | #scrollButton { 3 | position: fixed; 4 | bottom: 40px; 5 | left: 15px; 6 | width: 60px; 7 | height: 60px; 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | cursor: pointer; 12 | z-index: 1000; 13 | opacity: 0; /* Initially hidden */ 14 | pointer-events: none; /* Prevent interaction when hidden */ 15 | transition: opacity 0.7s ease; /* Smooth fade-in effect */ 16 | } 17 | 18 | 19 | #scrollButton.visible { 20 | opacity: 1; /* Fully visible */ 21 | pointer-events: auto; /* Enable interaction */ 22 | } 23 | 24 | .outer-circle { 25 | position: relative; 26 | width: 46px; 27 | height: 46px; 28 | border-radius: 50%; 29 | background: conic-gradient( 30 | #80d0c7 0deg, 31 | #13547a var(--scroll-progress, 0deg), 32 | #e9363600 var(--scroll-progress, 0deg), 33 | #b3575700 360deg 34 | ); 35 | transform: rotate(-90deg); /* Start animation from top */ 36 | display: flex; 37 | align-items: center; 38 | justify-content: center; 39 | transition: background 0.3s ease; 40 | } 41 | 42 | .inner-circle { 43 | width: 30px; 44 | height: 30px; 45 | background: white; 46 | border-radius: 50%; 47 | display: flex; 48 | align-items: center; 49 | justify-content: center; 50 | box-shadow: 0 4px 6px rgb(0 0 0 / 66%); 51 | } 52 | 53 | .arrow { 54 | font-size: 18px; 55 | color: #0075f4; 56 | font-weight: 1000; /* Extreme weight for more boldness */ 57 | text-shadow: 2px 2px 4px rgb(0 0 0 / 45%); /* Add shadow to make it appear thicker */ 58 | transform: rotate(90deg); /* Fixed direction to the right */ 59 | } 60 | -------------------------------------------------------------------------------- /css/add-topics.css: -------------------------------------------------------------------------------- 1 | .editor-container { 2 | margin-bottom: 10px; 3 | } 4 | .preview { 5 | border: 1px solid #ccc; 6 | padding: 10px; 7 | min-height: 120px; 8 | background: #f9f9f9; 9 | } 10 | textarea { 11 | width: 100%; 12 | height: 120px; 13 | font-size: 12px; 14 | padding: 10px; 15 | border: 1px solid #ccc; 16 | } 17 | button { 18 | margin-top: 10px; 19 | padding: 5px 10px; 20 | cursor: pointer; 21 | } 22 | 23 | /* Notification container */ 24 | .notification-container { 25 | position: fixed; 26 | top: 20px; 27 | right: 20px; 28 | display: none; 29 | z-index: 9999; 30 | } 31 | 32 | /* Notification message */ 33 | .notification-message { 34 | background-color: #28a745; /* Green for success */ 35 | color: white; 36 | padding: 15px; 37 | border-radius: 5px; 38 | font-size: 1rem; 39 | box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); 40 | opacity: 1; 41 | animation: fadeIn 0.5s ease-out, fadeOut 2s 3s ease-out; 42 | } 43 | 44 | /* Animation for fade-in and fade-out */ 45 | @keyframes fadeIn { 46 | from { opacity: 0; } 47 | to { opacity: 1; } 48 | } 49 | 50 | @keyframes fadeOut { 51 | from { opacity: 1; } 52 | to { opacity: 0; } 53 | } 54 | -------------------------------------------------------------------------------- /css/contact.css: -------------------------------------------------------------------------------- 1 | .notification { 2 | position: fixed; 3 | top: 10px; 4 | left: 50%; 5 | transform: translateX(-50%); 6 | background-color: #4CAF50; /* Green for success */ 7 | color: white; 8 | padding: 15px; 9 | border-radius: 5px; 10 | font-size: 16px; 11 | display: none; 12 | z-index: 9999; 13 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 14 | opacity: 0; 15 | transition: opacity 0.5s ease-in-out; 16 | } 17 | 18 | .notification.show { 19 | display: block; 20 | opacity: 1; 21 | } 22 | 23 | .notification.error { 24 | background-color: #f44336; /* Red for error */ 25 | } -------------------------------------------------------------------------------- /css/edit-insight.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Montserrat', sans-serif; 3 | background: linear-gradient(to bottom right, rgb(101, 169, 171), rgb(200, 230, 231)); 4 | background-repeat: no-repeat; 5 | background-size: cover; 6 | margin: 0; 7 | padding: 0; 8 | } 9 | 10 | .topics-detail-section { 11 | padding: 50px 0; 12 | } 13 | 14 | .topic-header { 15 | text-align: center; 16 | font-weight: 600; 17 | } 18 | 19 | .topic-image { 20 | display: block; 21 | margin: 20px auto; 22 | max-width: 100%; 23 | height: auto; 24 | border: 2px solid #ccc; 25 | border-radius: 10px; 26 | } 27 | 28 | .content-area { 29 | margin-top: 20px; 30 | text-align: center; 31 | } 32 | 33 | button#submitbtn { 34 | display: block; 35 | margin: 20px auto; 36 | padding: 10px 20px; 37 | font-size: 16px; 38 | background-color: #007bff; 39 | color: #fff; 40 | border: none; 41 | border-radius: 5px; 42 | cursor: pointer; 43 | transition: background-color 0.3s; 44 | } 45 | 46 | button#submitbtn:hover { 47 | background-color: #0056b3; 48 | } 49 | 50 | .title-input { 51 | margin-bottom: 20px; 52 | text-align: center; 53 | } -------------------------------------------------------------------------------- /css/edit-profile.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Montserrat', sans-serif; 3 | background: linear-gradient(to bottom right, rgb(101, 169, 171), rgb(200, 230, 231)); 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | .profile-container { 9 | max-width: 900px; 10 | margin: 50px auto; 11 | background: white; 12 | border-radius: 10px; 13 | padding: 30px; 14 | box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1); 15 | } 16 | 17 | .profile-title { 18 | font-size: 28px; 19 | color: rgb(101, 169, 171); 20 | margin-bottom: 20px; 21 | text-transform: uppercase; 22 | font-weight: 700; 23 | } 24 | 25 | .form-group { 26 | margin-bottom: 20px; 27 | } 28 | 29 | .form-control { 30 | padding: 10px; 31 | font-size: 16px; 32 | } 33 | 34 | .btn-submit { 35 | background-color: rgb(101, 169, 171); 36 | color: white; 37 | font-size: 16px; 38 | padding: 10px 20px; 39 | border: none; 40 | border-radius: 5px; 41 | transition: background-color 0.3s ease; 42 | } 43 | 44 | .btn-submit:hover { 45 | background-color: rgb(80, 135, 136); 46 | } 47 | 48 | .profile-image { 49 | height: 200px; 50 | width: 200px; 51 | margin-bottom: 20px; 52 | } 53 | 54 | .profile-image img { 55 | object-fit: cover; 56 | height: 100%; 57 | width: 100%; 58 | border-radius: 50%; 59 | } 60 | 61 | .notification { 62 | position: fixed; 63 | top: 10px; 64 | right: 10px; 65 | padding: 10px 20px; 66 | border-radius: 5px; 67 | color: white; 68 | font-size: 16px; 69 | z-index: 9999; 70 | transition: opacity 0.3s ease; 71 | } -------------------------------------------------------------------------------- /css/profile.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Montserrat", sans-serif; 3 | background: linear-gradient( 4 | to bottom right, 5 | rgb(101, 169, 171), 6 | rgb(200, 230, 231) 7 | ); 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | .profile-container { 13 | display: grid; 14 | grid-template-columns: 2fr; 15 | max-width: 100%; 16 | margin: 50px auto; 17 | background: rgb(101, 169, 171); 18 | border-radius: 10px; 19 | padding: 30px; 20 | box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1); 21 | text-align: center; 22 | transition: transform 0.3s ease-in-out; 23 | overflow: visible; 24 | } 25 | 26 | .profile-container:hover { 27 | box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); 28 | } 29 | 30 | .profile-left-subcontainer { 31 | display: flex; 32 | flex-direction: column; 33 | position: fixed; 34 | top: 21%; 35 | left: 3%; 36 | max-width: 30%; 37 | max-height: fit-content; 38 | background-color: white; 39 | padding: 20px; 40 | border-radius: 30px; 41 | } 42 | 43 | .profile-right-subcontainer{ 44 | position: relative; 45 | left: 31%; 46 | max-width: 70%; 47 | background-color: white; 48 | border-radius: 30px; 49 | } 50 | .profile-title { 51 | font-size: 28px; 52 | color: rgb(101, 169, 171); 53 | margin-bottom: 20px; 54 | text-transform: uppercase; 55 | font-weight: 700; 56 | } 57 | 58 | .profile-title:hover{ 59 | color: black; 60 | } 61 | 62 | .profile-item { 63 | margin: 15px 0; 64 | font-size: 18px; 65 | color: #333; 66 | } 67 | 68 | .profile-item span { 69 | font-weight: bold; 70 | color: rgb(101, 169, 171); 71 | } 72 | 73 | .insights-container { 74 | margin-top: 30px; 75 | height: 100vh; 76 | overflow-y: auto; 77 | } 78 | 79 | .insight-card { 80 | background: linear-gradient( 81 | to right, 82 | rgb(240, 248, 255), 83 | rgb(200, 230, 231) 84 | ); 85 | border-radius: 8px; 86 | padding: 20px; 87 | margin: 10px auto; 88 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); 89 | text-align: left; 90 | overflow: auto; 91 | transition: transform 0.3s ease, box-shadow 0.3s ease; 92 | } 93 | 94 | .insight-card:hover { 95 | transform: translateY(-5px); 96 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); 97 | } 98 | 99 | .insight-card h3 { 100 | font-size: 22px; 101 | color: rgb(101, 169, 171); 102 | margin-bottom: 10px; 103 | } 104 | 105 | .insight-card p { 106 | font-size: 16px; 107 | color: #555; 108 | } 109 | 110 | .insight-card img { 111 | max-width: 100%; 112 | height: auto; 113 | border-radius: 8px; 114 | margin-top: 10px; 115 | transition: transform 0.3s ease; 116 | } 117 | 118 | .insight-card img:hover { 119 | transform: scale(1.01); 120 | } 121 | 122 | nav { 123 | background: rgb(101, 169, 171); 124 | } 125 | 126 | .navbar-brand span { 127 | color: white; 128 | font-weight: bold; 129 | } 130 | 131 | .nav-link { 132 | color: white; 133 | margin-right: 15px; 134 | font-weight: 500; 135 | transition: color 0.3s ease; 136 | } 137 | 138 | .nav-link:hover { 139 | color: rgba(255, 255, 255, 0.8); 140 | } 141 | 142 | .navbar-toggler-icon { 143 | background-color: white; 144 | } 145 | 146 | .profile-image { 147 | height: 200px; 148 | transition: transform 0.2s ease-in-out; 149 | } 150 | 151 | .profile-image img { 152 | object-fit: cover; 153 | height: 200px; 154 | width: 190px; 155 | border: 2px solid rgb(101, 169, 171); 156 | border-radius: 1000px; 157 | transition: transform 0.2s ease-in-out; 158 | } 159 | 160 | .profile-image img:hover { 161 | transform: scale(1.07); 162 | transform: rotate(10deg); 163 | border: 2px solid rgb(101, 169, 171); 164 | } 165 | 166 | #notification-container { 167 | position: fixed; 168 | top: 10px; 169 | right: 10px; 170 | width: 300px; 171 | z-index: 1000; 172 | } 173 | 174 | .notification { 175 | background: rgb(101, 169, 171); 176 | color: white; 177 | padding: 15px; 178 | border-radius: 8px; 179 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 180 | margin-bottom: 10px; 181 | font-size: 16px; 182 | transition: transform 0.3s ease, opacity 0.3s ease; 183 | } 184 | 185 | .notification.success { 186 | background: #28a745; 187 | } 188 | 189 | .notification.error { 190 | background: #dc3545; 191 | } 192 | 193 | .notification.info { 194 | background: #007bff; 195 | } 196 | 197 | .notification.fade-out { 198 | transform: translateX(100%); 199 | opacity: 0; 200 | } 201 | 202 | #notification { 203 | font-family: Arial, sans-serif; 204 | animation: slideIn 0.5s ease-out; 205 | } 206 | 207 | @media screen and (max-width: 992px) { 208 | .profile-container { 209 | flex-direction: column; 210 | max-width: 100%; 211 | } 212 | 213 | .profile-left-subcontainer { 214 | max-width: 100%; 215 | position: static; 216 | margin-bottom: 40px; 217 | } 218 | 219 | .profile-right-subcontainer { 220 | margin-top: 20px; 221 | position: static; 222 | min-width: 100%; 223 | } 224 | 225 | .profile-image { 226 | height: 250px; 227 | } 228 | 229 | .profile-image img { 230 | height: 250px; 231 | width: 240px; 232 | } 233 | 234 | .profile-item { 235 | font-size: 20px; 236 | } 237 | 238 | .insight-card { 239 | padding: 20px; 240 | } 241 | 242 | .insight-card h3 { 243 | font-size: 24px; 244 | } 245 | 246 | .insight-card p { 247 | font-size: 18px; 248 | } 249 | 250 | .insight-card img { 251 | margin-top: 15px; 252 | } 253 | 254 | } 255 | 256 | @keyframes slideIn { 257 | from { 258 | transform: translateX(100%); 259 | } 260 | to { 261 | transform: translateX(0); 262 | } 263 | } -------------------------------------------------------------------------------- /css/publicprofile.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Montserrat", sans-serif; 3 | background: linear-gradient( 4 | to bottom right, 5 | rgb(101, 169, 171), 6 | rgb(200, 230, 231) 7 | ); 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | .profile-container { 13 | max-width: 900px; 14 | margin: 50px auto; 15 | background: white; 16 | border-radius: 10px; 17 | padding: 30px; 18 | box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1); 19 | text-align: center; 20 | transition: transform 0.3s ease-in-out; 21 | } 22 | 23 | .profile-container:hover { 24 | transform: scale(1.03); 25 | box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); 26 | } 27 | 28 | .profile-title { 29 | font-size: 28px; 30 | color: rgb(101, 169, 171); 31 | margin-bottom: 20px; 32 | text-transform: uppercase; 33 | font-weight: 700; 34 | } 35 | 36 | .profile-item { 37 | margin: 15px 0; 38 | font-size: 18px; 39 | color: #333; 40 | } 41 | 42 | .profile-item span { 43 | font-weight: bold; 44 | color: rgb(101, 169, 171); 45 | } 46 | 47 | .insights-container { 48 | margin-top: 30px; 49 | height: 100vh; 50 | overflow-y: auto; 51 | } 52 | 53 | .insight-card { 54 | background: linear-gradient( 55 | to right, 56 | rgb(240, 248, 255), 57 | rgb(200, 230, 231) 58 | ); 59 | border-radius: 8px; 60 | padding: 15px; 61 | margin: 10px auto; 62 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); 63 | text-align: left; 64 | overflow: auto; 65 | transition: transform 0.3s ease, box-shadow 0.3s ease; 66 | } 67 | 68 | .insight-card:hover { 69 | transform: translateY(-5px); 70 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); 71 | } 72 | 73 | .insight-card h3 { 74 | font-size: 22px; 75 | color: rgb(101, 169, 171); 76 | margin-bottom: 10px; 77 | } 78 | 79 | .insight-card p { 80 | font-size: 16px; 81 | color: #555; 82 | } 83 | 84 | .insight-card img { 85 | max-width: 100%; 86 | height: auto; 87 | border-radius: 8px; 88 | margin-top: 10px; 89 | transition: transform 0.3s ease; 90 | } 91 | 92 | .insight-card img:hover { 93 | transform: scale(1.05); 94 | } 95 | 96 | nav { 97 | background: rgb(101, 169, 171); 98 | } 99 | 100 | .navbar-brand span { 101 | color: white; 102 | font-weight: bold; 103 | } 104 | 105 | .nav-link { 106 | color: white; 107 | margin-right: 15px; 108 | font-weight: 500; 109 | transition: color 0.3s ease; 110 | } 111 | 112 | .nav-link:hover { 113 | color: rgba(255, 255, 255, 0.8); 114 | } 115 | 116 | .navbar-toggler-icon { 117 | background-color: white; 118 | } 119 | 120 | .profile-image { 121 | height: 200px; 122 | transition: transform 0.3s ease; 123 | } 124 | 125 | .profile-image img { 126 | object-fit: contain; 127 | height: 200px; 128 | width: 190px; 129 | border: 2px solid rgb(101, 169, 171); 130 | border-radius: 1000px; 131 | transition: transform 0.3s ease; 132 | } 133 | 134 | .profile-image img:hover { 135 | transform: scale(1.1); 136 | } 137 | 138 | #notification-container { 139 | position: fixed; 140 | top: 10px; 141 | right: 10px; 142 | width: 300px; 143 | z-index: 1000; 144 | } 145 | 146 | .notification { 147 | background: rgb(101, 169, 171); 148 | color: white; 149 | padding: 15px; 150 | border-radius: 8px; 151 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 152 | margin-bottom: 10px; 153 | font-size: 16px; 154 | transition: transform 0.3s ease, opacity 0.3s ease; 155 | } 156 | 157 | .notification.success { 158 | background: #28a745; 159 | } 160 | 161 | .notification.error { 162 | background: #dc3545; 163 | } 164 | 165 | .notification.info { 166 | background: #007bff; 167 | } 168 | 169 | .notification.fade-out { 170 | transform: translateX(100%); 171 | opacity: 0; 172 | } 173 | 174 | #notification { 175 | font-family: Arial, sans-serif; 176 | animation: slideIn 0.5s ease-out; 177 | } 178 | 179 | @keyframes slideIn { 180 | from { 181 | transform: translateX(100%); 182 | } 183 | to { 184 | transform: translateX(0); 185 | } 186 | } -------------------------------------------------------------------------------- /css/topics-detail.css: -------------------------------------------------------------------------------- 1 | h2 { 2 | text-align: center; 3 | } 4 | 5 | .comments-section { 6 | margin-top: 20px; 7 | } 8 | 9 | .comment-wrapper { 10 | display: flex; 11 | align-items: center; 12 | gap: 10px; 13 | margin-bottom: 15px; 14 | padding: 10px; 15 | border-bottom: 1px solid #ddd; 16 | } 17 | 18 | .comment-avatar { 19 | width: 40px; 20 | height: 40px; 21 | border-radius: 50%; 22 | object-fit: cover; 23 | } 24 | 25 | .comment-content { 26 | background: #f4f4f4; 27 | padding: 10px; 28 | border-radius: 8px; 29 | max-width: 80%; 30 | } 31 | 32 | .comment-content p { 33 | margin: 0; 34 | font-size: 14px; 35 | } 36 | 37 | .comment-content small { 38 | display: block; 39 | color: #666; 40 | margin-top: 5px; 41 | font-size: 12px; 42 | } 43 | body { 44 | font-family: Arial, sans-serif; 45 | margin: 0; 46 | padding: 20px; 47 | background: #f9f9f9; 48 | } 49 | 50 | .comments-container { 51 | max-width: 600px; 52 | margin: auto; 53 | background: #fff; 54 | padding: 20px; 55 | border-radius: 8px; 56 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 57 | } 58 | 59 | h2 { 60 | text-align: center; 61 | } 62 | 63 | .comment-form { 64 | display: flex; 65 | flex-direction: column; 66 | gap: 10px; 67 | margin-bottom: 20px; 68 | } 69 | 70 | .comment-form textarea { 71 | width: 100%; 72 | padding: 10px; 73 | border: 1px solid #ccc; 74 | border-radius: 5px; 75 | resize: none; 76 | } 77 | 78 | 79 | .comment-form button { 80 | background: #007bff; 81 | color: #fff; 82 | padding: 10px; 83 | border: none; 84 | border-radius: 5px; 85 | cursor: pointer; 86 | } 87 | 88 | .comment-form button:hover { 89 | background: #0056b3; 90 | } 91 | 92 | /* Comments List */ 93 | #comments-container { 94 | margin-top: 15px; 95 | } 96 | 97 | .comment-wrapper { 98 | display: flex; 99 | align-items: center; 100 | gap: 10px; 101 | margin-bottom: 15px; 102 | } 103 | 104 | .comment-avatar { 105 | width: 40px; 106 | height: 40px; 107 | border-radius: 50%; 108 | object-fit: cover; 109 | } 110 | 111 | .comment-content { 112 | background: #f4f4f4; 113 | padding: 10px; 114 | border-radius: 8px; 115 | max-width: 80%; 116 | } 117 | 118 | .comment-content p { 119 | margin: 0; 120 | } 121 | 122 | .comment-content small { 123 | display: block; 124 | color: #666; 125 | margin-top: 5px; 126 | } 127 | #summary-btn { 128 | background-color: #007bff; 129 | color: white; 130 | font-size: 16px; 131 | padding: 10px 15px; 132 | border: none; 133 | border-radius: 5px; 134 | cursor: pointer; 135 | margin: 10px 0; 136 | } 137 | 138 | #summary-btn:hover { 139 | background-color: #0056b3; 140 | } 141 | 142 | #summary-btn:disabled { 143 | background-color: gray; 144 | cursor: not-allowed; 145 | } 146 | 147 | .center-container { 148 | display: flex; 149 | flex-direction: column; 150 | align-items: center; 151 | justify-content: center; 152 | text-align: center; 153 | margin-top: 20px; 154 | } 155 | 156 | .btn-container { 157 | width: 100%; 158 | max-width: 500px; 159 | text-align: center; 160 | } 161 | 162 | #editbtn, #likebtn, #savebtn { 163 | width: 100%; 164 | background-color: rgb(85, 199, 214); 165 | padding: 15px; 166 | border: 2px solid aquamarine; 167 | border-radius: 50px; 168 | cursor: pointer; 169 | font-size: 18px; 170 | font-weight: bold; 171 | color: white; 172 | text-transform: uppercase; 173 | transition: background 0.3s ease, transform 0.2s; 174 | display: flex; 175 | align-items: center; 176 | justify-content: center; 177 | gap: 8px; 178 | } 179 | 180 | #editbtn:hover, #likebtn:hover { 181 | background-color: rgb(72, 179, 194); 182 | transform: scale(1.05); 183 | } 184 | 185 | #likebtn { 186 | margin-top: 10px; 187 | } 188 | 189 | .liked { 190 | color: red; 191 | } -------------------------------------------------------------------------------- /css/topics-listing.css: -------------------------------------------------------------------------------- 1 | /* Container for all cards */ 2 | #parentCont { 3 | display: flex; 4 | /* flex-direction: column; */ 5 | /* Stack cards vertically */ 6 | /* gap: 20px; */ 7 | /* Space between cards */ 8 | /* height: 70vh; */ 9 | /* overflow-y: scroll; */ 10 | } 11 | 12 | .box { 13 | background-color: #f0f0f0; 14 | display: flex; 15 | align-items: center; 16 | justify-content: space-between; 17 | height: 75px; 18 | width: 80vw; 19 | border-radius: 50px; 20 | margin: 0 auto; 21 | } 22 | 23 | .box input { 24 | width: 80%; 25 | height: 90%; 26 | outline: none; 27 | border: none; 28 | background-color: #f0f0f0; 29 | font-size: 25px; 30 | border-radius: 50px; 31 | padding-left: 20px; 32 | font-weight: 600; 33 | } 34 | 35 | .box button { 36 | height: 80%; 37 | outline: none; 38 | border: none; 39 | background-color: #80D0C7; 40 | width: 150px; 41 | border-radius: 40px; 42 | color: white; 43 | font-weight: 700; 44 | } 45 | 46 | select { 47 | width: min-content; 48 | padding: 8px; 49 | background-color: #f0f0f0; 50 | border-radius: 50px; 51 | outline: none; 52 | border: none; 53 | font-size: 15px; 54 | color: rgb(90, 90, 90); 55 | 56 | } 57 | 58 | .selectbtn { 59 | display: flex; 60 | align-items: center; 61 | justify-content: end; 62 | padding: 10px; 63 | padding-right: 50px; 64 | } 65 | 66 | .box button:hover { 67 | background-color: #13547A; 68 | } 69 | 70 | 71 | .custom-block-image { 72 | border-radius: 20px; 73 | margin-top: 0; 74 | margin-bottom: 30px; 75 | } 76 | 77 | .designPara { 78 | white-space: nowrap; 79 | text-overflow: ellipsis; 80 | width: 100%; 81 | display: block; 82 | overflow: hidden 83 | } 84 | 85 | #parentCont .custom-block .designlink .d-flex > div { 86 | width:100%; 87 | } 88 | .badge { 89 | border-radius: 30px !important; 90 | padding: 6px 16px; 91 | line-height: 1.4; 92 | margin-top: 14px; 93 | } -------------------------------------------------------------------------------- /edit-insight.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Edit Topic 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 81 |
82 |
83 |
84 | 85 |
86 | 87 |
88 | 89 |
90 | Topic Image 91 | 92 |
93 | 94 |
95 | 96 |
97 | 98 |
99 | 100 |
101 | 102 |
103 |
104 |
105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /edit-profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Edit Profile 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 72 | 73 |
74 |

Edit Your Profile

75 |
76 | Profile Image 77 |
78 |
79 |
80 | 81 | 82 |
83 |
84 | 85 | 86 |
87 |
88 | 89 | 90 |
91 | 92 |
93 |
94 |
95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /feedbackform.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | list-style: none; 6 | outline: none; 7 | font-family: 'Open Sans', sans-serif; 8 | } 9 | 10 | body { 11 | background: whitesmoke; 12 | color: black; 13 | font-size: 16px; 14 | position: relative; 15 | } 16 | 17 | .wrapper { 18 | width: 750px; 19 | max-width: 100%; 20 | background: white; 21 | margin: 100px auto 0; 22 | padding: 50px; 23 | border-radius: 5px; 24 | } 25 | 26 | .wrapper .headers { 27 | margin-bottom: 35px; 28 | display: flex; 29 | justify-content: center; 30 | } 31 | 32 | .wrapper .headers ul { 33 | display: flex; 34 | margin-right: -5rem; 35 | } 36 | 37 | .wrapper .headers ul li { 38 | position: relative; 39 | margin-right: 50px; 40 | } 41 | 42 | .wrapper .headers ul li:last-child { 43 | margin-right: 0; 44 | } 45 | 46 | .wrapper .headers ul li:before { 47 | content: ""; 48 | position: absolute; 49 | top: 50%; 50 | transform: translateY(-50%); 51 | left: 55px; 52 | width: 100%; 53 | height: 2px; 54 | background: grey; 55 | } 56 | 57 | .wrapper .headers ul li:last-child:before { 58 | display: none; 59 | } 60 | 61 | .wrapper .headers ul li div { 62 | padding: 5px; 63 | border-radius: 50%; 64 | } 65 | 66 | .wrapper .headers ul li p { 67 | width: 50px; 68 | height: 50px; 69 | background: gray; 70 | color: white; 71 | text-align: center; 72 | line-height: 150%; 73 | border-radius: 50%; 74 | font-size: 2rem; 75 | margin-right: 5rem; 76 | margin-top: 0.5rem; 77 | } 78 | 79 | .wrapper .headers ul li.active:before, 80 | .wrapper .headers ul li.active p { 81 | background: #13547a; 82 | 83 | } 84 | 85 | .wrapper .form_wrap { 86 | margin-bottom: 35px; 87 | } 88 | 89 | .textup { 90 | text-align: center; 91 | color:#13547a; 92 | font-weight: 700; 93 | } 94 | 95 | i { 96 | margin-right: 3px; 97 | } 98 | 99 | .form-box { 100 | background-color: #fff; 101 | box-shadow: 0 0 10px rgba(36, 67, 40, 0.8); 102 | padding: 15px; 103 | border-radius: 8px; 104 | width: 500px; 105 | } 106 | 107 | form { 108 | max-width: 400px; 109 | margin: 0 auto; 110 | } 111 | 112 | .radio-group { 113 | display: flex; 114 | margin-bottom: 16px; 115 | } 116 | 117 | input[type="radio"] { 118 | margin-right: 8px; 119 | } 120 | 121 | label { 122 | display: block; 123 | margin-bottom: 8px; 124 | font-size: 17px; 125 | color: #13547a; 126 | font-weight: 600; 127 | } 128 | 129 | input, textarea { 130 | width: 100%; 131 | padding: 8px; 132 | margin-bottom: 12px; 133 | box-sizing: border-box; 134 | border-radius: 10px; 135 | } 136 | 137 | .rating { 138 | margin-left: -25px; 139 | display: flex; 140 | flex-direction: row; 141 | } 142 | 143 | .rating input { 144 | display: none; 145 | } 146 | 147 | .rating label img { 148 | width: 30px; 149 | height: 30px; 150 | } 151 | 152 | .rating label { 153 | position: relative; 154 | width: 100px; 155 | height: 120px; 156 | cursor: pointer; 157 | filter: grayscale(1); 158 | text-align: center; 159 | opacity: 1; 160 | display: flex; 161 | gap: 1rem; 162 | margin: 1rem; 163 | flex-direction: column; 164 | flex-wrap: wrap; 165 | align-content: flex-start; 166 | align-items: center; 167 | } 168 | 169 | .rating input:checked + label { 170 | opacity: 1; 171 | width: 100px; 172 | filter: grayscale(0); 173 | } 174 | 175 | .rating input:checked + label h4 { 176 | opacity: 1; 177 | } 178 | 179 | .wrapper .btns_wrap { 180 | width: 350px; 181 | max-width: 100%; 182 | margin: 0 auto; 183 | } 184 | 185 | .wrapper .btns_wrap .common_btns { 186 | display: flex; 187 | justify-content: space-between; 188 | } 189 | 190 | .wrapper .btns_wrap .common_btns.form_1_btns { 191 | justify-content: flex-end; 192 | } 193 | 194 | .wrapper .btns_wrap .common_btns button { 195 | border: 0; 196 | padding: 12px 15px; 197 | background: #80d0c7; 198 | color: white; 199 | width: 135px; 200 | justify-content: center; 201 | display: flex; 202 | align-items: center; 203 | font-size: 16px; 204 | border-radius: 3px; 205 | transition: 0.5s ease; 206 | cursor: pointer; 207 | } 208 | 209 | .wrapper .btns_wrap .common_btns button.btn_back { 210 | background: #80d0c7; 211 | } 212 | 213 | .wrapper .btns_wrap .common_btns button.btn_next .icon { 214 | display: flex; 215 | margin-left: 10px; 216 | } 217 | 218 | .wrapper .btns_wrap .common_btns button.btn_back .icon { 219 | display: flex; 220 | margin-right: 10px; 221 | } 222 | 223 | .wrapper .btns_wrap .common_btns button.btn_next:hover, 224 | .wrapper .btns_wrap .common_btns button.btn_done:hover { 225 | background: #13547a; 226 | } 227 | 228 | .wrapper .btns_wrap .common_btns button.btn_back:hover { 229 | background: #13547a; 230 | } 231 | 232 | .modal_wrapper { 233 | position: 0; 234 | top: 0; 235 | left: 0; 236 | width: 100%; 237 | height: 100%; 238 | visibility: hidden; 239 | } 240 | 241 | .modal_wrapper .shadow { 242 | position: absolute; 243 | top: 0; 244 | left: 0; 245 | width: 100%; 246 | height: 100%; 247 | background-image: rgba(0, 0, 0, 0.8); 248 | transition: 0.2s ease; 249 | opacity: 0; 250 | } 251 | 252 | .modal_wrapper .success_wrap { 253 | position: absolute; 254 | top: 50%; 255 | left: 50%; 256 | transform: translate(-50%, -800px); 257 | background: white; 258 | padding: 50px; 259 | display: flex; 260 | align-items: center; 261 | border-radius: 5px; 262 | transition: 0.5s ease; 263 | } 264 | 265 | .modal_wrapper .success_wrap .modal_icon { 266 | margin-right: 20px; 267 | width: 50px; 268 | height: 50px; 269 | background: #80d0c7; 270 | color: #fff; 271 | border-radius: 50%; 272 | display: flex; 273 | align-items: center; 274 | justify-content: center; 275 | font-size: 32px; 276 | font-weight: 700; 277 | } 278 | 279 | .modal_wrapper.active { 280 | visibility: visible; 281 | } 282 | 283 | .modal_wrapper.active .shadow { 284 | opacity: 1; 285 | } 286 | 287 | .modal_wrapper.active .success_wrap { 288 | transform: translate(-50%, -50%); 289 | } 290 | 291 | @media (max-width: 600px) { 292 | .wrapper { 293 | width: 100%; 294 | padding: 10px; 295 | } 296 | .wrapper .headers ul { 297 | margin-right: 2rem; 298 | } 299 | 300 | .wrapper .headers ul li p { 301 | width: 40px; 302 | height: 40px; 303 | margin-right: 2rem; 304 | font-size: 1rem; 305 | } 306 | 307 | .wrapper .headers ul li:before { 308 | left: 30px; 309 | width: 178%; 310 | } 311 | 312 | .form_wrap { 313 | margin: 0; 314 | padding: 0; 315 | } 316 | 317 | .data_info { 318 | padding: 10px; 319 | } 320 | 321 | .btns_wrap .common_btns { 322 | flex-direction: row; 323 | align-items: center; 324 | } 325 | 326 | .btns_wrap .common_btns button { 327 | width: 100%; 328 | margin: 5px 0; 329 | } 330 | 331 | .radio-group { 332 | flex-direction: row; 333 | } 334 | 335 | .rating { 336 | margin-left: 0; 337 | display: flex 338 | ; 339 | flex-direction: row; 340 | align-items: center; 341 | } 342 | 343 | .rating label { 344 | width: 80px; 345 | height: 100px; 346 | } 347 | 348 | .rating label img { 349 | width: 25px; 350 | height: 25px; 351 | } 352 | 353 | .rating label h4 { 354 | font-size: 12px; 355 | } 356 | } 357 | 358 | @media (max-width: 300px) { 359 | .wrapper { 360 | width: 100%; 361 | padding: 5px; 362 | } 363 | .wrapper .headers ul { 364 | margin-right: 2rem; 365 | } 366 | 367 | .wrapper .headers ul li p { 368 | width: 20px; 369 | height: 20px; 370 | margin-right: 1rem; 371 | font-size: 1rem; 372 | } 373 | 374 | .wrapper .headers ul li:before { 375 | left: 20px; 376 | width: 156%; 377 | } 378 | 379 | .form_wrap { 380 | margin: 0; 381 | padding: 0; 382 | } 383 | 384 | .data_info { 385 | padding: 5px; 386 | } 387 | 388 | .btns_wrap .common_btns { 389 | flex-direction: row; 390 | align-items: center; 391 | } 392 | 393 | .btns_wrap .common_btns button { 394 | width: 100%; 395 | margin: 3px 0; 396 | } 397 | 398 | .radio-group { 399 | flex-direction: row; 400 | } 401 | 402 | .rating { 403 | margin-left: 0; 404 | display: flex 405 | ; 406 | flex-direction: row; 407 | align-items: center; 408 | } 409 | 410 | .rating label { 411 | width: 60px; 412 | height: 80px; 413 | } 414 | 415 | .rating label img { 416 | width: 20px; 417 | height: 20px; 418 | } 419 | 420 | .rating label h4 { 421 | font-size: 10px; 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /feedbackform.js: -------------------------------------------------------------------------------- 1 | var form_1 = document.querySelector(".form_1"); 2 | var form_2 = document.querySelector(".form_2"); 3 | var form_3 = document.querySelector(".form_3"); 4 | 5 | var form_1_btns = document.querySelector(".form_1_btns"); 6 | var form_2_btns = document.querySelector(".form_2_btns"); 7 | var form_3_btns = document.querySelector(".form_3_btns"); 8 | 9 | var form_1_next_btn = document.querySelector(".form_1_btns .btn_next"); 10 | var form_2_back_btn = document.querySelector(".form_2_btns .btn_back"); 11 | var form_2_next_btn = document.querySelector(".form_2_btns .btn_next"); 12 | var form_3_back_btn = document.querySelector(".form_3_btns .btn_back"); 13 | 14 | var form_2_progressbar = document.querySelector(".form_2_progressbar"); 15 | var form_3_progressbar = document.querySelector(".form_3_progressbar"); 16 | 17 | var btn_done = document.querySelector(".btn_done"); 18 | var modal_wrapper = document.querySelector(".modal_wrapper"); 19 | var shadow = document.querySelector(".shadow"); 20 | 21 | function validateForm(form) { 22 | var inputs = form.querySelectorAll("input, textarea"); 23 | for (var i = 0; i < inputs.length; i++) { 24 | if (!inputs[i].checkValidity()) { 25 | inputs[i].reportValidity(); 26 | return false; 27 | } 28 | } 29 | return true; 30 | } 31 | 32 | form_1_next_btn.addEventListener("click", function() { 33 | if (validateForm(form_1)) { 34 | form_1.style.display = "none"; 35 | form_2.style.display = "block"; 36 | 37 | form_1_btns.style.display = "none"; 38 | form_2_btns.style.display = "flex"; 39 | 40 | form_2_progressbar.classList.add("active"); 41 | } 42 | }); 43 | 44 | form_2_back_btn.addEventListener("click", function() { 45 | form_1.style.display = "block"; 46 | form_2.style.display = "none"; 47 | 48 | form_1_btns.style.display = "flex"; 49 | form_2_btns.style.display = "none"; 50 | 51 | form_2_progressbar.classList.remove("active"); 52 | }); 53 | 54 | form_2_next_btn.addEventListener("click", function() { 55 | if (validateForm(form_2)) { 56 | form_2.style.display = "none"; 57 | form_3.style.display = "block"; 58 | 59 | form_2_btns.style.display = "none"; 60 | form_3_btns.style.display = "flex"; 61 | 62 | form_3_progressbar.classList.add("active"); 63 | } 64 | }); 65 | 66 | form_3_back_btn.addEventListener("click", function() { 67 | form_2.style.display = "block"; 68 | form_3.style.display = "none"; 69 | 70 | form_2_btns.style.display = "flex"; 71 | form_3_btns.style.display = "none"; 72 | 73 | form_3_progressbar.classList.remove("active"); 74 | }); 75 | 76 | btn_done.addEventListener("click", function() { 77 | if (validateForm(form_3)) { 78 | modal_wrapper.classList.add("active"); 79 | } 80 | }); 81 | 82 | shadow.addEventListener("click", function() { 83 | modal_wrapper.classList.remove("active"); 84 | }); 85 | 86 | function togglePasswordVisibility() { 87 | const passwordInput = document.getElementById('loginPassword'); 88 | const togglePasswordIcon = document.getElementById('togglePasswordIcon'); 89 | if (passwordInput.type === 'password') { 90 | passwordInput.type = 'text'; 91 | togglePasswordIcon.classList.remove('bi-eye'); 92 | togglePasswordIcon.classList.add('bi-eye-slash'); 93 | } else { 94 | passwordInput.type = 'password'; 95 | togglePasswordIcon.classList.remove('bi-eye-slash'); 96 | togglePasswordIcon.classList.add('bi-eye'); 97 | } 98 | } 99 | 100 | function validateForm() { 101 | const email = document.getElementById('loginEmail'); 102 | const password = document.getElementById('loginPassword'); 103 | let valid = true; 104 | 105 | if (!email.value) { 106 | email.classList.add('is-invalid'); 107 | valid = false; 108 | } else { 109 | email.classList.remove('is-invalid'); 110 | } 111 | 112 | if (!password.value) { 113 | password.classList.add('is-invalid'); 114 | valid = false; 115 | } else { 116 | password.classList.remove('is-invalid'); 117 | } 118 | 119 | return valid; 120 | } 121 | function validateSignupForm() { 122 | const username = document.getElementById('signupUsername'); 123 | const email = document.getElementById('signupEmail'); 124 | const password = document.getElementById('signupPassword'); 125 | const confirmPassword = document.getElementById('signupConfirmPassword'); 126 | let valid = true; 127 | 128 | // Username validation (must be at least 3 characters) 129 | if (username.value.length < 3) { 130 | username.classList.add('is-invalid'); 131 | valid = false; 132 | } else { 133 | username.classList.remove('is-invalid'); 134 | } 135 | 136 | // Email validation (checks format) 137 | const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; 138 | if (!emailPattern.test(email.value)) { 139 | email.classList.add('is-invalid'); 140 | valid = false; 141 | } else { 142 | email.classList.remove('is-invalid'); 143 | } 144 | 145 | // Password validation (at least 6 characters and includes a number) 146 | const passwordPattern = /^(?=.*\d)(?=.*[a-zA-Z]).{6,}$/; 147 | if (!passwordPattern.test(password.value)) { 148 | password.classList.add('is-invalid'); 149 | valid = false; 150 | } else { 151 | password.classList.remove('is-invalid'); 152 | } 153 | 154 | // Confirm password validation (must match the password) 155 | if (password.value !== confirmPassword.value) { 156 | confirmPassword.classList.add('is-invalid'); 157 | valid = false; 158 | } else { 159 | confirmPassword.classList.remove('is-invalid'); 160 | } 161 | 162 | return valid; 163 | } 164 | 165 | function toggleSignupPasswordVisibility() { 166 | const passwordInput = document.getElementById('signupPassword'); 167 | const togglePasswordIcon = document.getElementById('toggleSignupPasswordIcon'); 168 | if (passwordInput.type === 'password') { 169 | passwordInput.type = 'text'; 170 | togglePasswordIcon.classList.remove('bi-eye'); 171 | togglePasswordIcon.classList.add('bi-eye-slash'); 172 | } else { 173 | passwordInput.type = 'password'; 174 | togglePasswordIcon.classList.remove('bi-eye-slash'); 175 | togglePasswordIcon.classList.add('bi-eye'); 176 | } 177 | } 178 | 179 | function toggleSignupConfirmPasswordVisibility() { 180 | const confirmPasswordInput = document.getElementById('signupConfirmPassword'); 181 | const toggleConfirmPasswordIcon = document.getElementById('toggleSignupConfirmPasswordIcon'); 182 | if (confirmPasswordInput.type === 'password') { 183 | confirmPasswordInput.type = 'text'; 184 | toggleConfirmPasswordIcon.classList.remove('bi-eye'); 185 | toggleConfirmPasswordIcon.classList.add('bi-eye-slash'); 186 | } else { 187 | confirmPasswordInput.type = 'password'; 188 | toggleConfirmPasswordIcon.classList.remove('bi-eye-slash'); 189 | toggleConfirmPasswordIcon.classList.add('bi-eye'); 190 | } 191 | } 192 | 193 | // Check if the user is logged in by verifying the presence of an auth token in localStorage 194 | // if (localStorage.getItem("authToken")) { 195 | // console.log("User logged in"); 196 | 197 | // // Select all elements with the class 'authcomp' 198 | // const authElements = document.getElementsByClassName("authcomp"); 199 | 200 | // // Loop through the elements and change their opacity 201 | // Array.from(authElements).forEach(element => { 202 | // element.style.opacity = 0; // Set opacity to 0 (hidden) 203 | // element.style.pointerEvents = "none"; // Prevent interaction 204 | // }); 205 | // } else { 206 | // console.log("User not logged in"); 207 | // } 208 | document.querySelector( 209 | ".copyright-text" 210 | ).textContent = `Copyright © ${new Date().getFullYear()} InsightSync. All rights reserved.`; 211 | 212 | console.log("modules") 213 | const logoutbtn = document.getElementById("signout") 214 | const loginbtn = document.getElementById("loginbtn") 215 | const signupbtn = document.getElementById("signupbtn") 216 | if (localStorage.getItem("authToken")) { 217 | console.log(signupbtn) 218 | loginbtn.style.display = "none" 219 | signupbtn.style.display= "none" 220 | }else{ 221 | 222 | logoutbtn.style.display = "none" 223 | } 224 | document.getElementById("logoutbtn2").addEventListener("click",()=>{ 225 | console.log("clickedbskjkbvsdhvbskdmbvduksvbsdmhcbjh") 226 | localStorage.clear() 227 | window.location.href="/" 228 | }) -------------------------------------------------------------------------------- /fetchcomment.js: -------------------------------------------------------------------------------- 1 | import { link } from "./Baselink.js"; 2 | 3 | const params = new URLSearchParams(window.location.search); 4 | const insightId = params.get('id'); 5 | 6 | function fetchComments() { 7 | axios.get(`${link}/api/v1/comment/getcommentsbyinsight/${insightId}`) 8 | .then((resp) => { 9 | const commentsContainer = document.getElementById("comments-container"); 10 | commentsContainer.innerHTML = ""; // Clear existing comments 11 | 12 | if (resp.data.comments.length === 0) { 13 | commentsContainer.innerHTML = "

No comments yet. Be the first to comment!

"; 14 | return; 15 | } 16 | 17 | resp.data.comments.forEach(comment => { 18 | const profileImage = comment.userId?.image || "default-avatar.png"; // Fallback if no image 19 | const commentText = comment.comment || "No text"; // Ensure text exists 20 | const commentTimestamp = new Date(comment.createdAt).toLocaleString(); // Format timestamp 21 | 22 | const commentDiv = document.createElement("div"); 23 | commentDiv.classList.add("comment"); 24 | 25 | commentDiv.innerHTML = ` 26 |
27 | User Avatar 28 |
29 |

${commentText}

30 | ${commentTimestamp} 31 |
32 |
33 | `; 34 | commentsContainer.appendChild(commentDiv); 35 | }); 36 | }) 37 | .catch((error) => { 38 | console.error("Error fetching comments:", error); 39 | document.getElementById("comments-container").innerHTML = "

Error loading comments.

"; 40 | }); 41 | } 42 | 43 | // Fetch comments when the page loads 44 | document.addEventListener("DOMContentLoaded", fetchComments); 45 | 46 | export { fetchComments }; 47 | -------------------------------------------------------------------------------- /fonts/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/fonts/bootstrap-icons.woff -------------------------------------------------------------------------------- /fonts/bootstrap-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/fonts/bootstrap-icons.woff2 -------------------------------------------------------------------------------- /images/OIP (5).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/OIP (5).jpeg -------------------------------------------------------------------------------- /images/OIP (7).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/OIP (7).jpeg -------------------------------------------------------------------------------- /images/OIP (8).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/OIP (8).jpeg -------------------------------------------------------------------------------- /images/Screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/Screenshot1.png -------------------------------------------------------------------------------- /images/Screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/Screenshot2.png -------------------------------------------------------------------------------- /images/Screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/Screenshot3.png -------------------------------------------------------------------------------- /images/businesswoman-using-tablet-analysis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/businesswoman-using-tablet-analysis.jpg -------------------------------------------------------------------------------- /images/colleagues-working-cozy-office-medium-shot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/colleagues-working-cozy-office-medium-shot.jpg -------------------------------------------------------------------------------- /images/faq_graphic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/faq_graphic.jpg -------------------------------------------------------------------------------- /images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/favicon.png -------------------------------------------------------------------------------- /images/rear-view-young-college-student.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/rear-view-young-college-student.jpg -------------------------------------------------------------------------------- /images/robot-state-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/robot-state-3.png -------------------------------------------------------------------------------- /images/topics/colleagues-working-cozy-office-medium-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/colleagues-working-cozy-office-medium-shot.png -------------------------------------------------------------------------------- /images/topics/undraw_Compose_music_re_wpiw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Compose_music_re_wpiw.png -------------------------------------------------------------------------------- /images/topics/undraw_Educator_re_ju47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Educator_re_ju47.png -------------------------------------------------------------------------------- /images/topics/undraw_Finance_re_gnv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Finance_re_gnv2.png -------------------------------------------------------------------------------- /images/topics/undraw_Graduation_re_gthn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Graduation_re_gthn.png -------------------------------------------------------------------------------- /images/topics/undraw_Group_video_re_btu7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Group_video_re_btu7.png -------------------------------------------------------------------------------- /images/topics/undraw_Podcast_audience_re_4i5q.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Podcast_audience_re_4i5q.png -------------------------------------------------------------------------------- /images/topics/undraw_Redesign_feedback_re_jvm0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Redesign_feedback_re_jvm0.png -------------------------------------------------------------------------------- /images/topics/undraw_Remote_design_team_re_urdx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_Remote_design_team_re_urdx.png -------------------------------------------------------------------------------- /images/topics/undraw_happy_music_g6wc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_happy_music_g6wc.png -------------------------------------------------------------------------------- /images/topics/undraw_online_ad_re_ol62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_online_ad_re_ol62.png -------------------------------------------------------------------------------- /images/topics/undraw_viral_tweet_gndb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dishika18/InsightSync/f55c6e0662038e1b2c360efae458a927a1423234/images/topics/undraw_viral_tweet_gndb.png -------------------------------------------------------------------------------- /js/add-topics.js: -------------------------------------------------------------------------------- 1 | function showNotification(message, type) { 2 | const notification = document.createElement('div'); 3 | notification.classList.add('notification'); 4 | notification.textContent = message; 5 | 6 | // Set the background color based on the type of notification 7 | if (type === 'success') { 8 | notification.style.backgroundColor = '#4CAF50'; // Green for success 9 | } else if (type === 'error') { 10 | notification.style.backgroundColor = '#f44336'; // Red for error 11 | } else { 12 | notification.style.backgroundColor = '#2196F3'; // Blue for default info 13 | } 14 | 15 | // Style the notification 16 | notification.style.color = 'white'; 17 | notification.style.padding = '10px 20px'; 18 | notification.style.borderRadius = '5px'; 19 | notification.style.position = 'fixed'; 20 | notification.style.top = '10px'; 21 | notification.style.right = '10px'; 22 | notification.style.zIndex = '9999'; 23 | notification.style.fontSize = '16px'; 24 | 25 | // Add notification to the body 26 | document.body.appendChild(notification); 27 | 28 | // Remove the notification after 3 seconds 29 | setTimeout(() => { 30 | notification.remove(); 31 | }, 3000); 32 | } 33 | 34 | 35 | // Button event listener 36 | import { link } from "./Baselink.js" 37 | console.log(link) 38 | const btn = document.getElementById("submitBtn"); 39 | 40 | 41 | btn.addEventListener("click", async (event) => { 42 | event.preventDefault(); // Prevent default form submission behavior 43 | 44 | // Get the form fields 45 | const title = document.getElementById("topicTitle").value; 46 | const content = document.getElementById("topicContent").value; 47 | const category = document.getElementById("topicCategory").value; 48 | const imageFile = document.getElementById("topicImage").files[0]; // Use `.files[0]` for file input 49 | 50 | // Validate required fields 51 | if (!title || !content || !category || !imageFile) { 52 | showNotification("Please fill in all fields and select an image.", "error"); 53 | return; 54 | } 55 | 56 | // Create FormData object 57 | const formData = new FormData(); 58 | formData.append("title", title); 59 | formData.append("topic", category); 60 | formData.append("content", content); 61 | formData.append("imagefile", imageFile); // Append the file directly 62 | 63 | try { 64 | // Send POST request 65 | console.log(formData) 66 | const response = await axios.post( 67 | `${link}/api/v1/insight/addinsight`, 68 | formData, 69 | { 70 | headers: { 71 | "Content-Type": "multipart/form-data", // Important for file upload 72 | "Authorization": `Bearer ${localStorage.getItem("authToken")}` 73 | }, 74 | } 75 | ); 76 | 77 | // Handle successful response 78 | console.log("Success:", response.data); 79 | showNotification("Topic submitted successfully!", "success"); 80 | } catch (error) { 81 | // Handle error response 82 | console.error("Error:", error); 83 | showNotification("There was an error submitting your topic. Please try again.", "error"); 84 | } 85 | }); 86 | 87 | console.log("modules") 88 | const logoutbtn = document.getElementById("signout") 89 | const loginbtn = document.getElementById("loginbtn") 90 | const signupbtn = document.getElementById("signupbtn") 91 | // if (localStorage.getItem("authToken")) { 92 | // console.log("hdbc") 93 | // console.log(loginbtn) 94 | // loginbtn.style.display = "none" 95 | // signupbtn.style.display= "none" 96 | // }else{ 97 | // logoutbtn.style.display = "none" 98 | // } 99 | document.getElementById("logoutbtn2").addEventListener("click",()=>{ 100 | console.log("clickedbskjkbvsdhvbskdmbvduksvbsdmhcbjh") 101 | localStorage.clear() 102 | window.location.href="/" 103 | }) 104 | 105 | const textarea = document.getElementById("topicContent"); 106 | const preview = document.getElementById("preview"); 107 | const toggleBtn = document.getElementById("toggleBtn"); 108 | let isMarkdown = false; 109 | 110 | toggleBtn.addEventListener("click", () => { 111 | if (isMarkdown) { 112 | // Switch to text editor mode 113 | preview.style.display = "none"; 114 | textarea.style.display = "block"; 115 | toggleBtn.textContent = "Switch to Preview"; 116 | } else { 117 | // Switch to markdown preview mode 118 | preview.innerHTML = marked.parse(textarea.value); 119 | preview.style.display = "block"; 120 | textarea.style.display = "none"; 121 | toggleBtn.textContent = "Switch to Editor"; 122 | } 123 | isMarkdown = !isMarkdown; 124 | }); 125 | 126 | if (!localStorage.getItem('authToken')) { 127 | alert("You are not logged in...You will be redirected") 128 | window.location.href="/" 129 | } -------------------------------------------------------------------------------- /js/click-scroll.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const scrollButton = document.getElementById('scrollButton'); 4 | const outerCircle = document.querySelector('.outer-circle'); 5 | const arrow = document.querySelector('.arrow'); 6 | 7 | // Function to handle scroll behavior 8 | window.addEventListener('scroll', () => { 9 | const scrollTop = window.scrollY; 10 | const scrollHeight = document.documentElement.scrollHeight - window.innerHeight; 11 | const scrollProgress = (scrollTop / scrollHeight) * 360; 12 | 13 | // Show button after scrolling 7-8 lines (~100px) 14 | if (scrollTop > 100) { 15 | scrollButton.classList.add('visible'); 16 | } else { 17 | scrollButton.classList.remove('visible'); 18 | } 19 | 20 | // Update the circular progress 21 | outerCircle.style.setProperty('--scroll-progress', `${scrollProgress}deg`); 22 | }); 23 | 24 | // Scroll-to-top functionality 25 | scrollButton.addEventListener('click', () => { 26 | window.scrollTo({ top: 0, behavior: 'smooth' }); 27 | }); 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | var sectionArray = [1, 2, 3, 4, 5]; 36 | 37 | $.each(sectionArray, function(index, value){ 38 | 39 | $(document).scroll(function(){ 40 | var offsetSection = $('#' + 'section_' + value).offset().top - 75; 41 | var docScroll = $(document).scrollTop(); 42 | var docScroll1 = docScroll + 1; 43 | 44 | 45 | if ( docScroll1 >= offsetSection ){ 46 | $('.navbar-nav .nav-item .nav-link').removeClass('active'); 47 | $('.navbar-nav .nav-item .nav-link:link').addClass('inactive'); 48 | $('.navbar-nav .nav-item .nav-link').eq(index).addClass('active'); 49 | $('.navbar-nav .nav-item .nav-link').eq(index).removeClass('inactive'); 50 | } 51 | 52 | }); 53 | 54 | $('.click-scroll').eq(index).click(function(e){ 55 | var offsetClick = $('#' + 'section_' + value).offset().top - 75; 56 | e.preventDefault(); 57 | $('html, body').animate({ 58 | 'scrollTop':offsetClick 59 | }, 300) 60 | }); 61 | 62 | }); 63 | 64 | $(document).ready(function(){ 65 | $('.navbar-nav .nav-item .nav-link:link').addClass('inactive'); 66 | $('.navbar-nav .nav-item .nav-link').eq(0).addClass('active'); 67 | $('.navbar-nav .nav-item .nav-link:link').eq(0).removeClass('inactive'); 68 | }); 69 | -------------------------------------------------------------------------------- /js/contacts.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | const contactForm = document.querySelector('.custom-form.contact-form'); // Select the contact form 3 | if (contactForm) { 4 | contactForm.addEventListener('submit', handleContactSubmit); 5 | } 6 | }); 7 | 8 | function handleContactSubmit(event) { 9 | event.preventDefault(); // Prevent the default form submission 10 | 11 | // Get form field values 12 | const name = document.getElementById('name').value; 13 | const email = document.getElementById('email').value; 14 | const subject = document.getElementById('subject').value; 15 | const message = document.getElementById('message').value; 16 | 17 | const formData = { name, email, subject, message }; 18 | 19 | fetch('https://insight-sync-u1bq.vercel.app', { // Update the URL to match your backend route 20 | method: 'POST', 21 | headers: { 'Content-Type': 'application/json' }, 22 | body: JSON.stringify(formData), 23 | }) 24 | .then(response => response.json()) 25 | .then(data => { 26 | if (data.message) { 27 | showNotification('Form submitted successfully: ' + data.message, 'success'); 28 | document.querySelector('.custom-form.contact-form').reset(); 29 | } else { 30 | showNotification('Failed to submit form: ' + (data.error || 'Unknown error'), 'error'); 31 | } 32 | }) 33 | .catch(error => { 34 | console.error('Error:', error); 35 | showNotification('Failed to submit form: An unexpected error occurred.', 'error'); 36 | }); 37 | } 38 | 39 | function showNotification(message, type) { 40 | const notification = document.createElement('div'); 41 | notification.classList.add('notification', type === 'success' ? 'show' : 'error'); 42 | notification.innerText = message; 43 | 44 | // Append the notification to the body 45 | document.body.appendChild(notification); 46 | 47 | // Add the show class after a delay to trigger the animation 48 | setTimeout(() => { 49 | notification.classList.add('show'); 50 | }, 100); 51 | 52 | // Remove the notification after 5 seconds 53 | setTimeout(() => { 54 | notification.classList.remove('show'); 55 | setTimeout(() => { 56 | notification.remove(); 57 | }, 500); 58 | }, 5000); 59 | } 60 | 61 | function togglePasswordVisibility() { 62 | const passwordInput = document.getElementById('loginPassword'); 63 | const togglePasswordIcon = document.getElementById('togglePasswordIcon'); 64 | if (passwordInput.type === 'password') { 65 | passwordInput.type = 'text'; 66 | togglePasswordIcon.classList.remove('bi-eye'); 67 | togglePasswordIcon.classList.add('bi-eye-slash'); 68 | } else { 69 | passwordInput.type = 'password'; 70 | togglePasswordIcon.classList.remove('bi-eye-slash'); 71 | togglePasswordIcon.classList.add('bi-eye'); 72 | } 73 | } 74 | 75 | function validateForm() { 76 | const email = document.getElementById('loginEmail'); 77 | const password = document.getElementById('loginPassword'); 78 | let valid = true; 79 | 80 | if (!email.value) { 81 | email.classList.add('is-invalid'); 82 | valid = false; 83 | } else { 84 | email.classList.remove('is-invalid'); 85 | } 86 | 87 | if (!password.value) { 88 | password.classList.add('is-invalid'); 89 | valid = false; 90 | } else { 91 | password.classList.remove('is-invalid'); 92 | } 93 | 94 | return valid; 95 | } 96 | function validateSignupForm() { 97 | const username = document.getElementById('signupUsername'); 98 | const email = document.getElementById('signupEmail'); 99 | const password = document.getElementById('signupPassword'); 100 | const confirmPassword = document.getElementById('signupConfirmPassword'); 101 | let valid = true; 102 | 103 | // Username validation (must be at least 3 characters) 104 | if (username.value.length < 3) { 105 | username.classList.add('is-invalid'); 106 | valid = false; 107 | } else { 108 | username.classList.remove('is-invalid'); 109 | } 110 | 111 | // Email validation (checks format) 112 | const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; 113 | if (!emailPattern.test(email.value)) { 114 | email.classList.add('is-invalid'); 115 | valid = false; 116 | } else { 117 | email.classList.remove('is-invalid'); 118 | } 119 | 120 | // Password validation (at least 6 characters and includes a number) 121 | const passwordPattern = /^(?=.*\d)(?=.*[a-zA-Z]).{6,}$/; 122 | if (!passwordPattern.test(password.value)) { 123 | password.classList.add('is-invalid'); 124 | valid = false; 125 | } else { 126 | password.classList.remove('is-invalid'); 127 | } 128 | 129 | // Confirm password validation (must match the password) 130 | if (password.value !== confirmPassword.value) { 131 | confirmPassword.classList.add('is-invalid'); 132 | valid = false; 133 | } else { 134 | confirmPassword.classList.remove('is-invalid'); 135 | } 136 | 137 | return valid; 138 | } 139 | 140 | function toggleSignupPasswordVisibility() { 141 | const passwordInput = document.getElementById('signupPassword'); 142 | const togglePasswordIcon = document.getElementById('toggleSignupPasswordIcon'); 143 | if (passwordInput.type === 'password') { 144 | passwordInput.type = 'text'; 145 | togglePasswordIcon.classList.remove('bi-eye'); 146 | togglePasswordIcon.classList.add('bi-eye-slash'); 147 | } else { 148 | passwordInput.type = 'password'; 149 | togglePasswordIcon.classList.remove('bi-eye-slash'); 150 | togglePasswordIcon.classList.add('bi-eye'); 151 | } 152 | } 153 | 154 | function toggleSignupConfirmPasswordVisibility() { 155 | const confirmPasswordInput = document.getElementById('signupConfirmPassword'); 156 | const toggleConfirmPasswordIcon = document.getElementById('toggleSignupConfirmPasswordIcon'); 157 | if (confirmPasswordInput.type === 'password') { 158 | confirmPasswordInput.type = 'text'; 159 | toggleConfirmPasswordIcon.classList.remove('bi-eye'); 160 | toggleConfirmPasswordIcon.classList.add('bi-eye-slash'); 161 | } else { 162 | confirmPasswordInput.type = 'password'; 163 | toggleConfirmPasswordIcon.classList.remove('bi-eye-slash'); 164 | toggleConfirmPasswordIcon.classList.add('bi-eye'); 165 | } 166 | } 167 | 168 | console.log("modules") 169 | const logoutbtn = document.getElementById("signout") 170 | const loginbtn = document.getElementById("loginbtn") 171 | const signupbtn = document.getElementById("signupbtn") 172 | if (localStorage.getItem("authToken")) { 173 | loginbtn.style.display = "none" 174 | signupbtn.style.display= "none" 175 | }else{ 176 | 177 | logoutbtn.style.display = "none" 178 | } 179 | document.getElementById("logoutbtn2").addEventListener("click",()=>{ 180 | console.log("clickedbskjkbvsdhvbskdmbvduksvbsdmhcbjh") 181 | localStorage.clear() 182 | window.location.href="/" 183 | }) 184 | 185 | // // Check if the user is logged in by verifying the presence of an auth token in localStorage 186 | // if (localStorage.getItem("authToken")) { 187 | // console.log("User logged in"); 188 | 189 | // // Select all elements with the class 'authcomp' 190 | // const authElements = document.getElementsByClassName("authcomp"); 191 | 192 | // // Loop through the elements and change their opacity 193 | // Array.from(authElements).forEach(element => { 194 | // element.style.opacity = 0; // Set opacity to 0 (hidden) 195 | // element.style.pointerEvents = "none"; // Prevent interaction 196 | // }); 197 | // } else { 198 | // console.log("User not logged in"); 199 | // } 200 | document.querySelector( 201 | ".copyright-text" 202 | ).textContent = `Copyright © ${new Date().getFullYear()} InsightSync. All rights reserved.`; -------------------------------------------------------------------------------- /js/custom.js: -------------------------------------------------------------------------------- 1 | 2 | (function ($) { 3 | 4 | "use strict"; 5 | 6 | // MENU 7 | $('.navbar-collapse a').on('click',function(){ 8 | $(".navbar-collapse").collapse('hide'); 9 | }); 10 | 11 | // CUSTOM LINK 12 | $('.smoothscroll').click(function(){ 13 | var el = $(this).attr('href'); 14 | var elWrapped = $(el); 15 | var header_height = $('.navbar').height(); 16 | 17 | scrollToDiv(elWrapped,header_height); 18 | return false; 19 | 20 | function scrollToDiv(element,navheight){ 21 | var offset = element.offset(); 22 | var offsetTop = offset.top; 23 | var totalScroll = offsetTop-navheight; 24 | 25 | $('body,html').animate({ 26 | scrollTop: totalScroll 27 | }, 300); 28 | } 29 | }); 30 | 31 | $(window).on('scroll', function(){ 32 | function isScrollIntoView(elem, index) { 33 | var docViewTop = $(window).scrollTop(); 34 | var docViewBottom = docViewTop + $(window).height(); 35 | var elemTop = $(elem).offset().top; 36 | var elemBottom = elemTop + $(window).height()*.5; 37 | if(elemBottom <= docViewBottom && elemTop >= docViewTop) { 38 | $(elem).addClass('active'); 39 | } 40 | if(!(elemBottom <= docViewBottom)) { 41 | $(elem).removeClass('active'); 42 | } 43 | var MainTimelineContainer = $('#vertical-scrollable-timeline')[0]; 44 | var MainTimelineContainerBottom = MainTimelineContainer.getBoundingClientRect().bottom - $(window).height()*.5; 45 | $(MainTimelineContainer).find('.inner').css('height',MainTimelineContainerBottom+'px'); 46 | } 47 | var timeline = $('#vertical-scrollable-timeline li'); 48 | Array.from(timeline).forEach(isScrollIntoView); 49 | }); 50 | 51 | })(window.jQuery); 52 | 53 | 54 | window.onscroll = function() {scrollFunction()}; 55 | 56 | function scrollFunction() { 57 | if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) { 58 | document.getElementById("scrollToTopBtn").style.display = "block"; 59 | } else { 60 | document.getElementById("scrollToTopBtn").style.display = "none"; 61 | } 62 | } 63 | 64 | function topFunction() { 65 | document.body.scrollTop = 0; 66 | document.documentElement.scrollTop = 0; 67 | } 68 | -------------------------------------------------------------------------------- /js/edit-insight.js: -------------------------------------------------------------------------------- 1 | import { link } from "./Baselink.js"; 2 | 3 | const params = new URLSearchParams(window.location.search); 4 | const id = params.get('id'); 5 | 6 | if (!id) { 7 | alert("No topic ID provided in the URL."); 8 | window.location.href = 'index.html'; 9 | } 10 | 11 | const titleElement = document.getElementById("topic-title"); 12 | const imageElement = document.getElementById("topic-image"); 13 | const imageInput = document.getElementById("image-input"); 14 | const contentElement = document.getElementById("topic-content"); 15 | const submitButton = document.getElementById("submitbtn"); 16 | 17 | // Fetch topic data by ID 18 | axios.post(`${link}/api/v1/insight/getinsightbyid`, { id: id }) 19 | .then((response) => { 20 | const data = response.data; 21 | 22 | if (data.succes) { 23 | titleElement.value = data.data.title || "Untitled Topic"; 24 | imageElement.src = data.data.Image || "placeholder.jpg"; 25 | imageElement.alt = data.data.title || "Topic Image"; 26 | contentElement.value = data.data.content || ""; 27 | } else { 28 | alert("Topic not found. Redirecting to home page."); 29 | window.location.href = 'index.html'; 30 | } 31 | }) 32 | .catch((err) => { 33 | console.error("Error fetching topic:", err); 34 | alert("An error occurred while fetching the topic."); 35 | }); 36 | 37 | // Submit updated topic data 38 | submitButton.addEventListener("click", () => { 39 | const formData = new FormData(); 40 | formData.append("id", id); 41 | formData.append("title", titleElement.value); 42 | formData.append("content", contentElement.value); 43 | 44 | // Append image if a new one is selected 45 | if (imageInput.files[0]) { 46 | formData.append("image", imageInput.files[0]); 47 | } 48 | 49 | axios.post(`${link}/api/v1/insight/editinsight`, formData, { 50 | headers: { 51 | "authorization": `Bearer ${localStorage.getItem('authToken')}`, 52 | "Content-Type": "multipart/form-data" 53 | } 54 | }) 55 | .then((response) => { 56 | console.log(response) 57 | if (response.data.succes) { 58 | alert("Topic updated successfully."); 59 | window.location.href = `topics-detail.html?id=${id}`; 60 | } else { 61 | alert("Failed to update topic."); 62 | } 63 | }) 64 | .catch((err) => { 65 | console.error("Error updating topic:", err); 66 | alert("An error occurred while updating the topic. You may not be the author of the topic"); 67 | }); 68 | }); 69 | 70 | if (localStorage.getItem("authToken") == null) { 71 | alert("you are not logged in") 72 | window.location.href="index.html" 73 | } -------------------------------------------------------------------------------- /js/edit-profile.js: -------------------------------------------------------------------------------- 1 | import { link } from "./Baselink.js"; 2 | 3 | // Function to show notification 4 | function showNotification(message, type) { 5 | const notification = document.createElement('div'); 6 | notification.classList.add('notification'); 7 | notification.textContent = message; 8 | 9 | if (type === 'success') { 10 | notification.style.backgroundColor = '#4CAF50'; // Green for success 11 | } else if (type === 'error') { 12 | notification.style.backgroundColor = '#f44336'; // Red for error 13 | } else { 14 | notification.style.backgroundColor = '#2196F3'; // Blue for default info 15 | } 16 | 17 | document.body.appendChild(notification); 18 | 19 | // Remove notification after 3 seconds 20 | setTimeout(() => { 21 | notification.remove(); 22 | window.location.href = "/profile.html"; 23 | }, 3000); 24 | } 25 | 26 | async function fetchProfile() { 27 | const profileUrl = `${link}/api/v1/profile/getprofile`; 28 | const authToken = localStorage.getItem("authToken"); 29 | 30 | if (!authToken) { 31 | showNotification("Authorization token not found. Please log in.", 'error'); 32 | window.location.href = "/index.html"; 33 | return; 34 | } 35 | 36 | try { 37 | const profileResponse = await axios.get(profileUrl, { 38 | headers: { 39 | "Authorization": `Bearer ${authToken}`, 40 | "Content-Type": "application/json" 41 | } 42 | }); 43 | populateForm(profileResponse.data.user); 44 | } catch (error) { 45 | showNotification("Error fetching profile. Please try again.", 'error'); 46 | console.error("Error fetching profile:", error.message); 47 | } 48 | } 49 | 50 | function populateForm(user) { 51 | document.getElementById("username").value = user.username || ""; 52 | document.getElementById("email").value = user.email || ""; 53 | const profileImage = document.getElementById("profile-image").querySelector("img"); 54 | if (user.profileImage) { 55 | profileImage.src = user.profileImage; 56 | } 57 | } 58 | 59 | document.getElementById("edit-profile-form").addEventListener("submit", async (event) => { 60 | event.preventDefault(); 61 | 62 | const formData = new FormData(event.target); 63 | const updateUrl = `${link}/api/v1/profile/editprofile`; 64 | const authToken = localStorage.getItem("authToken"); 65 | 66 | try { 67 | const response = await axios.post(updateUrl, formData, { 68 | headers: { 69 | "Authorization": `Bearer ${authToken}`, 70 | "Content-Type": "multipart/form-data" 71 | } 72 | }); 73 | 74 | showNotification("Profile updated successfully!", 'success'); 75 | fetchProfile(); // Refresh the profile 76 | } catch (error) { 77 | showNotification("Failed to update profile. Please try again.", 'error'); 78 | console.error("Error updating profile:", error.message); 79 | } 80 | }); 81 | 82 | window.onload = fetchProfile; -------------------------------------------------------------------------------- /js/jquery.sticky.js: -------------------------------------------------------------------------------- 1 | // Sticky Plugin v1.0.3 for jQuery 2 | // ============= 3 | // Author: Anthony Garand 4 | // Improvements by German M. Bravo (Kronuz) and Ruud Kamphuis (ruudk) 5 | // Improvements by Leonardo C. Daronco (daronco) 6 | // Created: 02/14/2011 7 | // Date: 07/20/2015 8 | // Website: http://stickyjs.com/ 9 | // Description: Makes an element on the page stick on the screen as you scroll 10 | // It will only set the 'top' and 'position' of your element, you 11 | // might need to adjust the width in some cases. 12 | 13 | (function($) { 14 | var slice = Array.prototype.slice; // save ref to original slice() 15 | var splice = Array.prototype.splice; // save ref to original slice() 16 | 17 | var defaults = { 18 | topSpacing: 0, 19 | bottomSpacing: 0, 20 | className: 'is-sticky', 21 | wrapperClassName: 'sticky-wrapper', 22 | center: false, 23 | getWidthFrom: '', 24 | widthFromWrapper: true, // works only when .getWidthFrom is empty 25 | responsiveWidth: false 26 | }, 27 | $window = $(window), 28 | $document = $(document), 29 | sticked = [], 30 | windowHeight = $window.height(), 31 | scroller = function() { 32 | var scrollTop = $window.scrollTop(), 33 | documentHeight = $document.height(), 34 | dwh = documentHeight - windowHeight, 35 | extra = (scrollTop > dwh) ? dwh - scrollTop : 0; 36 | 37 | for (var i = 0; i < sticked.length; i++) { 38 | var s = sticked[i], 39 | elementTop = s.stickyWrapper.offset().top, 40 | etse = elementTop - s.topSpacing - extra; 41 | 42 | //update height in case of dynamic content 43 | s.stickyWrapper.css('height', s.stickyElement.outerHeight()); 44 | 45 | if (scrollTop <= etse) { 46 | if (s.currentTop !== null) { 47 | s.stickyElement 48 | .css({ 49 | 'width': '', 50 | 'position': '', 51 | 'top': '' 52 | }); 53 | s.stickyElement.parent().removeClass(s.className); 54 | s.stickyElement.trigger('sticky-end', [s]); 55 | s.currentTop = null; 56 | } 57 | } 58 | else { 59 | var newTop = documentHeight - s.stickyElement.outerHeight() 60 | - s.topSpacing - s.bottomSpacing - scrollTop - extra; 61 | if (newTop < 0) { 62 | newTop = newTop + s.topSpacing; 63 | } else { 64 | newTop = s.topSpacing; 65 | } 66 | if (s.currentTop != newTop) { 67 | var newWidth; 68 | if (s.getWidthFrom) { 69 | newWidth = $(s.getWidthFrom).width() || null; 70 | } else if (s.widthFromWrapper) { 71 | newWidth = s.stickyWrapper.width(); 72 | } 73 | if (newWidth == null) { 74 | newWidth = s.stickyElement.width(); 75 | } 76 | s.stickyElement 77 | .css('width', newWidth) 78 | .css('position', 'fixed') 79 | .css('top', newTop); 80 | 81 | s.stickyElement.parent().addClass(s.className); 82 | 83 | if (s.currentTop === null) { 84 | s.stickyElement.trigger('sticky-start', [s]); 85 | } else { 86 | // sticky is started but it have to be repositioned 87 | s.stickyElement.trigger('sticky-update', [s]); 88 | } 89 | 90 | if (s.currentTop === s.topSpacing && s.currentTop > newTop || s.currentTop === null && newTop < s.topSpacing) { 91 | // just reached bottom || just started to stick but bottom is already reached 92 | s.stickyElement.trigger('sticky-bottom-reached', [s]); 93 | } else if(s.currentTop !== null && newTop === s.topSpacing && s.currentTop < newTop) { 94 | // sticky is started && sticked at topSpacing && overflowing from top just finished 95 | s.stickyElement.trigger('sticky-bottom-unreached', [s]); 96 | } 97 | 98 | s.currentTop = newTop; 99 | } 100 | } 101 | } 102 | }, 103 | resizer = function() { 104 | windowHeight = $window.height(); 105 | 106 | for (var i = 0; i < sticked.length; i++) { 107 | var s = sticked[i]; 108 | var newWidth = null; 109 | if (s.getWidthFrom) { 110 | if (s.responsiveWidth === true) { 111 | newWidth = $(s.getWidthFrom).width(); 112 | } 113 | } else if(s.widthFromWrapper) { 114 | newWidth = s.stickyWrapper.width(); 115 | } 116 | if (newWidth != null) { 117 | s.stickyElement.css('width', newWidth); 118 | } 119 | } 120 | }, 121 | methods = { 122 | init: function(options) { 123 | var o = $.extend({}, defaults, options); 124 | return this.each(function() { 125 | var stickyElement = $(this); 126 | 127 | var stickyId = stickyElement.attr('id'); 128 | var stickyHeight = stickyElement.outerHeight(); 129 | var wrapperId = stickyId ? stickyId + '-' + defaults.wrapperClassName : defaults.wrapperClassName 130 | var wrapper = $('
') 131 | .attr('id', wrapperId) 132 | .addClass(o.wrapperClassName); 133 | 134 | stickyElement.wrapAll(wrapper); 135 | 136 | var stickyWrapper = stickyElement.parent(); 137 | 138 | if (o.center) { 139 | stickyWrapper.css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"}); 140 | } 141 | 142 | if (stickyElement.css("float") == "right") { 143 | stickyElement.css({"float":"none"}).parent().css({"float":"right"}); 144 | } 145 | 146 | stickyWrapper.css('height', stickyHeight); 147 | 148 | o.stickyElement = stickyElement; 149 | o.stickyWrapper = stickyWrapper; 150 | o.currentTop = null; 151 | 152 | sticked.push(o); 153 | }); 154 | }, 155 | update: scroller, 156 | unstick: function(options) { 157 | return this.each(function() { 158 | var that = this; 159 | var unstickyElement = $(that); 160 | 161 | var removeIdx = -1; 162 | var i = sticked.length; 163 | while (i-- > 0) { 164 | if (sticked[i].stickyElement.get(0) === that) { 165 | splice.call(sticked,i,1); 166 | removeIdx = i; 167 | } 168 | } 169 | if(removeIdx != -1) { 170 | unstickyElement.unwrap(); 171 | unstickyElement 172 | .css({ 173 | 'width': '', 174 | 'position': '', 175 | 'top': '', 176 | 'float': '' 177 | }) 178 | ; 179 | } 180 | }); 181 | } 182 | }; 183 | 184 | // should be more efficient than using $window.scroll(scroller) and $window.resize(resizer): 185 | if (window.addEventListener) { 186 | window.addEventListener('scroll', scroller, false); 187 | window.addEventListener('resize', resizer, false); 188 | } else if (window.attachEvent) { 189 | window.attachEvent('onscroll', scroller); 190 | window.attachEvent('onresize', resizer); 191 | } 192 | 193 | $.fn.sticky = function(method) { 194 | if (methods[method]) { 195 | return methods[method].apply(this, slice.call(arguments, 1)); 196 | } else if (typeof method === 'object' || !method ) { 197 | return methods.init.apply( this, arguments ); 198 | } else { 199 | $.error('Method ' + method + ' does not exist on jQuery.sticky'); 200 | } 201 | }; 202 | 203 | $.fn.unstick = function(method) { 204 | if (methods[method]) { 205 | return methods[method].apply(this, slice.call(arguments, 1)); 206 | } else if (typeof method === 'object' || !method ) { 207 | return methods.unstick.apply( this, arguments ); 208 | } else { 209 | $.error('Method ' + method + ' does not exist on jQuery.sticky'); 210 | } 211 | }; 212 | $(function() { 213 | setTimeout(scroller, 0); 214 | }); 215 | })(jQuery); 216 | 217 | $(document).ready(function(){ 218 | $(".navbar").sticky({topSpacing:0}); 219 | }); -------------------------------------------------------------------------------- /js/login.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | const loginForm = document.getElementById('loginForm'); 3 | if (loginForm) { 4 | loginForm.addEventListener('submit', handleLoginSubmit); 5 | } 6 | }); 7 | 8 | function handleLoginSubmit(event) { 9 | event.preventDefault(); // Prevent the default form submission 10 | 11 | const email = document.getElementById('loginEmail').value; 12 | const password = document.getElementById('loginPassword').value; 13 | 14 | const formData = { email, password }; 15 | 16 | fetch('https://insight-sync-u1bq.vercel.app/api/v1/auth/login', { 17 | // fetch('http://localhost:3000/api/v1/auth/login', { 18 | 19 | method: 'POST', 20 | headers: { 'Content-Type': 'application/json' }, 21 | body: JSON.stringify(formData), 22 | }) 23 | .then(response => response.json()) 24 | .then(data => { 25 | if (data.token) { 26 | Toastify({ 27 | text: 'Login successful!', 28 | duration: 3000, // Duration in milliseconds 29 | gravity: 'top', // Vertical position 30 | position: 'right', // Horizontal position 31 | backgroundColor: '#4caf50', // Green background for success 32 | stopOnFocus: true, // Stop timeout on hover 33 | className: 'toastify-custom', // Custom class for styling 34 | }).showToast(); 35 | 36 | localStorage.setItem('authToken', data.token); 37 | console.log(data.loggedInUser) 38 | localStorage.setItem('userid',data.loggedInUser) 39 | setTimeout(() => { 40 | window.location.href = '/'; 41 | }, 3000); // Redirect after 3 seconds 42 | } else { 43 | Toastify({ 44 | text: 'Login failed: ' + data.message, 45 | duration: 3000, // Duration in milliseconds 46 | gravity: 'top', // Vertical position 47 | position: 'right', // Horizontal position 48 | backgroundColor: '#ff4d4d', // Red background for error 49 | stopOnFocus: true, // Stop timeout on hover 50 | className: 'toastify-custom', // Custom class for styling 51 | }).showToast(); 52 | } 53 | }) 54 | .catch(error => { 55 | console.error('Error:', error); 56 | Toastify({ 57 | text: 'An error occurred. Please try again later.', 58 | duration: 3000, // Duration in milliseconds 59 | gravity: 'top', // Vertical position 60 | position: 'right', // Horizontal position 61 | backgroundColor: '#ff4d4d', // Red background for error 62 | stopOnFocus: true, // Stop timeout on hover 63 | className: 'toastify-custom', // Custom class for styling 64 | }).showToast(); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /js/profile.js: -------------------------------------------------------------------------------- 1 | const editbtn = document.getElementById("editbtn"); 2 | editbtn.addEventListener( 3 | "click", 4 | () => (window.location.href = "/edit-profile.html") 5 | ); 6 | 7 | import { link } from "./Baselink.js"; 8 | const saveforlaterUrl = `${link}/api/v1/insight/getsaveforlater` 9 | 10 | async function fetchesaveforlater() { 11 | const reap = await axios.get(saveforlaterUrl,{ 12 | headers: { 13 | Authorization: `Bearer ${localStorage.getItem("authToken")}`, 14 | "Content-Type": "application/json", 15 | }, 16 | }) 17 | console.log("saved",reap.data) 18 | displaySaveforlater(reap.data.savedTopics) 19 | } 20 | 21 | fetchesaveforlater() 22 | 23 | function displaySaveforlater(insights){ 24 | const insightsContainer = document.getElementById("saveforlatercont"); 25 | insightsContainer.innerHTML = `

User Saved Insights

`; 26 | 27 | if (!insights || insights.length === 0) { 28 | insightsContainer.innerHTML += `

No insights found.

`; 29 | return; 30 | } 31 | 32 | insights.forEach((insight) => { 33 | const insightCard = document.createElement("div"); 34 | insightCard.className = "insight-card"; 35 | insightCard.innerHTML = ` 36 |

${insight.title}

37 |

Topic: ${insight.topic}

38 |

${insight.content}

39 | ${ 40 | insight.Image 41 | ? `${insight.title}` 42 | : "" 43 | } 44 | `; 45 | insightsContainer.appendChild(insightCard); 46 | }); 47 | } 48 | import { link } from "./Baselink.js"; 49 | console.log(localStorage); 50 | 51 | async function fetchProfile() { 52 | const profileUrl = `${link}/api/v1/profile/getprofile`; 53 | const insightsUrl = `${link}/api/v1/insight/getinsightbyuser`; 54 | const authToken = localStorage.getItem("authToken"); 55 | 56 | // Check if authToken exists, if not, show specific notification and redirect 57 | if (!authToken) { 58 | showNotification( 59 | "You need to log in to access this page. Redirecting..." 60 | ); 61 | setTimeout(() => { 62 | window.location.href = "/index.html"; 63 | }, 5000); 64 | return; // Stop further execution 65 | } 66 | 67 | try { 68 | // Fetch profile details 69 | const profileResponse = await axios.get(profileUrl, { 70 | headers: { 71 | Authorization: `Bearer ${authToken}`, 72 | "Content-Type": "application/json", 73 | }, 74 | }); 75 | displayProfile(profileResponse.data.user); 76 | 77 | // Fetch insights 78 | const insightsResponse = await axios.get(insightsUrl, { 79 | headers: { 80 | Authorization: `Bearer ${authToken}`, 81 | "Content-Type": "application/json", 82 | }, 83 | }); 84 | displayInsights(insightsResponse.data.insights); 85 | } catch (error) { 86 | // Handle specific errors 87 | if (error.response && error.response.status === 401) { 88 | showNotification("Unauthorized access. Please log in."); 89 | setTimeout(() => { 90 | window.location.href = "/index.html"; 91 | }, 5000); 92 | } 93 | } 94 | } 95 | 96 | 97 | function displayProfile(user) { 98 | console.log(user) 99 | const profileInfoDiv = document.getElementById("profile-info"); 100 | const formattedCreatedOn = user.createdOn 101 | ? new Date(user.createdOn).toLocaleDateString() 102 | : "N/A"; 103 | console.log(user); 104 | profileInfoDiv.innerHTML = ` 105 |
106 | profile image 107 |
108 |

Username: ${user.username}

109 |

Email: ${user.email}

110 |

Insights Count: ${user.inSightsCount}

111 |

Account Created On: ${formattedCreatedOn}

112 | `; 113 | } 114 | 115 | function displayInsights(insights) { 116 | const insightsContainer = document.getElementById("insights-container"); 117 | insightsContainer.innerHTML = `

User Insights

`; 118 | 119 | if (!insights || insights.length === 0) { 120 | insightsContainer.innerHTML += `

No insights found.

`; 121 | return; 122 | } 123 | 124 | insights.forEach((insight) => { 125 | const insightCard = document.createElement("div"); 126 | insightCard.className = "insight-card"; 127 | insightCard.innerHTML = ` 128 |

${insight.title}

129 |

Topic: ${insight.topic}

130 |

${insight.content}

131 | ${ 132 | insight.Image 133 | ? `${insight.title}` 134 | : "" 135 | } 136 | `; 137 | insightsContainer.appendChild(insightCard); 138 | }); 139 | } 140 | 141 | function showNotification(message) { 142 | const notification = document.getElementById("notification"); 143 | const notificationMessage = document.getElementById( 144 | "notification-message" 145 | ); 146 | 147 | notificationMessage.textContent = message; 148 | notification.style.display = "block"; 149 | 150 | // Automatically hide and redirect after 5 seconds 151 | setTimeout(() => { 152 | notification.style.display = "none"; 153 | }, 5000); 154 | } 155 | 156 | 157 | 158 | window.onload = fetchProfile; -------------------------------------------------------------------------------- /js/publicprofile.js: -------------------------------------------------------------------------------- 1 | import { link } from "./Baselink.js"; 2 | 3 | async function fetchPublicProfile() { 4 | const urlParams = new URLSearchParams(window.location.search); 5 | const username = urlParams.get("username"); 6 | if (!username) { 7 | showNotification("Username not provided in URL."); 8 | return; 9 | } 10 | 11 | const publicProfileUrl = `${link}/api/v1/profile/getpublicprofile/${username}`; 12 | 13 | try { 14 | const response = await fetch(publicProfileUrl, { 15 | headers: { "Content-Type": "application/json" }, 16 | }); 17 | const data = await response.json(); 18 | displayProfile(data); 19 | } catch (error) { 20 | showNotification("Error fetching profile."); 21 | console.error(error); 22 | } 23 | } 24 | 25 | function displayProfile(user) { 26 | const profileInfoDiv = document.getElementById("profile-info"); 27 | profileInfoDiv.innerHTML = ` 28 |
29 |
30 | Profile image 31 |
32 |

Username: ${user.username}

33 |

Insights Count: ${user.inSightsCount}

34 |
35 | `; 36 | } 37 | 38 | function showNotification(message) { 39 | const notification = document.getElementById("notification"); 40 | notification.textContent = message; 41 | notification.style.display = "block"; 42 | setTimeout(() => { 43 | notification.style.display = "none"; 44 | }, 5000); 45 | } 46 | 47 | window.onload = fetchPublicProfile; 48 | 49 | // console.log("modules") 50 | const logoutbtn = document.getElementById("signout") 51 | const loginbtn = document.getElementById("loginbtn") 52 | const signupbtn = document.getElementById("signupbtn") 53 | if (localStorage.getItem("authToken")) { 54 | loginbtn.style.display = "none" 55 | signupbtn.style.display= "none" 56 | }else{ 57 | logoutbtn.style.display = "none" 58 | } 59 | document.getElementById("logoutbtn2").addEventListener("click",()=>{ 60 | // console.log("clickedbskjkbvsdhvbskdmbvduksvbsdmhcbjh") 61 | localStorage.clear() 62 | window.location.href="/" 63 | }) -------------------------------------------------------------------------------- /js/signup.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | const signupForm = document.getElementById('signupForm'); 3 | if (signupForm) { 4 | signupForm.addEventListener('submit', handleSignupSubmit); 5 | } 6 | }); 7 | 8 | function handleSignupSubmit(event) { 9 | event.preventDefault(); // Prevent the default form submission 10 | 11 | const username = document.getElementById('signupUsername').value; 12 | const email = document.getElementById('signupEmail').value; 13 | const password = document.getElementById('signupPassword').value; 14 | const confirmPassword = document.getElementById('signupConfirmPassword').value; 15 | 16 | if (password !== confirmPassword) { 17 | Toastify({ 18 | text: 'Passwords do not match', 19 | duration: 3000, // Duration in milliseconds 20 | gravity: 'top', // Vertical position 21 | position: 'right', // Horizontal position 22 | backgroundColor: '#ff4d4d', // Red background for error 23 | stopOnFocus: true, // Stop timeout on hover 24 | className: 'toastify-custom', // Custom class for styling 25 | }).showToast(); 26 | return; 27 | } 28 | 29 | const formData = { username, email, password }; 30 | 31 | fetch('https://insight-sync-u1bq.vercel.app/api/v1/auth/signup', { 32 | // fetch('http://localhost:3000/api/v1/auth/signup', { 33 | 34 | method: 'POST', 35 | headers: { 'Content-Type': 'application/json' }, 36 | body: JSON.stringify(formData), 37 | }) 38 | .then(response => response.json()) 39 | .then(data => { 40 | if (data.token) { 41 | Toastify({ 42 | text: 'Signup successful! You are now logged in.', 43 | duration: 3000, 44 | gravity: 'top', 45 | position: 'right', 46 | backgroundColor: '#4caf50', // Green background for success 47 | stopOnFocus: true, 48 | className: 'toastify-custom', 49 | }).showToast(); 50 | 51 | localStorage.setItem('authToken', data.token); 52 | setTimeout(() => { 53 | window.location.href = '/'; 54 | }, 3000); // Redirect after 3 seconds 55 | } else { 56 | Toastify({ 57 | text: 'Signup failed: ' + data.message, 58 | duration: 3000, 59 | gravity: 'top', 60 | position: 'right', 61 | backgroundColor: '#ff4d4d', // Red background for error 62 | stopOnFocus: true, 63 | className: 'toastify-custom', 64 | }).showToast(); 65 | } 66 | }) 67 | .catch(error => { 68 | console.error('Error:', error); 69 | Toastify({ 70 | text: 'An error occurred. Please try again later.', 71 | duration: 3000, 72 | gravity: 'top', 73 | position: 'right', 74 | backgroundColor: '#ff4d4d', // Red background for error 75 | stopOnFocus: true, 76 | className: 'toastify-custom', 77 | }).showToast(); 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /js/topics-detail.js: -------------------------------------------------------------------------------- 1 | function togglePasswordVisibility() { 2 | const passwordInput = document.getElementById('loginPassword'); 3 | const togglePasswordIcon = document.getElementById('togglePasswordIcon'); 4 | if (passwordInput.type === 'password') { 5 | passwordInput.type = 'text'; 6 | togglePasswordIcon.classList.remove('bi-eye'); 7 | togglePasswordIcon.classList.add('bi-eye-slash'); 8 | } else { 9 | passwordInput.type = 'password'; 10 | togglePasswordIcon.classList.remove('bi-eye-slash'); 11 | togglePasswordIcon.classList.add('bi-eye'); 12 | } 13 | } 14 | 15 | function validateForm() { 16 | const email = document.getElementById('loginEmail'); 17 | const password = document.getElementById('loginPassword'); 18 | let valid = true; 19 | 20 | if (!email.value) { 21 | email.classList.add('is-invalid'); 22 | valid = false; 23 | } else { 24 | email.classList.remove('is-invalid'); 25 | } 26 | 27 | if (!password.value) { 28 | password.classList.add('is-invalid'); 29 | valid = false; 30 | } else { 31 | password.classList.remove('is-invalid'); 32 | } 33 | 34 | return valid; 35 | } 36 | function validateSignupForm() { 37 | const username = document.getElementById('signupUsername'); 38 | const email = document.getElementById('signupEmail'); 39 | const password = document.getElementById('signupPassword'); 40 | const confirmPassword = document.getElementById('signupConfirmPassword'); 41 | let valid = true; 42 | 43 | // Username validation (must be at least 3 characters) 44 | if (username.value.length < 3) { 45 | username.classList.add('is-invalid'); 46 | valid = false; 47 | } else { 48 | username.classList.remove('is-invalid'); 49 | } 50 | 51 | // Email validation (checks format) 52 | const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; 53 | if (!emailPattern.test(email.value)) { 54 | email.classList.add('is-invalid'); 55 | valid = false; 56 | } else { 57 | email.classList.remove('is-invalid'); 58 | } 59 | 60 | // Password validation (at least 6 characters and includes a number) 61 | const passwordPattern = /^(?=.*\d)(?=.*[a-zA-Z]).{6,}$/; 62 | if (!passwordPattern.test(password.value)) { 63 | password.classList.add('is-invalid'); 64 | valid = false; 65 | } else { 66 | password.classList.remove('is-invalid'); 67 | } 68 | 69 | // Confirm password validation (must match the password) 70 | if (password.value !== confirmPassword.value) { 71 | confirmPassword.classList.add('is-invalid'); 72 | valid = false; 73 | } else { 74 | confirmPassword.classList.remove('is-invalid'); 75 | } 76 | 77 | return valid; 78 | } 79 | 80 | function toggleSignupPasswordVisibility() { 81 | const passwordInput = document.getElementById('signupPassword'); 82 | const togglePasswordIcon = document.getElementById('toggleSignupPasswordIcon'); 83 | if (passwordInput.type === 'password') { 84 | passwordInput.type = 'text'; 85 | togglePasswordIcon.classList.remove('bi-eye'); 86 | togglePasswordIcon.classList.add('bi-eye-slash'); 87 | } else { 88 | passwordInput.type = 'password'; 89 | togglePasswordIcon.classList.remove('bi-eye-slash'); 90 | togglePasswordIcon.classList.add('bi-eye'); 91 | } 92 | } 93 | 94 | function toggleSignupConfirmPasswordVisibility() { 95 | const confirmPasswordInput = document.getElementById('signupConfirmPassword'); 96 | const toggleConfirmPasswordIcon = document.getElementById('toggleSignupConfirmPasswordIcon'); 97 | if (confirmPasswordInput.type === 'password') { 98 | confirmPasswordInput.type = 'text'; 99 | toggleConfirmPasswordIcon.classList.remove('bi-eye'); 100 | toggleConfirmPasswordIcon.classList.add('bi-eye-slash'); 101 | } else { 102 | confirmPasswordInput.type = 'password'; 103 | toggleConfirmPasswordIcon.classList.remove('bi-eye-slash'); 104 | toggleConfirmPasswordIcon.classList.add('bi-eye'); 105 | } 106 | } 107 | 108 | import { link } from "./Baselink.js"; 109 | 110 | const params4 = new URLSearchParams(window.location.search); 111 | const id4 = params4.get('id'); 112 | // console.log("forsave for later", id4); 113 | 114 | const savebtn1 = document.getElementById("savebtn"); 115 | // console.log("savebtn element:", savebtn1); 116 | 117 | if (!savebtn1) { 118 | console.error("Button with id 'savebtn' not found"); 119 | } 120 | 121 | const token = localStorage.getItem("authToken"); 122 | if (!token) { 123 | console.error("Auth token not found"); 124 | } else { 125 | axios.get(`${link}/api/v1/insight/getsaveforlater`, { 126 | headers: { 127 | Authorization: `Bearer ${token}`, 128 | "Content-Type": "application/json", 129 | }, 130 | }) 131 | .then((resp) => { 132 | // console.log("fetch save", resp.data.savedTopics); 133 | if (resp.data.savedTopics.some(el => el._id === id4)) { 134 | // console.log("included"); 135 | savebtn1.innerHTML = "Saved"; 136 | } else { 137 | // console.log("not included"); 138 | savebtn1.innerText = "Save For Later"; 139 | } 140 | }) 141 | .catch((err) => { 142 | console.error("API error:", err); 143 | }); 144 | } 145 | 146 | import { link } from "./Baselink.js" 147 | const params3 = new URLSearchParams(window.location.search); 148 | const id3 = params.get('id'); 149 | const savebtn = document.getElementById("savebtn") 150 | savebtn.addEventListener("click",()=>{ 151 | // console.log("clickeed") 152 | axios.post(`${link}/api/v1/insight/saveforlater`,{t_id:id3},{ 153 | headers: { 154 | Authorization: `Bearer ${localStorage.getItem("authToken")}`, 155 | "Content-Type": "application/json", 156 | }, 157 | }) 158 | .then((data)=>{ 159 | // console.log(data.data) 160 | if (data.data.updationStatus) { 161 | savebtn.innerText="Saved" 162 | }else{ 163 | savebtn.innerText="Save For Later" 164 | } 165 | }) 166 | .catch((err)=>{console.error(err)}) 167 | }) 168 | 169 | import { link } from "./Baselink.js" 170 | // const username = "exampleUser"; // Replace this with the actual username 171 | const profileLink = document.getElementById("profilelink"); 172 | console.log(profileLink) 173 | 174 | 175 | // console.log(link) 176 | const usernamecont = document.getElementById("username") 177 | const userimg = document.getElementById("userimg") 178 | window.onload = async () => { 179 | const params = new URLSearchParams(window.location.search); 180 | const id = params.get('id'); 181 | // console.log("Insight ID:", id); 182 | 183 | // Fetch data for the specific ID or perform any other logic 184 | axios.post(`${link}/api/v1/insight/getinsightbyid`, { 185 | id 186 | }) 187 | .then(({ data }) => { 188 | console.log(data); 189 | 190 | // Set image source 191 | const img = document.getElementById("topic-image-id"); 192 | if (img) { 193 | img.src = data.data.Image; 194 | } 195 | 196 | // Set heading text 197 | const headings = document.querySelectorAll(".topic-header"); 198 | console.log(headings) 199 | headings.forEach(heading => { 200 | heading.innerHTML = data.data.title 201 | }); 202 | 203 | // Set topic content 204 | const contentClass = document.getElementById("topic-content") 205 | contentClass.innerHTML = data.data.content 206 | 207 | if (data.data.likedBy.includes(localStorage.getItem('userid'))) { 208 | console.log("user liked") 209 | likeIcon.classList.remove("far"); 210 | likeIcon.classList.add("fas", "liked"); 211 | } 212 | 213 | userimg.innerText = data.data.submittedby.username 214 | userimg.src = data.data.submittedby.image 215 | profileLink.href = `./puplicprofile.html?username=${data.data.submittedby._id}`; 216 | 217 | }) 218 | .catch((err) => { 219 | console.error("Error fetching insight data:", err); 220 | }); 221 | }; 222 | 223 | // Check if the user is logged in by verifying the presence of an auth token in localStorage 224 | // if (localStorage.getItem("authToken")) { 225 | // console.log("User logged in"); 226 | 227 | // // Select all elements with the class 'authcomp' 228 | // const authElements = document.getElementsByClassName("authcomp"); 229 | 230 | // // Loop through the elements and change their opacity 231 | // Array.from(authElements).forEach(element => { 232 | // element.style.opacity = 0; // Set opacity to 0 (hidden) 233 | // element.style.pointerEvents = "none"; // Prevent interaction 234 | // }); 235 | // } else { 236 | // console.log("User not logged in"); 237 | // } 238 | document.querySelector( 239 | ".copyright-text" 240 | ).textContent = `Copyright © ${new Date().getFullYear()} InsightSync. All rights reserved.`; 241 | 242 | const editbtn = document.getElementById("editbtn") 243 | const params = new URLSearchParams(window.location.search); 244 | // console.log(params.get('id')) 245 | editbtn.addEventListener("click",()=>{ 246 | window.location.href=`edit-insight.html?id=${params.get('id')}` 247 | }) 248 | 249 | import { link } from "./Baselink.js" 250 | const params2 = new URLSearchParams(window.location.search); 251 | const id = params2.get('id'); 252 | 253 | const likeBtn = document.getElementById("likebtn"); 254 | const likeIcon = document.getElementById("likeIcon"); 255 | let isLiked = false; 256 | 257 | 258 | 259 | likeBtn.addEventListener("click", () => { 260 | axios.post(`${link}/api/v1/insight/like/${id}`, {}, { // Empty object for request body 261 | headers: { 262 | 'authorization': `Bearer ${localStorage.getItem('authToken')}` 263 | } 264 | }) 265 | .then((resp) => { 266 | // console.log(resp.data); 267 | if (resp.data.liked) { 268 | likeIcon.classList.remove("far"); 269 | likeIcon.classList.add("fas", "liked"); 270 | }else{ 271 | likeIcon.classList.remove("fas", "liked"); 272 | likeIcon.classList.add("far"); 273 | } 274 | }) 275 | .catch((error) => { 276 | console.error("Error liking the insight:", error); 277 | }); 278 | }); 279 | 280 | // console.log("modules") 281 | const logoutbtn = document.getElementById("signout") 282 | const loginbtn = document.getElementById("loginbtn") 283 | const signupbtn = document.getElementById("signupbtn") 284 | if (localStorage.getItem("authToken")) { 285 | loginbtn.style.display = "none" 286 | signupbtn.style.display= "none" 287 | }else{ 288 | logoutbtn.style.display = "none" 289 | } 290 | document.getElementById("logoutbtn2").addEventListener("click",()=>{ 291 | // console.log("clickedbskjkbvsdhvbskdmbvduksvbsdmhcbjh") 292 | localStorage.clear() 293 | window.location.href="/" 294 | }) -------------------------------------------------------------------------------- /js/topics-listing.js: -------------------------------------------------------------------------------- 1 | function togglePasswordVisibility() { 2 | const passwordInput = document.getElementById("loginPassword"); 3 | const togglePasswordIcon = 4 | document.getElementById("togglePasswordIcon"); 5 | if (passwordInput.type === "password") { 6 | passwordInput.type = "text"; 7 | togglePasswordIcon.classList.remove("bi-eye"); 8 | togglePasswordIcon.classList.add("bi-eye-slash"); 9 | } else { 10 | passwordInput.type = "password"; 11 | togglePasswordIcon.classList.remove("bi-eye-slash"); 12 | togglePasswordIcon.classList.add("bi-eye"); 13 | } 14 | } 15 | 16 | function validateForm() { 17 | const email = document.getElementById("loginEmail"); 18 | const password = document.getElementById("loginPassword"); 19 | let valid = true; 20 | 21 | if (!email.value) { 22 | email.classList.add("is-invalid"); 23 | valid = false; 24 | } else { 25 | email.classList.remove("is-invalid"); 26 | } 27 | 28 | if (!password.value) { 29 | password.classList.add("is-invalid"); 30 | valid = false; 31 | } else { 32 | password.classList.remove("is-invalid"); 33 | } 34 | 35 | return valid; 36 | } 37 | function validateSignupForm() { 38 | const username = document.getElementById("signupUsername"); 39 | const email = document.getElementById("signupEmail"); 40 | const password = document.getElementById("signupPassword"); 41 | const confirmPassword = document.getElementById( 42 | "signupConfirmPassword" 43 | ); 44 | let valid = true; 45 | 46 | // Username validation (must be at least 3 characters) 47 | if (username.value.length < 3) { 48 | username.classList.add("is-invalid"); 49 | valid = false; 50 | } else { 51 | username.classList.remove("is-invalid"); 52 | } 53 | 54 | // Email validation (checks format) 55 | const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; 56 | if (!emailPattern.test(email.value)) { 57 | email.classList.add("is-invalid"); 58 | valid = false; 59 | } else { 60 | email.classList.remove("is-invalid"); 61 | } 62 | 63 | // Password validation (at least 6 characters and includes a number) 64 | const passwordPattern = /^(?=.*\d)(?=.*[a-zA-Z]).{6,}$/; 65 | if (!passwordPattern.test(password.value)) { 66 | password.classList.add("is-invalid"); 67 | valid = false; 68 | } else { 69 | password.classList.remove("is-invalid"); 70 | } 71 | 72 | // Confirm password validation (must match the password) 73 | if (password.value !== confirmPassword.value) { 74 | confirmPassword.classList.add("is-invalid"); 75 | valid = false; 76 | } else { 77 | confirmPassword.classList.remove("is-invalid"); 78 | } 79 | 80 | return valid; 81 | } 82 | 83 | function toggleSignupPasswordVisibility() { 84 | const passwordInput = document.getElementById("signupPassword"); 85 | const togglePasswordIcon = document.getElementById( 86 | "toggleSignupPasswordIcon" 87 | ); 88 | if (passwordInput.type === "password") { 89 | passwordInput.type = "text"; 90 | togglePasswordIcon.classList.remove("bi-eye"); 91 | togglePasswordIcon.classList.add("bi-eye-slash"); 92 | } else { 93 | passwordInput.type = "password"; 94 | togglePasswordIcon.classList.remove("bi-eye-slash"); 95 | togglePasswordIcon.classList.add("bi-eye"); 96 | } 97 | } 98 | 99 | function toggleSignupConfirmPasswordVisibility() { 100 | const confirmPasswordInput = document.getElementById( 101 | "signupConfirmPassword" 102 | ); 103 | const toggleConfirmPasswordIcon = document.getElementById( 104 | "toggleSignupConfirmPasswordIcon" 105 | ); 106 | if (confirmPasswordInput.type === "password") { 107 | confirmPasswordInput.type = "text"; 108 | toggleConfirmPasswordIcon.classList.remove("bi-eye"); 109 | toggleConfirmPasswordIcon.classList.add("bi-eye-slash"); 110 | } else { 111 | confirmPasswordInput.type = "password"; 112 | toggleConfirmPasswordIcon.classList.remove("bi-eye-slash"); 113 | toggleConfirmPasswordIcon.classList.add("bi-eye"); 114 | } 115 | } 116 | 117 | import { link } from "./Baselink.js"; 118 | 119 | window.onload = async () => { 120 | /* 121 |
122 | 139 |
140 | */ 141 | axios 142 | .get(`${link}/api/v1/insight/getallinsight`) 143 | .then((resp) => { 144 | console.log(resp.data.data); 145 | // const parent = document.getElementById("parentCont"); 146 | const parent = document.getElementById("parentCont"); 147 | parent.classList.add('row','flex-wrap') 148 | 149 | resp.data.data.forEach((item) => { 150 | const colDiv = document.createElement("div"); 151 | colDiv.classList.add("col-lg-4", "col-md-6", "col-12", "mb-4","smh"); 152 | colDiv.id = item.updatedAt; 153 | 154 | const customBlock = document.createElement("div"); 155 | customBlock.classList.add("custom-block", "bg-white", "shadow-lg"); 156 | 157 | const linkElement = document.createElement("a"); 158 | linkElement.classList.add("designlink"); 159 | linkElement.href = `topics-detail.html?id=${item._id}`; 160 | 161 | const flexDiv = document.createElement("div"); 162 | flexDiv.classList.add("d-flex"); 163 | 164 | const textDiv = document.createElement("div"); 165 | 166 | const heading = document.createElement("h5"); 167 | heading.classList.add("mb-2", "designHeading"); 168 | heading.textContent = item.title; 169 | 170 | const paragraph = document.createElement("p"); 171 | paragraph.classList.add("mb-0", "designPara"); 172 | paragraph.textContent = item.content; 173 | 174 | textDiv.appendChild(heading); 175 | textDiv.appendChild(paragraph); 176 | 177 | const badge = document.createElement("span"); 178 | badge.classList.add("badge", "bg-design"); 179 | badge.textContent = item.topic; 180 | 181 | const like = document.createElement("span"); 182 | like.classList.add("badge", "bg-design"); 183 | like.textContent = item.likes; 184 | colDiv.setAttribute("data-liked", item.likes); 185 | 186 | const topicLikeDiv = document.createElement("div"); 187 | topicLikeDiv.style.display = "flex"; 188 | topicLikeDiv.style.justifyContent = "space-between"; 189 | topicLikeDiv.classList.add("ms-auto"); 190 | topicLikeDiv.appendChild(badge); 191 | topicLikeDiv.appendChild(like); 192 | 193 | flexDiv.appendChild(textDiv); 194 | // flexDiv.appendChild(badge); 195 | 196 | const image = document.createElement("img"); 197 | image.classList.add("custom-block-image", "img-fluid", "designimage"); 198 | image.src = item.Image || "default-image.png"; 199 | image.alt = item.title; 200 | 201 | linkElement.appendChild(image); 202 | linkElement.appendChild(flexDiv); 203 | linkElement.appendChild(topicLikeDiv); 204 | 205 | customBlock.appendChild(linkElement); 206 | colDiv.appendChild(customBlock); 207 | 208 | parent.appendChild(colDiv); 209 | }); 210 | }) 211 | .catch((err) => console.log(err)); 212 | }; 213 | 214 | const searchbtn = document.getElementById("searchbutton"); 215 | const searchinput = document.getElementById("searchinput"); 216 | 217 | searchbtn.addEventListener("click", () => { 218 | const elements = document.getElementsByClassName("smh"); 219 | const searchtext = searchinput.value.toLowerCase().trim(); // Normalize the search text for case-insensitive comparison 220 | 221 | Array.from(elements).forEach((element) => { 222 | const cardText = element.textContent.toLowerCase(); // Normalize card content for comparison 223 | if (cardText.includes(searchtext)) { 224 | element.style.display = "block"; // Show matching cards 225 | } else { 226 | element.style.display = "none"; // Hide non-matching cards 227 | } 228 | }); 229 | }); 230 | 231 | const sortbtn = document.getElementById("sortbtn"); 232 | 233 | sortbtn.addEventListener("change", () => { 234 | const sortOrder = sortbtn.value; // Get selected sort option 235 | const parent = document.getElementById("parentCont"); 236 | const cards = Array.from(parent.getElementsByClassName("smh")); // Get all card elements 237 | 238 | // Sort the cards array based on the 'id' attribute (createdAt) 239 | cards.sort((a, b) => { 240 | if (sortOrder === "new" || sortOrder === "old") { 241 | const dateA = new Date(a.id || 0); // Convert id to Date object, handle empty id 242 | const dateB = new Date(b.id || 0); 243 | 244 | if (sortOrder === "new") { 245 | return dateB - dateA; // Recent first 246 | } else if (sortOrder === "old") { 247 | return dateA - dateB; // Oldest first 248 | } 249 | } else if (sortOrder === "liked") { 250 | const likeA = a.dataset.liked; 251 | const likeB = b.dataset.liked; 252 | 253 | return likeB - likeA; 254 | } 255 | }); 256 | console.log(cards) 257 | 258 | // Append the sorted cards back to the parent container 259 | cards.forEach((card) => parent.appendChild(card)); 260 | }); 261 | 262 | // // Check if the user is logged in by verifying the presence of an auth token in localStorage 263 | // if (localStorage.getItem("authToken")) { 264 | // console.log("User logged in"); 265 | 266 | // // Select all elements with the class 'authcomp' 267 | // const authElements = document.getElementsByClassName("authcomp"); 268 | 269 | // // Loop through the elements and change their opacity 270 | // Array.from(authElements).forEach((element) => { 271 | // element.style.opacity = 0; // Set opacity to 0 (hidden) 272 | // element.style.pointerEvents = "none"; // Prevent interaction 273 | // }); 274 | // } else { 275 | // console.log("User not logged in"); 276 | // } 277 | document.querySelector( 278 | ".copyright-text" 279 | ).textContent = `Copyright © ${new Date().getFullYear()} InsightSync. All rights reserved.`; 280 | 281 | const sortbytopicbtn = document.getElementById("sortbytopicbtn") 282 | 283 | sortbytopicbtn.addEventListener("change", (e) => { 284 | const parentsortingElement =document.getElementsByClassName("smh"); 285 | console.log(parentsortingElement) 286 | Array.from(parentsortingElement).forEach((elem)=>{ 287 | elem.style.display="flex" 288 | console.log(elem.lastChild.lastChild.lastChild.innerText) 289 | console.log(e.target.value) 290 | // if (elem.lastChild.innerText !== e.target.value) { 291 | // console.log("element is",elem) 292 | // elem.style.display ="none" 293 | // // element.style.pointerEvents = "none"; // Prevent interaction 294 | // } 295 | if (elem.lastChild.lastChild.lastChild.innerText != e.target.value) { 296 | elem.style.display="none" 297 | } 298 | }) 299 | console.log(e.target.value) 300 | }) 301 | 302 | console.log("modules") 303 | const logoutbtn = document.getElementById("signout") 304 | const loginbtn = document.getElementById("loginbtn") 305 | const signupbtn = document.getElementById("signupbtn") 306 | if (localStorage.getItem("authToken")) { 307 | loginbtn.style.display = "none" 308 | signupbtn.style.display= "none" 309 | }else{ 310 | 311 | logoutbtn.style.display = "none" 312 | } 313 | document.getElementById("logoutbtn2").addEventListener("click",()=>{ 314 | console.log("clickedbskjkbvsdhvbskdmbvduksvbsdmhcbjh") 315 | localStorage.clear() 316 | window.location.href="/" 317 | }) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "axios": "^1.7.9", 4 | "cloudinary": "^2.5.1", 5 | "dotenv": "^16.4.7", 6 | "multer": "^1.4.5-lts.1", 7 | "streamifier": "^0.1.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Profile Page 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 140 |
141 | 142 |
143 |
144 |

Profile Information

145 |
146 |

Loading profile details...

147 |
148 | 149 |
150 | 160 |
161 |
162 |
163 |
164 |

Saved For Later

165 |

Loading insights...

166 |
167 | 168 |
169 |

User Insights

170 |

Loading insights...

171 |
172 |
173 |
174 | 175 | 176 |
191 | 192 |
193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | function togglePasswordVisibility() { 2 | const passwordInput = document.getElementById('loginPassword'); 3 | const togglePasswordIcon = document.getElementById('togglePasswordIcon'); 4 | if (passwordInput.type === 'password') { 5 | passwordInput.type = 'text'; 6 | togglePasswordIcon.classList.remove('bi-eye'); 7 | togglePasswordIcon.classList.add('bi-eye-slash'); 8 | } else { 9 | passwordInput.type = 'password'; 10 | togglePasswordIcon.classList.remove('bi-eye-slash'); 11 | togglePasswordIcon.classList.add('bi-eye'); 12 | } 13 | } 14 | 15 | function validateForm() { 16 | const email = document.getElementById('loginEmail'); 17 | const password = document.getElementById('loginPassword'); 18 | let valid = true; 19 | 20 | if (!email.value) { 21 | email.classList.add('is-invalid'); 22 | valid = false; 23 | } else { 24 | email.classList.remove('is-invalid'); 25 | } 26 | 27 | if (!password.value) { 28 | password.classList.add('is-invalid'); 29 | valid = false; 30 | } else { 31 | password.classList.remove('is-invalid'); 32 | } 33 | 34 | return valid; 35 | } 36 | 37 | function validateSignupForm() { 38 | const username = document.getElementById('signupUsername'); 39 | const email = document.getElementById('signupEmail'); 40 | const password = document.getElementById('signupPassword'); 41 | const confirmPassword = document.getElementById('signupConfirmPassword'); 42 | let valid = true; 43 | 44 | // Username validation (must be at least 3 characters) 45 | if (username.value.length < 3) { 46 | username.classList.add('is-invalid'); 47 | valid = false; 48 | } else { 49 | username.classList.remove('is-invalid'); 50 | } 51 | 52 | // Email validation (checks format) 53 | const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; 54 | if (!emailPattern.test(email.value)) { 55 | email.classList.add('is-invalid'); 56 | valid = false; 57 | } else { 58 | email.classList.remove('is-invalid'); 59 | } 60 | 61 | // Password validation (at least 6 characters and includes a number) 62 | const passwordPattern = /^(?=.*\d)(?=.*[a-zA-Z]).{6,}$/; 63 | if (!passwordPattern.test(password.value)) { 64 | password.classList.add('is-invalid'); 65 | valid = false; 66 | } else { 67 | password.classList.remove('is-invalid'); 68 | } 69 | 70 | // Confirm password validation (must match the password) 71 | if (password.value !== confirmPassword.value) { 72 | confirmPassword.classList.add('is-invalid'); 73 | valid = false; 74 | } else { 75 | confirmPassword.classList.remove('is-invalid'); 76 | } 77 | 78 | return valid; 79 | } 80 | 81 | function toggleSignupPasswordVisibility() { 82 | const passwordInput = document.getElementById('signupPassword'); 83 | const togglePasswordIcon = document.getElementById('toggleSignupPasswordIcon'); 84 | if (passwordInput.type === 'password') { 85 | passwordInput.type = 'text'; 86 | togglePasswordIcon.classList.remove('bi-eye'); 87 | togglePasswordIcon.classList.add('bi-eye-slash'); 88 | } else { 89 | passwordInput.type = 'password'; 90 | togglePasswordIcon.classList.remove('bi-eye-slash'); 91 | togglePasswordIcon.classList.add('bi-eye'); 92 | } 93 | } 94 | 95 | function toggleSignupConfirmPasswordVisibility() { 96 | const confirmPasswordInput = document.getElementById('signupConfirmPassword'); 97 | const toggleConfirmPasswordIcon = document.getElementById('toggleSignupConfirmPasswordIcon'); 98 | if (confirmPasswordInput.type === 'password') { 99 | confirmPasswordInput.type = 'text'; 100 | toggleConfirmPasswordIcon.classList.remove('bi-eye'); 101 | toggleConfirmPasswordIcon.classList.add('bi-eye-slash'); 102 | } else { 103 | confirmPasswordInput.type = 'password'; 104 | toggleConfirmPasswordIcon.classList.remove('bi-eye-slash'); 105 | toggleConfirmPasswordIcon.classList.add('bi-eye'); 106 | } 107 | } 108 | 109 | // Check if the user is logged in by verifying the presence of an auth token in localStorage 110 | if (localStorage.getItem("authToken")) { 111 | console.log("User logged in"); 112 | 113 | // Select all elements with the class 'authcomp' 114 | const authElements = document.getElementsByClassName("authcomp"); 115 | 116 | // Loop through the elements and change their opacity 117 | Array.from(authElements).forEach(element => { 118 | element.style.opacity = 0; // Set opacity to 0 (hidden) 119 | element.style.pointerEvents = "none"; // Prevent interaction 120 | }); 121 | } else { 122 | console.log("User not logged in"); 123 | } 124 | document.querySelector( 125 | ".copyright-text" 126 | ).textContent = `Copyright © ${new Date().getFullYear()} InsightSync. All rights reserved.`; 127 | 128 | 129 | document.getElementById("subscribe-form").addEventListener("submit", function(event) { 130 | event.preventDefault(); 131 | 132 | window.scrollTo({ top: 0, behavior: "smooth" }); // Scroll to top smoothly 133 | 134 | setTimeout(() => this.submit(), 100); 135 | event.preventDefault(); 136 | const email = document.getElementById("email").value; 137 | Toastify({ 138 | text: "SUCCESSFULLY SUBSCRIBED", 139 | duration: 200000, 140 | close: true, 141 | gravity: "top", 142 | position: 'right', 143 | background : "green", 144 | stopOnFocus: true, 145 | }).showToast(); 146 | }); 147 | 148 | console.log("modules") 149 | const logoutbtn = document.getElementById("signout") 150 | const loginbtn = document.getElementById("loginbtn") 151 | const signupbtn = document.getElementById("signupbtn") 152 | if (localStorage.getItem("authToken")) { 153 | loginbtn.style.display = "none" 154 | signupbtn.style.display= "none" 155 | }else{ 156 | 157 | logoutbtn.style.display = "none" 158 | } 159 | document.getElementById("logoutbtn2").addEventListener("click",()=>{ 160 | console.log("clickedbskjkbvsdhvbskdmbvduksvbsdmhcbjh") 161 | localStorage.clear() 162 | window.location.href="/" 163 | }) -------------------------------------------------------------------------------- /server/.env.sample: -------------------------------------------------------------------------------- 1 | MONGO_URI="mongo_db_url" 2 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /server/api/app.js: -------------------------------------------------------------------------------- 1 | 2 | import express from 'express'; 3 | import cors from 'cors'; 4 | import bodyParser from 'body-parser'; 5 | import authRoutes from '../routes/authRoutes.js'; 6 | import insightRouter from '../routes/insight.route.js'; 7 | import { profileRouter } from '../routes/profile.routes.js'; 8 | import contactRoutes from '../routes/contact.route.js'; 9 | import { commentRouter } from '../routes/comment.router.js'; 10 | 11 | const app = express(); 12 | 13 | // Middleware 14 | app.use(cors()); 15 | 16 | app.get("/",(req,res)=>{ 17 | res.send("express and mongodb") 18 | }) 19 | app.use(express.json({ limit: '16kb' })); 20 | app.use(express.urlencoded({ extended: true, limit: '16kb' })); 21 | app.use(bodyParser.json()); 22 | app.use(bodyParser.urlencoded({ extended: true })); 23 | // Handle 404 errors for all other routes 24 | // app.use((req, res, next) => { 25 | // if (req.method !== 'GET') { 26 | // return res.status(405).json({ error: 'Method Not Allowed' }); 27 | // } 28 | // res.status(404).sendFile(__dirname + "/404.html"); 29 | // }); 30 | 31 | 32 | // Routes 33 | app.use('/api/v1/auth', authRoutes); // Authentication routes 34 | app.use('/api/v1/insight', insightRouter); // Insight routes 35 | app.use("/api/v1/profile",profileRouter); 36 | app.use("/api/v1/contact", contactRoutes);//contactform route 37 | app.use("/api/v1/comment",commentRouter); 38 | 39 | 40 | export { app }; 41 | -------------------------------------------------------------------------------- /server/api/dbconnect.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import dotenv from 'dotenv'; 3 | 4 | dotenv.config(); 5 | // Database connection 6 | export const dbConnect = async () => { 7 | const url = process.env.MONGO_URI; 8 | 9 | if (!url) { 10 | console.error('No URL received from env. Check .env file path.'); 11 | process.exit(1); 12 | } 13 | 14 | try { 15 | await mongoose.connect(url, { 16 | useNewUrlParser: true, 17 | useUnifiedTopology: true, 18 | }); 19 | console.log('MongoDB connected'); 20 | } catch (err) { 21 | console.error('Database connection error:', err); 22 | process.exit(1); // Exit process with failure 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /server/api/index.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | import { dbConnect } from './dbconnect.js'; 3 | import { app } from './app.js'; 4 | 5 | // Load environment variables 6 | dotenv.config(); 7 | 8 | console.log('Starting app. MONGO_URI:', process.env.MONGO_URI); 9 | 10 | // Connect to the database and start the server 11 | dbConnect() 12 | .then(() => { 13 | const PORT = process.env.PORT || 3000; 14 | app.listen(PORT, () => { 15 | console.log(`App is running on port ${PORT} and DB connected`); 16 | }); 17 | }) 18 | .catch((err) => { 19 | console.error('Database connection failed:', err); 20 | }); 21 | -------------------------------------------------------------------------------- /server/controller/authController.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import User from '../models/User.js'; 3 | 4 | // User Registration 5 | export const registerUser = async (req, res) => { 6 | const { username, email, password, dob, location } = req.body; 7 | 8 | // Check for required fields 9 | if (!username || !email || !password) { 10 | return res.status(400).send({ error: "Username, email, and password are required." }); 11 | } 12 | 13 | try { 14 | // Check if the user already exists 15 | const userExists = await User.findOne({ email }); 16 | 17 | if (userExists) { 18 | return res.status(400).send({ message: 'User already exists' }); 19 | } 20 | 21 | // Create a new user 22 | const user = new User({ 23 | username, 24 | email, 25 | password, 26 | dob, // Optional field 27 | location, // Optional field 28 | }); 29 | 30 | await user.save(); 31 | console.log('User saved successfully'); 32 | 33 | // Generate JWT token 34 | const token = jwt.sign({ id: user._id }, "THIS_IS_A_JWT_SECRET", { 35 | expiresIn: '1h', 36 | }); 37 | 38 | // Respond with success message and token 39 | res.status(201).send({ 40 | message: 'User created successfully', 41 | token, 42 | userCreated: user._id 43 | }); 44 | } catch (error) { 45 | console.error('Error during user registration:', error); 46 | res.status(500).send({ message: 'Server error', error: error.message }); 47 | } 48 | }; 49 | 50 | // User Login 51 | export const loginUser = async (req, res) => { 52 | const { email, password } = req.body; 53 | 54 | try { 55 | const user = await User.findOne({ email }); 56 | 57 | if (!user) { 58 | console.log("User does not exist"); 59 | return res.status(400).send({ message: 'Invalid credentials user' }); 60 | } 61 | 62 | const isPasswordMatch = await user.matchPassword(password); 63 | 64 | if (!isPasswordMatch) { 65 | console.log("Password doesn't match"); 66 | return res.status(400).send({ message: 'Invalid credentials password' }); 67 | } 68 | 69 | const token = jwt.sign({ id: user._id }, "THIS_IS_A_JWT_SECRET", { 70 | expiresIn: '1h', 71 | }); 72 | 73 | res.send({ 74 | message: 'Login successful', 75 | token, 76 | loggedInUser:user._id 77 | }); 78 | } catch (error) { 79 | console.error('Error during user login:', error); 80 | res.status(500).send({ message: 'Server error', error: error.message }); 81 | } 82 | }; 83 | 84 | -------------------------------------------------------------------------------- /server/controller/comment.controller.js: -------------------------------------------------------------------------------- 1 | import User from "../models/User.js"; 2 | import { Comment } from "../models/comment.model.js"; 3 | import jwt from 'jsonwebtoken'; 4 | import { Insight } from "../models/insight.model.js"; 5 | 6 | const addComment = async (req, res) => { 7 | const {i_id, comment} = req.body; 8 | 9 | if (!i_id || !comment) { 10 | return res.status(400).json({ message: 'All fields are required' }); 11 | } 12 | 13 | try { 14 | const authheader = req.headers.authorization 15 | if (!authheader) { 16 | return res.status(401).send({ error: "auth headers not received" }) 17 | } 18 | 19 | const token = authheader.split(" ")[1]; 20 | 21 | if (!token) { 22 | console.error("Bearer token is missing."); 23 | return res.status(401).json({ error: "Invalid token format." }); 24 | } 25 | 26 | // Decode and Verify Token 27 | let decoded; 28 | try { 29 | decoded = jwt.verify(token, "THIS_IS_A_JWT_SECRET"); 30 | } catch (err) { 31 | console.error("Error decoding token:", err.message); 32 | return res.status(401).json({ error: "Invalid or expired token." }); 33 | } 34 | 35 | // Find the authenticated user 36 | const userId = decoded.id; 37 | const user = await User.findById(userId) 38 | 39 | if (!user) { 40 | return res.status(401).json({ error: "User not found." }); 41 | } 42 | 43 | const fetchedInsight = await Insight.findById(i_id) 44 | if (!fetchedInsight) { 45 | return res.status(400).json({ message: 'Insight not found' }); 46 | } 47 | 48 | const newComment = new Comment({ 49 | inisightId: i_id, 50 | userId: userId, 51 | comment: comment 52 | }) 53 | 54 | await newComment.save() 55 | 56 | user.comments.push(newComment._id) 57 | fetchedInsight.comments.push(newComment._id) 58 | await user.save() 59 | await fetchedInsight.save() 60 | res.status(201).json({ message: 'Comment added successfully' }); 61 | } catch (error) { 62 | console.error(error); 63 | res.status(500).json({ message: 'Server error' }); 64 | 65 | } 66 | } 67 | 68 | 69 | const getComments = async (req, res) => { 70 | try { 71 | const authheader = req.headers.authorization 72 | if (!authheader) { 73 | return res.status(401).send({ error: "auth headers not received" }) 74 | } 75 | 76 | const token = authheader.split(" ")[1]; 77 | 78 | if (!token) { 79 | console.error("Bearer token is missing."); 80 | return res.status(401).json({ error: "Invalid token format." }); 81 | } 82 | 83 | // Decode and Verify Token 84 | let decoded; 85 | try { 86 | decoded = jwt.verify(token, "THIS_IS_A_JWT_SECRET"); 87 | } catch (err) { 88 | console.error("Error decoding token:", err.message); 89 | return res.status(401).json({ error: "Invalid or expired token." }); 90 | } 91 | 92 | // Find the authenticated user 93 | const userId = decoded.id; 94 | const user = await User.findById(userId).populate({path:'comments',populate:{path:'inisightId userId'}}) 95 | 96 | if (!user) { 97 | return res.status(401).json({ error: "User not found." }); 98 | } 99 | 100 | return res.status(200).json({ message: 'Comment fetched successfully', comments: user.comments}); 101 | } catch (error) { 102 | console.error(error); 103 | return res.status(500).json({ message: 'server error' }); 104 | } 105 | } 106 | 107 | const getCommentsByInsight = async (req, res) => { 108 | console.log("gethit") 109 | const { id } = req.params; 110 | let i_id = id; 111 | if (!i_id) { 112 | return res.status(400).json({ message: 'Insight ID is required' }); 113 | } 114 | 115 | try { 116 | const fetchedInsight = await Insight.findById(i_id).populate({ path: 'comments', populate: { path: 'userId' } }); 117 | if (!fetchedInsight) { 118 | return res.status(400).json({ message: 'Insight not found' }); 119 | } 120 | 121 | return res.status(200).json({ message: 'Comments fetched successfully', comments: fetchedInsight.comments }); 122 | } catch (error) { 123 | console.error(error); 124 | return res.status(500).json({ message: 'Server error' }); 125 | } 126 | } 127 | 128 | const deleteComment = async (req, res) => { 129 | const { c_id } = req.body; 130 | if (!c_id) { 131 | return res.status(400).json({ message: 'All fields are required' }); 132 | } 133 | 134 | try { 135 | const response = await comment.findByIdAndDelete(c_id) 136 | if (!response) { 137 | return res.status(400).json({ message: 'Comment not found' }); 138 | } 139 | return res.status(200).json({ message: 'Comment deleted successfully' }); 140 | } catch (error) { 141 | console.error(error); 142 | return res.status(500).json({ message: 'server error' }); 143 | } 144 | } 145 | 146 | export { addComment, getComments, deleteComment, getCommentsByInsight }; -------------------------------------------------------------------------------- /server/controller/profile.controller.js: -------------------------------------------------------------------------------- 1 | import User from "../models/User.js"; 2 | import jwt from 'jsonwebtoken' 3 | import { upload_on_cloudinary } from "../utils/cloudinary.utils.js"; 4 | import { Insight } from "../models/insight.model.js"; 5 | 6 | // Get user profile function 7 | const getProfile = async (req, res) => { 8 | try { 9 | console.log("getProfile called"); 10 | 11 | // Step 1: Get the token from the authorization header 12 | const authHeader = req.headers.authorization; 13 | if (!authHeader) { 14 | // If the header is missing, return an error 15 | console.error("Authorization header is missing."); 16 | return res.status(401).json({ error: "No token provided." }); 17 | } 18 | 19 | // Step 2: Extract the token from the Authorization header 20 | const token = authHeader.split(' ')[1]; 21 | if (!token) { 22 | // If the token is missing, return an error 23 | console.error("Bearer token is missing."); 24 | return res.status(401).json({ error: "Invalid token format." }); 25 | } 26 | 27 | // Step 3: Verify the token 28 | const decoded = jwt.verify(token, "THIS_IS_A_JWT_SECRET"); 29 | 30 | // Step 4: Retrieve user information based on the decoded token's ID 31 | const user = await User.findById(decoded.id); 32 | if (!user) { 33 | // If no user is found, return an error 34 | console.error(`User not found for token with userId: ${decoded.userId}.`); 35 | return res.status(404).json({ error: "User not found." }); 36 | } 37 | 38 | // Step 5: Send back detailed user profile data 39 | res.json({ 40 | user 41 | }); 42 | 43 | console.log(`Profile fetched successfully for user ${user.username}.`); 44 | } catch (error) { 45 | console.error("Error during profile retrieval:", error); 46 | if (error.name === "JsonWebTokenError") { 47 | // Handle invalid JWT errors 48 | return res.status(401).json({ error: "Invalid token." }); 49 | } 50 | // Handle unexpected errors 51 | res.status(500).json({ error: "An error occurred while fetching the profile." }); 52 | } 53 | }; 54 | 55 | // Edit user profile function 56 | const editProfile = async (req, res) => { 57 | try { 58 | console.log("editProfile called"); 59 | 60 | // Step 1: Get the token from the authorization header 61 | const authHeader = req.headers.authorization; 62 | if (!authHeader) { 63 | // If the header is missing, return an error 64 | console.error("Authorization header is missing."); 65 | return res.status(401).json({ error: "No token provided." }); 66 | } 67 | 68 | // Step 2: Extract the token from the Authorization header 69 | const token = authHeader.split(' ')[1]; 70 | if (!token) { 71 | // If the token is missing, return an error 72 | console.error("Bearer token is missing."); 73 | return res.status(401).json({ error: "Invalid token format." }); 74 | } 75 | 76 | // Step 3: Verify the token 77 | const decoded = jwt.verify(token, "THIS_IS_A_JWT_SECRET"); 78 | 79 | // Step 4: Find the user by ID from the decoded token 80 | const user = await User.findById(decoded.id); 81 | if (!user) { 82 | // If no user is found, return an error 83 | console.error(`User not found for token with userId: ${decoded.id}.`); 84 | return res.status(404).json({ error: "User not found." }); 85 | } 86 | 87 | // Step 5: Update the user's profile fields if provided in the request body 88 | const { username, password, email, dob, location } = req.body; 89 | const filebuffer = req.file ? req.file.buffer : null; // Assuming file is available in req.file.buffer 90 | 91 | if (username) user.username = username; 92 | if (password) user.password = password; // Ensure to hash the password if implementing 93 | if (email) user.email = email; 94 | if (dob) user.dob = dob; 95 | if (location) user.location = location; 96 | if (filebuffer) { 97 | // Step 6: If an image is provided, upload it to Cloudinary 98 | const uploadedurl = await upload_on_cloudinary(filebuffer); 99 | user.image = uploadedurl; 100 | } 101 | 102 | // Step 7: Save the updated user information 103 | await user.save(); 104 | 105 | console.log(`Profile updated successfully for user ${user.username}.`); 106 | res.json({ message: "Profile updated successfully.", user }); 107 | } catch (error) { 108 | console.error("Error during profile update:", error); 109 | if (error.name === "JsonWebTokenError") { 110 | // Handle invalid JWT errors 111 | return res.status(401).json({ error: "Invalid token." }); 112 | } 113 | // Handle unexpected errors 114 | res.status(500).json({ error: "An error occurred while updating the profile." }); 115 | } 116 | }; 117 | 118 | // Delete user profile function 119 | const deleteProfile = async (req, res) => { 120 | try { 121 | // Step 1: Get the token from the authorization header 122 | const authHeader = req.headers.authorization; 123 | if (!authHeader) { 124 | // If the header is missing, return an error 125 | return res.status(401).json({ error: "No token provided." }); 126 | } 127 | 128 | // Step 2: Extract the token from the Authorization header 129 | const token = authHeader.split(' ')[1]; 130 | // Step 3: Verify the token 131 | const decoded = jwt.verify(token, "THIS_IS_A_JWT_SECRET"); 132 | 133 | // Step 4: Find the user by ID from the decoded token 134 | const user = await User.findById(decoded.id); 135 | if (!user) { 136 | // If no user is found, return an error 137 | return res.status(404).json({ error: "User not found." }); 138 | } 139 | 140 | // Step 5: Delete the user profile 141 | await user.deleteOne(); 142 | res.json({ message: "Profile deleted successfully." }); 143 | } catch (error) { 144 | // Handle unexpected errors 145 | res.status(500).json({ error: "An error occurred while deleting the profile." }); 146 | } 147 | }; 148 | 149 | // Get user insights function 150 | const getinsights = async (req, res) => { 151 | try { 152 | console.log("getInsights called"); 153 | 154 | // Step 1: Get the token from the authorization header 155 | const authHeader = req.headers.authorization; 156 | if (!authHeader) { 157 | // If the header is missing, return an error 158 | console.error("Authorization header is missing."); 159 | return res.status(401).json({ success: false, error: "Authorization header is missing." }); 160 | } 161 | 162 | // Step 2: Extract the token from the Authorization header 163 | const token = authHeader.split(' ')[1]; 164 | if (!token) { 165 | // If the token is missing, return an error 166 | console.error("Bearer token is missing."); 167 | return res.status(401).json({ success: false, error: "Invalid token format." }); 168 | } 169 | 170 | // Step 3: Verify the token 171 | let decoded; 172 | try { 173 | decoded = jwt.verify(token, "THIS_IS_A_JWT_SECRET"); 174 | } catch (error) { 175 | console.error("Invalid token:", error.message); 176 | return res.status(401).json({ success: false, error: "Invalid token." }); 177 | } 178 | 179 | // Step 4: Check if the decoded token contains a valid user ID 180 | if (!decoded || !decoded.id) { 181 | console.error("Token is missing a valid user ID."); 182 | return res.status(400).json({ success: false, error: "Malformed token." }); 183 | } 184 | 185 | // Step 5: Fetch the user using the decoded token's ID 186 | const user = await User.findById(decoded.id); 187 | console.log("this is profile get insight ", user) 188 | if (!user) { 189 | // If no user is found, return an error 190 | console.error(`User not found for token with user ID: ${decoded.id}.`); 191 | return res.status(404).json({ success: false, error: "User not found." }); 192 | } 193 | 194 | // Step 6: Safe check on user's insights count 195 | const totalInsights = user.inSightsCount || 0; 196 | 197 | // Step 7: Fetch insights for the user 198 | const insights = await Insight.find({ submittedby: user._id }); 199 | console.log(insights) 200 | if (!insights || insights.length === 0) { 201 | // If no insights are found, return a message 202 | console.warn(`No insights found for user ID: ${user._id}.`); 203 | return res.status(200).json({ success: true, totalInsights, insights: [] }); 204 | } 205 | 206 | // Step 8: Send successful response 207 | res.status(200).json({ 208 | success: true, 209 | totalInsights, 210 | insights, 211 | }); 212 | 213 | console.log(`Insights fetched successfully for user ${user.username}.`); 214 | } catch (error) { 215 | console.error("Error during getInsights operation:", error); 216 | // Handle unexpected errors 217 | res.status(500).json({ success: false, error: "An internal server error occurred." }); 218 | } 219 | }; 220 | 221 | //Get user public profile function 222 | 223 | const getPublicProfile = async (req, res) => { 224 | try { 225 | const { username } = req.params; 226 | 227 | // Find user by username and exclude sensitive fields like password, email, etc. 228 | const user = await User.findOne({_id: username}) 229 | 230 | if (!user) { 231 | return res.status(404).json({ message: "User not found" }); 232 | } 233 | 234 | res.status(200).json(user); 235 | } catch (error) { 236 | console.error(error); 237 | res.status(500).json({ message: "Server error" }); 238 | } 239 | }; 240 | 241 | 242 | 243 | 244 | // Export functions 245 | export {getProfile, editProfile, deleteProfile, getinsights, getPublicProfile} 246 | -------------------------------------------------------------------------------- /server/deplyment.md: -------------------------------------------------------------------------------- 1 | # If you want to deploy the Backend on Vercel 2 | 3 | If you want to deploy your backend to Vercel, follow these steps: 4 | 5 | 1. Access the Vercel Website 6 | Go to [Vercel](https://vercel.com/) and log in or sign up if you haven’t already. 7 | 8 | 2. Import the Repository 9 | After logging in, import the **Insightsync forked repository** from your GitHub account. 10 | 11 | 3. Configure Deployment Settings 12 | - In the **Root Directory** option, select the `server` folder. 13 | - Add environment variables by clicking on the **Environment Variables** section. 14 | 15 | 4. Add the Environment Variable 16 | Inside the Vercel project settings, add the following environment variable: 17 | ```bash 18 | MONGO_URI="your_mongodb_connection_url" 19 | ``` 20 | Make sure to replace `your_mongodb_connection_url` with your actual MongoDB URL. 21 | 22 | 5. Deploy the Server 23 | Click on the **Deploy** button to start the deployment process. After a few moments, your backend server will be deployed successfully! 24 | 25 | You can now test your deployed API by checking the provided Vercel URL. 26 | 27 | -------------------------------------------------------------------------------- /server/middleware/auth.middleware.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | 3 | const authUser = async (req, res, next) => { 4 | try { 5 | // Extract the Authorization header 6 | const authHeader = req.headers.authorization; // Note: Headers are case-insensitive 7 | 8 | if (!authHeader) { 9 | return res.status(401).json({ error: "No token found. Please log in first." }); 10 | } 11 | 12 | // Split the Bearer token 13 | const token = authHeader.split(' ')[1]; // Assumes format: "Bearer token" 14 | 15 | if (!token) { 16 | return res.status(401).json({ error: "Malformed token. Authorization failed." }); 17 | } 18 | 19 | // Verify the token 20 | const decoded = jwt.verify(token, "THIS_IS_A_JWT_SECRET"); // Replace with your secret key 21 | 22 | // Add user data to request for downstream handlers 23 | req.user = decoded; 24 | 25 | // Pass control to the next middleware or route handler 26 | next(); 27 | } catch (error) { 28 | console.error("Error verifying token:", error.message); 29 | 30 | // Handle specific JWT errors 31 | if (error.name === 'TokenExpiredError') { 32 | return res.status(401).json({ error: "Token expired. Please log in again." }); 33 | } else if (error.name === 'JsonWebTokenError') { 34 | return res.status(401).json({ error: "Invalid token. Authorization failed." }); 35 | } 36 | 37 | // Catch-all for unexpected errors 38 | res.status(500).json({ error: "Internal server error during authentication." }); 39 | } 40 | }; 41 | 42 | export {authUser}; 43 | -------------------------------------------------------------------------------- /server/models/User.js: -------------------------------------------------------------------------------- 1 | // const mongoose = require('mongoose'); 2 | // const bcrypt = require('bcryptjs'); 3 | 4 | // // User schema 5 | // const userSchema = new mongoose.Schema({ 6 | // username: { 7 | // type: String, 8 | // required: true, 9 | // unique: true, 10 | // }, 11 | // email: { 12 | // type: String, 13 | // required: true, 14 | // unique: true, 15 | // }, 16 | // password: { 17 | // type: String, 18 | // required: true, 19 | // }, 20 | // }); 21 | 22 | // userSchema.pre('save', async function(next) { 23 | // if (!this.isModified('password')) { 24 | // return next(); 25 | // } 26 | // const salt = await bcrypt.genSalt(10); 27 | // this.password = await bcrypt.hash(this.password, salt); 28 | // next(); 29 | // }); 30 | 31 | // // Method to compare password for login 32 | // userSchema.methods.matchPassword = async function (enteredPassword) { 33 | // return await bcrypt.compare(enteredPassword, this.password); 34 | // }; 35 | 36 | // const User = mongoose.model('User', userSchema); 37 | 38 | // module.exports = User; 39 | 40 | import mongoose from 'mongoose'; 41 | import bcrypt from 'bcryptjs'; 42 | 43 | // User schema 44 | const userSchema = new mongoose.Schema({ 45 | username: { 46 | type: String, 47 | required: true, 48 | unique: true, 49 | }, 50 | email: { 51 | type: String, 52 | required: true, 53 | unique: true, 54 | }, 55 | password: { 56 | type: String, 57 | required: true, 58 | }, 59 | inSightsCount: { 60 | type: Number, 61 | default: 0, 62 | }, 63 | createdOn: { 64 | type: Date, 65 | default: Date.now, // Automatically set to the current date and time 66 | }, 67 | dob: { 68 | type: Date, // Date of Birth field 69 | required: false, // Optional field 70 | }, 71 | location: { 72 | type: String, // Can store city, state, or country 73 | required: false, // Optional field 74 | }, 75 | image:{ 76 | type:String 77 | }, 78 | likedtopics:[{type: mongoose.Schema.Types.ObjectId, ref:'insightmodel'}], 79 | saveforlaterTopics:[{type: mongoose.Schema.Types.ObjectId , ref:'insightmodel'}], 80 | comments:[{type: mongoose.Schema.Types.ObjectId , ref:'Comment'}] 81 | }, {timestamps: true}); 82 | 83 | // Hash password before saving 84 | userSchema.pre('save', async function (next) { 85 | if (!this.isModified('password')) { 86 | return next(); 87 | } 88 | const salt = await bcrypt.genSalt(10); 89 | this.password = await bcrypt.hash(this.password, salt); 90 | next(); 91 | }); 92 | 93 | // Method to compare password for login 94 | userSchema.methods.matchPassword = async function (enteredPassword) { 95 | return await bcrypt.compare(enteredPassword, this.password); 96 | }; 97 | 98 | const User = mongoose.model('User', userSchema); 99 | 100 | export default User; 101 | -------------------------------------------------------------------------------- /server/models/comment.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const commentSchema = new mongoose.Schema({ 4 | inisightId:{ 5 | type:mongoose.Schema.Types.ObjectId, 6 | ref:'insightmodel' 7 | }, 8 | userId:{ 9 | type:mongoose.Schema.Types.ObjectId, 10 | ref:'User' 11 | }, 12 | comment:{ 13 | type:String, 14 | required:true 15 | } 16 | }, {timestamps:true}) 17 | 18 | 19 | const Comment = mongoose.model('Comment',commentSchema) 20 | 21 | export {Comment} -------------------------------------------------------------------------------- /server/models/contact.js: -------------------------------------------------------------------------------- 1 | 2 | import mongoose from 'mongoose'; 3 | 4 | 5 | const contactSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | required: true, 9 | trim: true, 10 | }, 11 | email: { 12 | type: String, 13 | required: true, 14 | match: [/.+\@.+\..+/, 'Please enter a valid email address'], 15 | }, 16 | subject: { 17 | type: String, 18 | required: true, 19 | trim: true, 20 | }, 21 | message: { 22 | type: String, 23 | required: true, 24 | trim: true, 25 | }, 26 | createdAt: { 27 | type: Date, 28 | default: Date.now, 29 | }, 30 | }); 31 | 32 | 33 | const Contact = mongoose.model('Contact', contactSchema); 34 | 35 | export default Contact; -------------------------------------------------------------------------------- /server/models/insight.model.js: -------------------------------------------------------------------------------- 1 | import { Schema } from "mongoose"; 2 | import mongoose from "mongoose"; 3 | 4 | const insightSchema = new Schema( 5 | { 6 | title:{ 7 | type:String, 8 | required: true 9 | }, 10 | topic:{ 11 | type:String, 12 | required: true 13 | }, 14 | content:{ 15 | type:String, 16 | required: true 17 | }, 18 | submittedby:{ type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, 19 | submittedbyName:{type:String}, 20 | Image:{ 21 | type:String 22 | }, 23 | likes:{ 24 | type:Number, 25 | default:0 26 | }, 27 | likedBy:[{type: mongoose.Schema.Types.ObjectId , ref:'User'}], 28 | comments:[{type: mongoose.Schema.Types.ObjectId , ref:'Comment'}] 29 | }, 30 | { 31 | timestamps: true 32 | } 33 | ) 34 | 35 | export const Insight = mongoose.model("insightmodel",insightSchema) -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "type": "module", 12 | "description": "", 13 | "dependencies": { 14 | "bcryptjs": "^2.4.3", 15 | "body-parser": "^1.20.3", 16 | "cloudinary": "^2.5.1", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.4.7", 19 | "express": "^4.21.2", 20 | "express-validator": "^7.2.1", 21 | "jsonwebtoken": "^9.0.2", 22 | "mongoose": "^8.9.3", 23 | "multer": "^1.4.5-lts.1", 24 | "streamifier": "^0.1.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/routes/authRoutes.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { registerUser, loginUser } from '../controller/authController.js'; 3 | const router = express.Router(); 4 | 5 | // Register Route 6 | router.post('/signup', registerUser); 7 | 8 | // Login Route 9 | router.post('/login', loginUser); 10 | 11 | export default router; 12 | -------------------------------------------------------------------------------- /server/routes/comment.router.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { addComment, deleteComment, getComments, getCommentsByInsight } from '../controller/comment.controller.js' 3 | 4 | 5 | const commentRouter = express.Router() 6 | 7 | 8 | commentRouter.post('/addComment',addComment ) 9 | commentRouter.get('/getComments', getComments ) 10 | commentRouter.delete('/deleteComment',deleteComment ) 11 | commentRouter.get('/getcommentsbyinsight/:id', getCommentsByInsight ) 12 | 13 | 14 | export {commentRouter} 15 | -------------------------------------------------------------------------------- /server/routes/contact.route.js: -------------------------------------------------------------------------------- 1 | // // server/routes/contact.routes.js 2 | // import express from 'express'; 3 | // import contact from '../models/contact.js'; 4 | // const router = express.Router(); 5 | 6 | // // Handle the POST request from the contact form 7 | // router.post('/submit', async (req, res) => { 8 | // const { name, email, subject, message } = req.body; 9 | 10 | // // Backend validation 11 | // if (!name || !email || !subject || !message) { 12 | // return res.status(400).json({ error: 'All fields are required' }); 13 | // } 14 | 15 | // // Create a new contact document 16 | // const newContact = new contact({ 17 | // name, 18 | // email, 19 | // subject, 20 | // message, 21 | // }); 22 | 23 | // try { 24 | // // Save the contact data to MongoDB 25 | // await newContact.save(); 26 | // res.status(200).json({ message: 'Form submitted successfully!' }); 27 | // } catch (err) { 28 | // console.error('Error saving data:', err); 29 | // res.status(500).json({ error: 'Failed to save contact data' }); 30 | // } 31 | // }); 32 | 33 | // export default router; 34 | import express from 'express'; 35 | import Contact from '../models/contact.js'; 36 | 37 | const router = express.Router(); 38 | 39 | // POST route to handle form submissions 40 | router.post('/', async (req, res) => { 41 | try { 42 | const { name, email, subject, message } = req.body; 43 | 44 | 45 | if (!name || !email || !subject || !message) { 46 | return res.status(400).json({ message: 'All fields are required.' }); 47 | } 48 | 49 | 50 | const contact = new Contact({ name, email, subject, message }); 51 | await contact.save(); 52 | 53 | 54 | res.status(200).json({ message: 'Form saved successfully' }); 55 | } catch (error) { 56 | res.status(500).json({ message: 'Failed to send form', error: error.message }); 57 | } 58 | }); 59 | 60 | export default router; 61 | -------------------------------------------------------------------------------- /server/routes/insight.route.js: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { addInsight, deleteinsight, editinsight, getallInsight, getinsightbyid, getinsightbytopic, getinsightbyUser, getSaveForLater, likeinsight, saveForLater } from "../controller/insight.controller.js"; 3 | import multer from 'multer' 4 | import { upload_on_cloudinary } from "../utils/cloudinary.utils.js"; 5 | import { authUser } from "../middleware/auth.middleware.js"; 6 | 7 | // Multer Storage Configuration 8 | const storage = multer.diskStorage({ 9 | destination: function (req, file, cb) { 10 | cb(null, '/tmp'); // Use relative path for portability 11 | }, 12 | filename: function (req, file, cb) { 13 | const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); 14 | cb(null, file.fieldname + '-' + uniqueSuffix); 15 | } 16 | }); 17 | 18 | // const upload = multer({storage:storage}); 19 | const upload = multer({ storage: multer.memoryStorage() }); 20 | const router = Router() 21 | 22 | router.route('/addinsight').post( upload.single('imagefile'), authUser, addInsight) 23 | // router.post("/addinsight", upload.single("imagefile"), async (req, res) => { 24 | // try { 25 | // const fileBuffer = req.file.buffer; // Get file buffer from Multer 26 | // const imageUrl = await upload_on_cloudinary(fileBuffer); // Upload image 27 | // res.status(200).json({ success: true, imageUrl: imageUrl }); 28 | // } catch (error) { 29 | // console.error("Upload error:", error); 30 | // res.status(500).json({ success: false, error: "Image upload failed" }); 31 | // } 32 | // }); 33 | router.route('/getallinsight').get(getallInsight) 34 | router.route("/getinsightbytopic").post(getinsightbytopic) 35 | router.route("/getinsightbyid").post(getinsightbyid) 36 | router.route("/getinsightbyuser").get(getinsightbyUser) 37 | router.route("/editinsight").post(upload.single('image'), editinsight) 38 | router.route("/deleteinsight").delete(authUser ,deleteinsight) 39 | 40 | //for liking insight 41 | router.route('/like/:insightId').post(likeinsight) 42 | 43 | //for save for later 44 | router.route('/saveforlater').post(saveForLater) 45 | router.route('/getsaveforlater').get(getSaveForLater) 46 | export default router -------------------------------------------------------------------------------- /server/routes/profile.routes.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { deleteProfile, editProfile, getinsights, getProfile,getPublicProfile } from '../controller/profile.controller.js' 3 | import multer from 'multer' 4 | 5 | const upload = multer({storage:multer.memoryStorage()}) 6 | const profileRouter = express.Router() 7 | 8 | profileRouter.route('/getprofile').get(getProfile) 9 | profileRouter.route('/editprofile').post( upload.single('image') ,editProfile) 10 | profileRouter.route('/deleteprofile').delete(deleteProfile) 11 | profileRouter.route('/getinsights').get(getinsights) 12 | profileRouter.route('/getpublicprofile/:username').get(getPublicProfile) 13 | 14 | export {profileRouter} -------------------------------------------------------------------------------- /server/utils/cloudinary.utils.js: -------------------------------------------------------------------------------- 1 | // import {v2 as cloudinary} from 'cloudinary' 2 | 3 | 4 | // const upload_on_cloudinary = async (filepath) => { 5 | // // Configuration 6 | // cloudinary.config({ 7 | // cloud_name: 'djfhwhtyy', 8 | // api_key: '944476654513192', 9 | // api_secret: 'AbbLeYlaOpNfB1lHWeJACHmJGlg' // Click 'View API Keys' above to copy your API secret 10 | // }); 11 | 12 | // if (!filepath) { 13 | // console.log("file path is not upadating ") 14 | // return null 15 | // } 16 | 17 | // const upload_res = await cloudinary.uploader.upload(filepath) 18 | // return upload_res 19 | 20 | // } 21 | 22 | // export {upload_on_cloudinary} 23 | 24 | // import { v2 as cloudinary } from "cloudinary"; 25 | 26 | // // Cloudinary configuration 27 | // cloudinary.config({ 28 | // cloud_name: "your_cloud_name", 29 | // api_key: "your_api_key", 30 | // api_secret: "your_api_secret", 31 | // secure: true, 32 | // }); 33 | 34 | // const uploadToCloudinary = async (file) => { 35 | // try { 36 | // if (!file || !(file instanceof File || file instanceof Blob)) { 37 | // throw new Error("Invalid file. Ensure you provide a valid File or Blob object."); 38 | // } 39 | 40 | // // Read file as a base64 string 41 | // const base64Data = await new Promise((resolve, reject) => { 42 | // const reader = new FileReader(); 43 | // reader.onload = () => resolve(reader.result.split(",")[1]); // Exclude `data:mime;base64,` 44 | // reader.onerror = (err) => reject(err); 45 | // reader.readAsDataURL(file); 46 | // }); 47 | 48 | // // Generate Data URI for Cloudinary 49 | // const mime = file.type; 50 | // const fileUri = `data:${mime};base64,${base64Data}`; 51 | 52 | // // Upload to Cloudinary 53 | // const result = await cloudinary.uploader.upload(fileUri, { invalidate: true }); 54 | 55 | // // Return secure URL 56 | // return result.secure_url; 57 | // } catch (error) { 58 | // console.error("Error uploading to Cloudinary:", error); 59 | // throw error; 60 | // } 61 | // }; 62 | 63 | // export { uploadToCloudinary }; 64 | 65 | // const { v2: cloudinary } = require('cloudinary'); 66 | import {v2 as cloudinary} from 'cloudinary' 67 | import streamifier from 'streamifier' 68 | // const streamifier = require('streamifier'); 69 | 70 | // Cloudinary configuration 71 | cloudinary.config({ 72 | cloud_name: 'djfhwhtyy', 73 | api_key: '944476654513192', 74 | api_secret: 'AbbLeYlaOpNfB1lHWeJACHmJGlg', 75 | secure: true, 76 | }); 77 | 78 | const upload_on_cloudinary = async (fileBuffer, folderName = "demo") => { 79 | try { 80 | if (!fileBuffer) { 81 | console.log("No file buffer provided"); 82 | return null; 83 | } 84 | 85 | return new Promise((resolve, reject) => { 86 | const stream = cloudinary.uploader.upload_stream( 87 | { 88 | folder: folderName, // Optional folder in Cloudinary 89 | }, 90 | (error, result) => { 91 | if (error) { 92 | console.error("Cloudinary upload error:", error); 93 | reject(error); 94 | } else { 95 | resolve(result.secure_url); // Return the secure URL of the uploaded image 96 | } 97 | } 98 | ); 99 | 100 | // Pipe the file buffer to Cloudinary's upload stream 101 | streamifier.createReadStream(fileBuffer).pipe(stream); 102 | }); 103 | } catch (error) { 104 | console.error("Error during Cloudinary upload:", error); 105 | throw error; 106 | } 107 | }; 108 | 109 | export { upload_on_cloudinary }; 110 | -------------------------------------------------------------------------------- /server/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "api/index.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "api/index.js", 13 | "methods": [ 14 | "GET", 15 | "POST", 16 | "PUT", 17 | "DELETE", 18 | "PATCH", 19 | "OPTIONS" 20 | ] 21 | }, 22 | { 23 | "src": "/api/(.*)", 24 | "headers": { 25 | "Access-Control-Allow-Credentials": "true", 26 | "Access-Control-Allow-Origin": "*", 27 | "Access-Control-Allow-Methods": "GET,OPTIONS,PATCH,DELETE,POST,PUT", 28 | "Access-Control-Allow-Headers": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /summary.js: -------------------------------------------------------------------------------- 1 | const GEMINI_API_KEY = "your_api_key"; // Replace with your actual API key 2 | 3 | // Function to send article text to Gemini 1.5 Pro API for summarization 4 | async function getAISummary(text) { 5 | try { 6 | const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro:generateContent?key=${GEMINI_API_KEY}`, { 7 | method: "POST", 8 | headers: { 9 | "Content-Type": "application/json" 10 | }, 11 | body: JSON.stringify({ 12 | contents: [{ role: "user", parts: [{ text: `Summarize this article in a few sentences: ${text}` }] }] 13 | }) 14 | }); 15 | 16 | const data = await response.json(); 17 | return data.candidates?.[0]?.content?.parts?.[0]?.text || "Summary not available."; 18 | } catch (error) { 19 | console.error("Error generating summary:", error); 20 | return "Failed to generate summary."; 21 | } 22 | } 23 | 24 | 25 | async function summarizeArticle() { 26 | const articleContent = document.getElementById("topic-content").innerText.trim(); 27 | 28 | if (!articleContent) { 29 | document.getElementById("summary-text").innerText = "Hit on the generate summary button."; 30 | return; 31 | } 32 | 33 | document.getElementById("summary-text").innerText = "Generating summary..."; 34 | const summary = await getAISummary(articleContent); 35 | document.getElementById("summary-text").innerText = summary; 36 | } 37 | 38 | 39 | document.addEventListener("DOMContentLoaded", summarizeArticle); 40 | -------------------------------------------------------------------------------- /testimonial.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap'); 2 | 3 | :root{ 4 | --primary-color: #80d0c7; 5 | --secondary-color: #f0f8ff; 6 | --text-dark: #b9a3a3; 7 | --text-light: #94a3b8; 8 | --white: #ffffff; 9 | --section-bg-color: #80d0c7; /* Added missing variable */ 10 | } 11 | 12 | *{ 13 | padding: 0; 14 | margin: 0; 15 | box-sizing: border-box; 16 | } 17 | 18 | body{ 19 | font-family: "Poppins", sans-serif; 20 | background-color: var(--section-bg-color); 21 | } 22 | 23 | .section_container{ 24 | max-width: 1200px; 25 | margin: auto; 26 | padding: 20px; 27 | text-align: center; 28 | animation: fadeIn 1s ease-in-out; 29 | } 30 | 31 | .section_container h2{ 32 | font-size: 1.5rem; 33 | font-weight: 600; 34 | color: var(--secondary-color); 35 | animation: fadeIn 1.5s ease-in-out; 36 | } 37 | 38 | .section_container h1{ 39 | position: relative; 40 | margin-bottom: 2rem; 41 | font-size: 2.5rem; 42 | font-weight: 600; 43 | color: black; 44 | animation: fadeIn 2s ease-in-out; 45 | } 46 | 47 | .section_container h1::after{ 48 | position: absolute; 49 | content: ""; 50 | left: 50%; 51 | bottom: 0; 52 | transform: translateX(-50%); 53 | height: 2px; 54 | width: 5rem; 55 | background-color: var(--section-bg-color); 56 | animation: slideIn 1s ease-in-out; 57 | } 58 | 59 | .section_grid{ 60 | display: grid; 61 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); 62 | gap: 20px; 63 | animation: fadeIn 2.5s ease-in-out; 64 | } 65 | 66 | .section_card{ 67 | position: relative; 68 | isolation: isolate; 69 | overflow: hidden; 70 | padding: 5rem 2rem 2rem; 71 | background-color: var(--white); 72 | border-radius: 8px; 73 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 74 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); 75 | animation: float 3s ease-in-out infinite; 76 | } 77 | 78 | .section_card::before{ 79 | position: absolute; 80 | content: ""; 81 | top: 0; 82 | left: 0; 83 | transform: translate(-50%, -50%); 84 | width: 55%; 85 | aspect-ratio: 1; 86 | border-radius: 100%; 87 | background-color: var(--secondary-color); 88 | z-index: -1; 89 | transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); 90 | } 91 | 92 | .section_card span{ 93 | position: absolute; 94 | top: 0; 95 | left: 0; 96 | padding: 1rem; 97 | font-size: 3rem; 98 | color: var(--white); 99 | animation: fadeIn 1s ease-in-out; 100 | } 101 | 102 | .section_card h4{ 103 | margin-top: 4rem; 104 | margin-bottom: 1rem; 105 | font-size: 1.5rem; 106 | font-weight: 600; 107 | color: var(--secondary-color); 108 | transition: all 0.3s ease; 109 | } 110 | 111 | .section_card p{ 112 | margin-bottom: 2rem; 113 | color: var(--text-light); 114 | transition: all 0.3s ease; 115 | } 116 | 117 | .section_card img{ 118 | display: block; 119 | margin: 0 auto 1rem; 120 | width: 100px; 121 | height: 100px; 122 | object-fit: cover; 123 | border-radius: 50%; 124 | border: 2px solid var(--secondary-color); 125 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 126 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); 127 | } 128 | 129 | .section_card h5{ 130 | font-size: 1.25rem; 131 | font-weight: 600; 132 | color: var(--secondary-color); 133 | transition: all 0.3s ease; 134 | } 135 | 136 | .section_card h6{ 137 | font-size: 1rem; 138 | font-weight: 400; 139 | color: var(--text-light); 140 | transition: all 0.3s ease; 141 | } 142 | 143 | .section_card:hover { 144 | transform: translateY(-5px); 145 | box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2); 146 | } 147 | 148 | .section_card:hover::before{ 149 | width: 480%; 150 | } 151 | 152 | .section_card:hover :is(h4, h5){ 153 | color: var(--white); 154 | text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 155 | } 156 | 157 | .section_card:hover :is(p, h6){ 158 | color: var(--section-bg-color); 159 | } 160 | 161 | .section_card:hover img{ 162 | border-color: var(--white); 163 | box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25); 164 | transform: scale(1.08); 165 | } 166 | 167 | @keyframes fadeIn { 168 | 0% { opacity: 0; } 169 | 100% { opacity: 1; } 170 | } 171 | 172 | @keyframes slideIn { 173 | 0% { width: 0; } 174 | 100% { width: 5rem; } 175 | } 176 | 177 | @keyframes float { 178 | 0%, 100% { transform: translateY(0); } 179 | 50% { transform: translateY(-10px); } 180 | } 181 | 182 | .section_container { 183 | padding: 20px; 184 | } 185 | 186 | .section_grid { 187 | display: grid; 188 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); 189 | gap: 20px; 190 | } 191 | 192 | .section_card { 193 | background: #fff; 194 | border-radius: 8px; 195 | padding: 20px; 196 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 197 | text-align: center; 198 | display: flex; 199 | flex-direction: column; 200 | justify-content: space-between; 201 | } 202 | 203 | .section_card img { 204 | display: block; 205 | margin: 0 auto; 206 | border-radius: 50%; 207 | width: 100px; 208 | height: 100px; 209 | object-fit: cover; 210 | object-position: top; 211 | border: 2px solid var(--secondary-color); 212 | transition: border-color 0.3s ease; 213 | } 214 | 215 | @media (max-width: 768px) { 216 | .section_container { 217 | padding: 10px; 218 | } 219 | 220 | .section_grid { 221 | grid-template-columns: 1fr; 222 | } 223 | 224 | .section_card { 225 | padding: 15px; 226 | } 227 | 228 | .section_card img { 229 | width: 100px; 230 | height: 100px; 231 | } 232 | } 233 | 234 | 235 | @media (max-width: 768px) { 236 | .section_container { 237 | padding: 10px; 238 | } 239 | 240 | .section_grid { 241 | grid-template-columns: 1fr; 242 | } 243 | 244 | .section_card { 245 | padding: 15px; 246 | } 247 | 248 | .section_card img { 249 | width: 90px; 250 | height: 90px; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /trending topics_and_footer.txt: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | 6 |
7 |

Trending Topics

8 |
9 | 10 | 28 | 29 |
30 |
31 |
32 | 34 | 35 |
36 |
37 |
Finance
38 | 39 |

Lorem ipsum dolor, sit amet consectetur adipisicing elit. 40 | Sint animi necessitatibus aperiam repudiandae nam omnis

41 | 42 | Learn More 43 |
44 | 45 | 25 46 |
47 | 48 | 67 | 68 |
69 |
70 |
71 |
72 | 73 |
74 |
75 |
76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | --------------------------------------------------------------------------------