├── .env.sample ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── documentation_update.yml │ └── feature_request.yml ├── Pull_Request_Template.md └── workflows │ ├── auto-label.yml │ ├── autocomment-iss-close.yml │ ├── autocomment-pr-merge.yml │ ├── autocomment-pr-raise.yml │ ├── close-old-issue.yml │ ├── close-old-pr.yml │ └── greetings.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── PROJECT_DETAILS.md ├── README.md ├── client ├── .env.sample ├── .gitignore ├── Dockerfile ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── 404.html │ ├── action.html │ ├── books.html │ ├── cart.webp │ ├── error.png │ ├── favicon.ico │ ├── fiction.html │ ├── google.png │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manga.html │ ├── manifest.json │ ├── philosophy.html │ ├── preload.mp4 │ ├── quiz.html │ ├── robots.txt │ ├── script.js │ ├── styles.css │ ├── tech.html │ └── thriller.html ├── src │ ├── Animations.css │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── Context │ │ ├── AuthContext.js │ │ └── ToastContext.js │ ├── Lottie-animation │ │ └── loginAnimation.json │ ├── Pages │ │ ├── AboutUs.jsx │ │ ├── ActionPage.jsx │ │ ├── BannerCard.css │ │ ├── BannerCard.js │ │ ├── Book.jsx │ │ ├── CancelReturns.jsx │ │ ├── Careers.jsx │ │ ├── Cart.css │ │ ├── Cart.jsx │ │ ├── Contact.jsx │ │ ├── Contributors.jsx │ │ ├── CustomerProfile.jsx │ │ ├── Faq.jsx │ │ ├── FictionPage.jsx │ │ ├── GiftCards.jsx │ │ ├── Home.jsx │ │ ├── Licensing.jsx │ │ ├── LoginPage.jsx │ │ ├── MangaPage.jsx │ │ ├── Newarrivals.js │ │ ├── NotFound.jsx │ │ ├── Orders.jsx │ │ ├── PhilosophyPage.jsx │ │ ├── PrivacyPolicy.jsx │ │ ├── ProductItem.js │ │ ├── Profile.jsx │ │ ├── ResetPassword.jsx │ │ ├── Review.jsx │ │ ├── RomancePage.jsx │ │ ├── Shop.jsx │ │ ├── SignUpPage.jsx │ │ ├── Spinner.js │ │ ├── Spinner.jsx │ │ ├── TechPage.jsx │ │ ├── Terms.jsx │ │ ├── ThrillerPage.jsx │ │ ├── Wishlist.jsx │ │ └── preLoader.gif │ ├── Toast │ │ └── Toast.js │ ├── api │ │ └── api.js │ ├── assets │ │ ├── Giftcard.jpg │ │ ├── Logo.png │ │ ├── google_icon.jpg │ │ ├── image │ │ │ ├── B1Child.jpeg │ │ │ ├── Dune.jpg │ │ │ ├── WhereTheCrawdadsSings.jpg │ │ │ ├── author1.jpeg │ │ │ ├── contactus.jpg │ │ │ ├── klaraAndtheSun.jpg │ │ │ ├── malibuRising.jpg │ │ │ ├── profile.jpg │ │ │ ├── projectHailMary.jpg │ │ │ ├── theMidnightLibrary.jpg │ │ │ └── theVanishingHalf.jpg │ │ ├── moon.png │ │ └── sun.png │ ├── chatbot.jsx │ ├── components │ │ ├── AI-BookRecommdation │ │ │ ├── .gitignore │ │ │ ├── PREREQUISITES.md │ │ │ ├── README.md │ │ │ ├── main.py │ │ │ ├── requirements.txt │ │ │ └── sample.config.toml │ │ ├── Card │ │ │ ├── Book.jsx │ │ │ ├── ProductCard.css │ │ │ └── ProductCard.jsx │ │ ├── CustomerProfile │ │ │ ├── CorporateRequirements.jsx │ │ │ ├── DeliveryLocation.jsx │ │ │ ├── Giftcard.jsx │ │ │ ├── Orders.jsx │ │ │ ├── Pancard.jsx │ │ │ ├── Profile.jsx │ │ │ └── Sidebar.jsx │ │ ├── Footer │ │ │ └── Footer.jsx │ │ ├── GoToTop.jsx │ │ ├── GoogleLogin.jsx │ │ ├── GoogleTranslate.jsx │ │ ├── Header │ │ │ └── Header.js │ │ ├── HomePage.js │ │ ├── NavBar │ │ │ ├── NavBar.jsx │ │ │ └── ScrollProgressBar.jsx │ │ ├── Preloader.jsx │ │ ├── Product.jsx │ │ ├── Profile │ │ │ └── ProfilePage.js │ │ ├── SearchBar.js │ │ ├── Trending.jsx │ │ ├── index.js │ │ └── temp.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ ├── server.js │ ├── setupTests.js │ └── validations │ │ └── validation.js └── tailwind.config.js ├── controllers ├── adminController.js ├── cartController.js ├── customerController.js ├── mailcontroller.js ├── orderController.js └── productController.js ├── cover-page.png ├── docker-compose.yml ├── index.js ├── middlewares ├── auth.js ├── catchAsyncErrors.js ├── error.js └── models │ ├── cartSchema.js │ ├── customerSchema.js │ ├── feebackSchema.js │ ├── orderSchema.js │ ├── productSchema.js │ └── wishlistSchema.js ├── package-lock.json ├── package.json ├── routes ├── adminRoutes.js ├── cartRoutes.js ├── customerRoutes.js ├── orderRoutes.js ├── productRoutes.js └── wishlistRoutes.js └── utils ├── errorHandler.js ├── jwtToken.js └── responseHandler.js /.env.sample: -------------------------------------------------------------------------------- 1 | MONGO_URL=mongodb://localhost:27017 2 | PORT=8080 3 | REFRESH_TOKEN_COOKIE_EXPIRE=30 4 | REFRESH_TOKEN_SECRET=XYZ 5 | GOOGLE_CLIENT_ID=your_client_id 6 | GOOGLE_CLIENT_SECRET=your_client_secret 7 | JWT_SECRET=yourSecretKey 8 | SMTP_EMAIL=your-email@gmail.com 9 | SMTP_PASSWORD=your-email-password 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 🐞 2 | description: File a bug report 3 | title: "[Bug]: " 4 | body: 5 | - type: checkboxes 6 | id: existing-issue 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for the bug you encountered. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: textarea 14 | id: what-happened 15 | attributes: 16 | label: Describe the bug 17 | description: A concise description of what you are experiencing. 18 | placeholder: Tell us what you see! 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: expected-behaviour 23 | attributes: 24 | label: Expected behavior 25 | description: A clear and concise description of what you expected to happen. 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: screenshots 30 | attributes: 31 | label: Add ScreenShots 32 | description: Add sufficient ScreenShots to explain your issue. 33 | - type: dropdown 34 | id: devices 35 | attributes: 36 | label: On which device are you experiencing this bug? 37 | multiple: true 38 | options: 39 | - Android 40 | - iPhone 41 | - Linux 42 | - Chrome 43 | - Windows 44 | - type: checkboxes 45 | id: terms 46 | attributes: 47 | label: Record 48 | options: 49 | - label: "I have read the Contributing Guidelines" 50 | required: true 51 | - label: "I'm a GSSOC'24 contributor" 52 | required: False 53 | - label: "I have starred the repository" 54 | required: true 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation_update.yml: -------------------------------------------------------------------------------- 1 | name: 📝 Documentation Update 2 | description: Improve Documentation 3 | title: "[Documentation Update]: " 4 | body: 5 | - type: checkboxes 6 | id: existing-issue 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for the updates you want to make. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: textarea 14 | id: issue-description 15 | attributes: 16 | label: Issue Description 17 | description: Please provide a clear description of the documentation update you are suggesting. 18 | placeholder: Describe the improvement or correction you'd like to see in the documentation. 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: suggested-change 23 | attributes: 24 | label: Suggested Change 25 | description: Provide details of the proposed change to the documentation. 26 | placeholder: Explain how the documentation should be updated or corrected. 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: rationale 31 | attributes: 32 | label: Rationale 33 | description: Why is this documentation update necessary or beneficial? 34 | placeholder: Explain the importance or reasoning behind the suggested change. 35 | validations: 36 | required: False 37 | - type: dropdown 38 | id: urgency 39 | attributes: 40 | label: Urgency 41 | description: How urgently do you believe this documentation update is needed? 42 | options: 43 | - High 44 | - Medium 45 | - Low 46 | default: 0 47 | validations: 48 | required: true 49 | - type: checkboxes 50 | id: terms 51 | attributes: 52 | label: Record 53 | options: 54 | - label: "I have read the Contributing Guidelines" 55 | required: true 56 | - label: "I'm a GSSOC'24 contributor" 57 | required: false 58 | - label: "I have starred the repository" 59 | required: true 60 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: ✨ Feature Request 2 | description: Suggest a feature 3 | title: "[Feature Request]: " 4 | body: 5 | - type: checkboxes 6 | id: existing-issue 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for this feature. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: textarea 14 | id: feature-description 15 | attributes: 16 | label: Feature Description 17 | description: Please provide a detailed description of the feature you are requesting. 18 | placeholder: Describe the new feature or enhancement you'd like to see. 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: use-case 23 | attributes: 24 | label: Use Case 25 | description: How would this feature enhance your use of the project? 26 | placeholder: Describe a specific use case or scenario where this feature would be beneficial. 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: benefits 31 | attributes: 32 | label: Benefits 33 | description: What benefits would this feature bring to the project or community? 34 | placeholder: Explain the advantages of implementing this feature. 35 | - type: textarea 36 | id: screenShots 37 | attributes: 38 | label: Add ScreenShots 39 | description: If any... 40 | - type: dropdown 41 | id: priority 42 | attributes: 43 | label: Priority 44 | description: How important is this feature to you? 45 | options: 46 | - High 47 | - Medium 48 | - Low 49 | default: 0 50 | validations: 51 | required: true 52 | - type: checkboxes 53 | id: terms 54 | attributes: 55 | label: Record 56 | options: 57 | - label: "I have read the Contributing Guidelines" 58 | required: true 59 | - label: "I'm a GSSOC'24 contributor" 60 | required: false 61 | - label: "I have starred the repository" 62 | required: true 63 | -------------------------------------------------------------------------------- /.github/Pull_Request_Template.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | A clear and concise description of what the PR does. 3 | - This PR does the following: 4 | - Adds ... 5 | - Fixes ... 6 | - Updates ... 7 | 8 | ### Related Issues 9 | Link any related issues using the format `Fixes #issue_number`. 10 | This helps to automatically close related issues when the PR is merged. 11 | - Placeholder: "Fixes #123" 12 | 13 | ### Changes 14 | List the detailed changes made in this PR. 15 | - Added a new feature to ... 16 | - Refactored the ... 17 | - Fixed a bug in ... 18 | 19 | ### Testing Instructions 20 | Detailed instructions on how to test the changes. Include any setup needed and specific test cases. 21 | 1. Pull this branch. 22 | 2. Run `npm install` to install dependencies. 23 | 3. Run `npm test` to execute the test suite. 24 | 4. Verify that ... 25 | 26 | ### Screenshots (if applicable) 27 | Add any screenshots that help explain or visualize the changes. 28 | 29 | ### Additional Context 30 | Any additional context or information that reviewers should be aware of. 31 | - This PR is based on the following... 32 | 33 | ### Checklist 34 | Make sure to check off all the items before submitting. Mark with [x] if done. 35 | - [ ] I have performed a self-review of my code 36 | - [ ] I have commented my code, particularly in hard-to-understand areas 37 | - [ ] My changes generate no new warnings 38 | - [ ] I am working on this issue under GSSOC 39 | -------------------------------------------------------------------------------- /.github/workflows/auto-label.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Auto Label Issue 3 | 4 | on: 5 | issues: 6 | types: [opened, reopened, edited] 7 | 8 | jobs: 9 | label_issue: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | steps: 14 | - name: Label Issue 15 | uses: actions/github-script@v6 16 | with: 17 | github-token: ${{secrets.GITHUB_TOKEN}} 18 | script: | 19 | const issue = context.payload.issue; 20 | const issueBody = issue.body ? issue.body.toLowerCase() : ''; 21 | const issueTitle = issue.title.toLowerCase(); 22 | 23 | // Add gssoc label to all issues 24 | await github.rest.issues.addLabels({ 25 | owner: context.repo.owner, 26 | repo: context.repo.repo, 27 | issue_number: issue.number, 28 | labels: ['gssoc-ext','hacktoberfest-accepted'] 29 | }); 30 | const addLabel = async (label) => { 31 | await github.rest.issues.addLabels({ 32 | owner: context.repo.owner, 33 | repo: context.repo.repo, 34 | issue_number: issue.number, 35 | labels: [label] 36 | }); 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/autocomment-iss-close.yml: -------------------------------------------------------------------------------- 1 | name: Comment on Issue Close 2 | 3 | on: 4 | issues: 5 | types: [closed] 6 | 7 | jobs: 8 | greet-on-close: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Greet User 13 | uses: actions/github-script@v5 14 | with: 15 | github-token: ${{ secrets.GITHUB_TOKEN }} 16 | script: | 17 | const issue = context.payload.issue; 18 | const issueCreator = issue.user.login; 19 | const issueNumber = issue.number; 20 | 21 | const greetingMessage = `Hello @${issueCreator}! Your issue #${issueNumber} has been closed. Thank you for your contribution!`; 22 | 23 | github.rest.issues.createComment({ 24 | owner: context.repo.owner, 25 | repo: context.repo.repo, 26 | issue_number: issueNumber, 27 | body: greetingMessage 28 | }); -------------------------------------------------------------------------------- /.github/workflows/autocomment-pr-merge.yml: -------------------------------------------------------------------------------- 1 | name: Auto Comment on PR Merge 2 | 3 | on: 4 | pull_request: 5 | types: [closed] 6 | 7 | permissions: 8 | issues: write 9 | pull-requests: write 10 | 11 | jobs: 12 | comment: 13 | runs-on: ubuntu-latest 14 | if: github.event.pull_request.merged == true 15 | 16 | steps: 17 | - name: Checkout Repository 18 | uses: actions/checkout@v2 19 | 20 | - name: Add Comment to Issue 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | run: | 24 | COMMENT=$(cat < 35 | issue.pull_request && issue.pull_request.url === pr.url 36 | ); 37 | 38 | if (!linkedIssue) { 39 | await octokit.pulls.update({ owner, repo, pull_number: pr.number, state: 'closed' }); 40 | await octokit.issues.createComment({ 41 | owner, 42 | repo, 43 | issue_number: pr.number, 44 | body: "This pull request has been closed because it does not mention the issue that it solves. Please refer to the [Contributing files](https://github.com/Sulagna-Dutta-Roy/GGExtensions/blob/master/CONTRIBUTING.md) to help you add this information. Then, tag the maintainers so your PR can be reopened." 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yaml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: actions/first-interaction@v1 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-message: "Hi there! Thanks for opening this issue. We appreciate your contribution to this open-source project. We aim to respond or assign your issue as soon as possible." 16 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:21 2 | 3 | WORKDIR /expressapp 4 | 5 | COPY package* . 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | # Set the DATABASE_URL environment variable from the .env file 12 | ENV MONGO_URL=${MONGO_URL} 13 | 14 | CMD ["npm", "run", "start"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Trisha Sahu 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 | -------------------------------------------------------------------------------- /client/.env.sample: -------------------------------------------------------------------------------- 1 | REACT_APP_NY_TIMES_APIKEY=get_Your_NYTimes_APIKEY -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 as build 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm ci 8 | 9 | COPY . . 10 | 11 | RUN npm run build 12 | 13 | FROM nginx:alpine 14 | 15 | COPY --from=build /app/build /usr/share/nginx/html 16 | 17 | EXPOSE 80 18 | 19 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.11.4", 7 | "@emotion/styled": "^11.11.5", 8 | "@floating-ui/react": "^0.26.24", 9 | "@heroicons/react": "^2.1.3", 10 | "@mui/icons-material": "^5.15.17", 11 | "@mui/material": "^5.16.7", 12 | "@testing-library/jest-dom": "^5.17.0", 13 | "@testing-library/react": "^13.4.0", 14 | "@testing-library/user-event": "^13.5.0", 15 | "axios": "^1.7.3", 16 | "client": "file:", 17 | "flowbite-react": "^0.10.2", 18 | "lottie-react": "^2.4.0", 19 | "react": "^18.3.1", 20 | "react-dom": "^18.3.1", 21 | "react-hot-toast": "^2.4.1", 22 | "react-icons": "^5.3.0", 23 | "react-router-dom": "^6.23.1", 24 | "react-scripts": "^5.0.1", 25 | "styled-components": "^6.1.12", 26 | "swiper": "^11.1.14", 27 | "tailwindcss": "^3.4.4", 28 | "web-vitals": "^2.1.4", 29 | "yup": "^1.4.0" 30 | }, 31 | "scripts": { 32 | "start": "react-scripts start", 33 | "build": "react-scripts build", 34 | "test": "react-scripts test", 35 | "eject": "react-scripts eject" 36 | }, 37 | "eslintConfig": { 38 | "extends": [ 39 | "react-app", 40 | "react-app/jest" 41 | ] 42 | }, 43 | "browserslist": { 44 | "production": [ 45 | ">0.2%", 46 | "not dead", 47 | "not op_mini all" 48 | ], 49 | "development": [ 50 | "last 1 chrome version", 51 | "last 1 firefox version", 52 | "last 1 safari version" 53 | ] 54 | }, 55 | "devDependencies": { 56 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /client/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page Not Found 7 | 8 | 9 | 10 |
11 |

404

12 |

Oops! The page you are looking for does not exist.

13 |

You will be redirected to the homepage in 5 seconds.

14 | Go to Homepage 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /client/public/action.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Action Books 8 | 9 | 93 | 94 | 95 | 96 | 97 | 98 |

Action Books

99 |
100 | 101 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /client/public/books.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Book Categories 7 | 8 | 51 | 52 | 53 |

Book Preference Quiz

54 | 55 |

Book Categories

56 | 57 |
58 |
59 |

Action

60 | Show Books 61 |
62 |
63 |

Thriller

64 | Show Books 65 |
66 |
67 |

Fiction

68 | Show Books 69 |
70 |
71 |

Tech

72 | Show Books 73 |
74 |
75 |

Philosophy

76 | Show Books 77 |
78 |
79 |

Manga

80 | Show Books 81 |
82 |
83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /client/public/cart.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/public/cart.webp -------------------------------------------------------------------------------- /client/public/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/public/error.png -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/fiction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Thriller Books 8 | 9 | 93 | 94 | 95 | 96 | 97 |

FICTION BOOKS

98 |
99 | 100 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /client/public/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/public/google.png -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manga.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Thriller Books 8 | 9 | 93 | 94 | 95 | 96 | 97 |

MANGA BOOKS

98 |
99 | 100 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/philosophy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Thriller Books 8 | 9 | 93 | 94 | 95 | 96 | 97 |

PHILOSOPHY BOOKS

98 |
99 | 100 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /client/public/preload.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/public/preload.mp4 -------------------------------------------------------------------------------- /client/public/quiz.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Book Preference Form 8 | 9 | 76 | 77 | 78 | 79 | 80 |

Book Preference Form

81 |
82 |
83 | 84 | 90 | 91 | 92 | 93 | 94 | 95 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 |
113 | 114 | 118 |
119 | 120 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/public/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | let countdownElement = document.getElementById('countdown'); 3 | let countdownValue = parseInt(countdownElement.textContent); 4 | 5 | let countdownInterval = setInterval(function() { 6 | countdownValue -= 1; 7 | countdownElement.textContent = countdownValue; 8 | 9 | if (countdownValue <= 0) { 10 | clearInterval(countdownInterval); 11 | window.location.href = 'index.html'; 12 | } 13 | }, 1000); 14 | }); 15 | -------------------------------------------------------------------------------- /client/public/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Arial', sans-serif; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | height: 100vh; 9 | background: linear-gradient(to right, #ff7e5f, #feb47b); 10 | } 11 | 12 | .error-container { 13 | text-align: center; 14 | color: #fff; 15 | } 16 | 17 | .error-container h1 { 18 | font-size: 10em; 19 | margin: 0; 20 | } 21 | 22 | .error-container p { 23 | font-size: 1.5em; 24 | margin: 20px 0; 25 | } 26 | 27 | .home-button { 28 | display: inline-block; 29 | padding: 10px 20px; 30 | font-size: 1.2em; 31 | color: #ff7e5f; 32 | background-color: #fff; 33 | border-radius: 5px; 34 | text-decoration: none; 35 | transition: background-color 0.3s, color 0.3s; 36 | } 37 | 38 | .home-button:hover { 39 | background-color: #ff7e5f; 40 | color: #fff; 41 | } 42 | -------------------------------------------------------------------------------- /client/public/tech.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Thriller Books 8 | 9 | 93 | 94 | 95 | 96 | 97 |

TECHNOLOGY BOOKS

98 |
99 | 100 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /client/public/thriller.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Thriller Books 8 | 9 | 93 | 94 | 95 | 96 | 97 |

THRILLER BOOKS

98 |
99 | 100 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /client/src/Animations.css: -------------------------------------------------------------------------------- 1 | /* Animations */ 2 | .fromTop{ 3 | animation: slideTop 1s ease forwards; 4 | } 5 | .fromLeft{ 6 | animation: slideLeft 1s ease forwards; 7 | } 8 | .fromRight{ 9 | animation: slideRight 1s ease forwards; 10 | } 11 | .fromBottom{ 12 | animation: slideBottom 1s ease forwards; 13 | } 14 | 15 | @keyframes slideRight { 16 | 0% { 17 | transform: translateX(100px); 18 | opacity: 0; 19 | } 20 | 100% { 21 | transform: translateX(0); 22 | opacity: 1; 23 | } 24 | } 25 | @keyframes slideLeft { 26 | 0% { 27 | transform: translateX(-100px); 28 | opacity: 0; 29 | } 30 | 100% { 31 | transform: translateX(0); 32 | opacity: 1; 33 | } 34 | } 35 | @keyframes slideTop { 36 | 0% { 37 | transform: translateY(-100px); 38 | opacity: 0; 39 | } 40 | 100% { 41 | transform: translateY(0); 42 | opacity: 1; 43 | } 44 | } 45 | @keyframes slideBottom { 46 | 0% { 47 | transform: translateY(100px); 48 | opacity: 0; 49 | } 50 | 100% { 51 | transform: translateY(0); 52 | opacity: 1; 53 | } 54 | } 55 | @keyframes zoomIn { 56 | 0% { 57 | transform: scale(0); 58 | opacity: 0; 59 | } 60 | 100% { 61 | transform: scale(1); 62 | opacity: 1; 63 | } 64 | } 65 | 66 | 67 | /* contributors animation */ 68 | .contributor-card:hover { 69 | transform: scale(1.05); 70 | box-shadow: 0 4px 6px rgba(5, 205, 208, 0.752); 71 | } 72 | 73 | .contributor-card:hover p { 74 | text-shadow: 1px 1px 2px rgb(0, 225, 255), 0 0 0.2em rgb(0, 191, 255), 0 0 0.8em rgb(135, 206, 235); 75 | color: rgb(0, 0, 0); 76 | font-weight: 500; 77 | } 78 | 79 | .contributor-card:hover h2 { 80 | text-shadow: 1px 1px 2px rgba(237, 9, 176, 0.926), 0 0 0.2em rgb(0, 191, 255), 0 0 0.8em rgb(135, 206, 235); 81 | color: white; 82 | font-size: 1.04rem; 83 | font-weight: 600; 84 | text-decoration: wavy; 85 | } 86 | 87 | .contributor-card:hover .contributor-avatar { 88 | border: 3.5px solid #89e6f0; 89 | width: 5.2rem; 90 | box-shadow: -2px 4px 10px 1px rgba(1, 41, 218, 0.75); 91 | height: 5.2rem; 92 | } 93 | 94 | .contributor-card::before { 95 | content: ''; 96 | position: absolute; 97 | top: 0; 98 | left: 0; 99 | width: 100%; 100 | height: 100%; 101 | background: linear-gradient(152deg, #0077b6 50%, #023e8a 50%); 102 | transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; 103 | transform: translate(-100%, -100%); 104 | opacity: 0; 105 | z-index: -1; 106 | } 107 | 108 | .contributor-card:hover::before { 109 | transform: translate(0, 0); 110 | opacity: 1; 111 | } -------------------------------------------------------------------------------- /client/src/App.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;700&family=Poppins:wght@400;500;600;700&display=swap'); 2 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; 3 | import './App.css'; 4 | import './Animations.css'; 5 | import { Navbar, Footer } from './Components/index.js'; 6 | // import { ProfilePage, Product } from './Components/index.js'; 7 | import { Product } from './Components/index.js'; 8 | import RomancePage from './Pages/RomancePage.jsx'; 9 | import ActionPage from './Pages/ActionPage.jsx'; 10 | import ThrillerPage from './Pages/ThrillerPage.jsx'; 11 | import FictionPage from './Pages/FictionPage.jsx'; 12 | import TechPage from './Pages/TechPage.jsx'; 13 | import PhilosophyPage from './Pages/PhilosophyPage.jsx'; 14 | import MangaPage from './Pages/MangaPage.jsx'; 15 | import LoginPage from './Pages/LoginPage.jsx'; 16 | import SignUpPage from './Pages/SignUpPage.jsx'; 17 | import Cart from './Pages/Cart.jsx'; 18 | // import Orders from './Pages/Orders.jsx'; 19 | import Wishlist from './Pages/Wishlist.jsx'; 20 | import HomePage from './Pages/Home.jsx'; 21 | import Shop from "./Pages/Shop.jsx"; 22 | import Contact from "./Pages/Contact.jsx"; 23 | import PrivacyPolicy from "./Pages/PrivacyPolicy.jsx"; 24 | import TermsAndConditions from "./Pages/Terms.jsx"; 25 | import FAQ from "./Pages/Faq.jsx"; 26 | import AboutUs from "./Pages/AboutUs.jsx"; 27 | import { OrderList } from "./Pages/Orders.jsx"; 28 | import Contributors from "./Pages/Contributors.jsx"; 29 | import Preloader from "./Components/Preloader.jsx"; 30 | import { Toast } from "./Toast/Toast.js"; 31 | import GoToTop from "./Components/GoToTop.jsx"; 32 | import License from "./Pages/Licensing.jsx"; 33 | import CustomerProfile from "./Pages/CustomerProfile.jsx"; 34 | import GiftCards from "./Pages/GiftCards.jsx"; 35 | import Careers from "./Pages/Careers.jsx"; 36 | import NotFound from "./Pages/NotFound.jsx"; 37 | import CancelReturns from './Pages/CancelReturns.jsx' 38 | import ChatBotEmbed from "./chatbot.jsx"; 39 | 40 | import ResetPassword from "./Pages/ResetPassword.jsx"; 41 | import Book from './Pages/Book.jsx'; 42 | 43 | 44 | function App() { 45 | const [darkMode, setDarkMode] = useState(false); 46 | 47 | const toggleDarkMode = () => { 48 | setDarkMode(!darkMode); 49 | }; 50 | 51 | useEffect(() => { 52 | document.body.classList.toggle("dark", darkMode); 53 | }, [darkMode]); 54 | 55 | const appStyle = { 56 | backgroundColor: darkMode ? "#333" : "#f4f4f4", 57 | }; 58 | 59 | return ( 60 | 61 |
62 | 63 | 64 | } /> 65 | } /> 66 | } /> 67 | } /> 68 | } /> 69 | } /> 70 | } /> 71 | } /> 72 | } /> 73 | } /> 74 | } /> 75 | } /> 76 | } /> 77 | } /> 78 | } /> 79 | } /> 80 | } /> 81 | } /> 82 | } /> 83 | } /> 84 | } /> 85 | } /> 86 | } /> 87 | } /> 88 | } /> 89 | } /> 90 | } /> 91 | } /> {/* Fallback route */} 92 | } /> 93 | 94 | 95 |
{/* Pass darkMode prop here */} 96 | {/* Ensure Preloader is correctly styled */} 97 | {/* Added GoToTop component */} 98 | 99 | 100 |
101 |
102 | ); 103 | } 104 | 105 | export default App; 106 | -------------------------------------------------------------------------------- /client/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /client/src/Context/AuthContext.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext, createContext, useEffect } from 'react'; 2 | 3 | // Create the context 4 | const AuthContext = createContext(); 5 | 6 | // Create the provider component 7 | export const AuthContextProvider = ({ children }) => { 8 | const [userLoggedIn, setUserLoggedIn] = useState(false); 9 | 10 | useEffect(() => { 11 | const token =localStorage.getItem('token'); 12 | console.log(token); 13 | if (token) { 14 | setUserLoggedIn(true); 15 | } 16 | }, []); 17 | 18 | return ( 19 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | // Create a custom hook to use the AuthContext 26 | export const useAuth = () => useContext(AuthContext); 27 | -------------------------------------------------------------------------------- /client/src/Context/ToastContext.js: -------------------------------------------------------------------------------- 1 | 2 | import { useState, useContext, createContext } from 'react' 3 | 4 | const ToastContext = createContext() 5 | 6 | let ToastContextProvider = ({children}) => { 7 | const [toastList, setToastList] = useState([]) 8 | 9 | let toastProperties = null; 10 | 11 | const showToast = (typeOfToast, toastTitle, toastDescription, time=5000) => 12 | { 13 | switch(typeOfToast) 14 | { 15 | case "success" : toastProperties = { 16 | id : toastList.length+1, 17 | title : toastTitle, 18 | description : toastDescription, 19 | type:"success", 20 | time: time 21 | 22 | } 23 | 24 | break; 25 | case "error" : toastProperties = { 26 | id : toastList.length+1, 27 | title : toastTitle, 28 | description : toastDescription, 29 | type:"error", 30 | time: time 31 | } 32 | break; 33 | case "warning" : toastProperties = { 34 | id : toastList.length+1, 35 | title : toastTitle, 36 | description : toastDescription, 37 | type:"warning", 38 | time: time 39 | } 40 | break; 41 | case "info" : toastProperties = { 42 | id : toastList.length+1, 43 | title : toastTitle, 44 | description : toastDescription, 45 | type:"info", 46 | time: time 47 | } 48 | break; 49 | default : toastProperties = {} 50 | } 51 | 52 | setToastList(toastList=>[...toastList, toastProperties]) 53 | } 54 | 55 | return ( 56 | 57 | {children} 58 | 59 | ) 60 | } 61 | 62 | let useToast = () => useContext(ToastContext) 63 | 64 | export { ToastContextProvider, useToast }; -------------------------------------------------------------------------------- /client/src/Pages/AboutUs.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Preloader from '../Components/Preloader'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | const AboutUs = () => { 6 | useEffect(() => { 7 | window.scrollTo(0, 0); 8 | }, []); 9 | 10 | return ( 11 | <> 12 | 13 |
14 |
15 |
16 |
17 |

About Us

18 |

19 | Online Book Sales is an e-commerce platform dedicated to providing book lovers with a 20 | convenient way to browse and purchase a wide variety of books online. Our goal is to 21 | offer an extensive catalog of books across various genres, including Fiction, 22 | Non-fiction, Science Fiction, Romance, Mystery & Thriller, Biographies, Self-help, 23 | Children's Books, and Academic & Educational Books. 24 |

25 |

26 | At Online Book Sales, we prioritize customer satisfaction and aim to provide a seamless shopping experience. From browsing our curated catalog to securely checking out and tracking your order, we ensure that your journey with us is enjoyable and hassle-free. 27 |

28 |

29 | Our team is dedicated to maintaining high standards of service and reliability. Should you have any questions or need assistance, our customer support team is readily available to help. Visit our FAQ page for answers to common queries or reach out to us directly through our Contact Us page. 30 |

31 |

32 | Thank you for choosing Online Book Sales. Happy reading! 33 |

34 |

Our Mission

35 |

36 | Our mission is to make reading accessible and enjoyable for everyone by offering a 37 | seamless online shopping experience for books of all kinds. We aim to connect readers 38 | with their favorite authors and discover new books that inspire and entertain. 39 |

40 |

Contact Us

41 |

42 | For any inquiries, suggestions, or support, please visit our{' '} 43 | 44 | Contact Us 45 | {' '} 46 | page. Our dedicated support team is available to assist you. 47 |

48 |
49 |
50 |
51 |
52 | 53 | ); 54 | }; 55 | 56 | export default AboutUs; 57 | 58 | -------------------------------------------------------------------------------- /client/src/Pages/ActionPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ActionPage = () => { 4 | return ( 5 |
6 |

Action Page

7 |
8 | ); 9 | }; 10 | 11 | export default ActionPage; -------------------------------------------------------------------------------- /client/src/Pages/BannerCard.css: -------------------------------------------------------------------------------- 1 | .mySwiper .swiper-pagination { 2 | bottom: 0; /* Moves pagination to the bottom */ 3 | margin-top: 10px; /* Adds some margin between the card and dots */ 4 | position: relative; /* Avoids overlap with the image */ 5 | } 6 | 7 | -------------------------------------------------------------------------------- /client/src/Pages/BannerCard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { Swiper, SwiperSlide } from 'swiper/react'; 3 | import { Link } from 'react-router-dom'; 4 | import 'swiper/css'; 5 | import 'swiper/css/pagination'; 6 | import { FaCartShopping } from 'react-icons/fa6'; 7 | import { Pagination } from 'swiper/modules'; 8 | import './BannerCard.css'; 9 | 10 | function BannerCard({ books, highlightedBookId }) { 11 | const bookref = useRef({}); // Ensure refs are stored in an object 12 | const headLine = "New Arrivals"; 13 | 14 | // Scroll to the highlighted book when highlightedBookId changes 15 | useEffect(() => { 16 | if (highlightedBookId && bookref.current[highlightedBookId]) { 17 | const bookElement = bookref.current[highlightedBookId]; 18 | bookElement.scrollIntoView({ behavior: "smooth", block: "center" }); 19 | bookElement.classList.add("highlight"); 20 | setTimeout(() => { 21 | bookElement.classList.remove("highlight"); 22 | }, 3000); 23 | } 24 | }, [highlightedBookId]); 25 | 26 | return ( 27 |
28 |

{headLine}

29 |
{/* Added overflow */} 30 | 53 | {books.map((book) => ( 54 | 55 | 56 |
(bookref.current[book.bookTitle] = el)} className="relative"> 57 | {book.bookTitle} 62 |
63 | 64 |
65 |
66 |
67 |
68 |

{book.bookTitle}

69 |

{book.authorName}

70 |
71 |
72 |

$10.00

73 |
74 |
75 | 76 |
77 | ))} 78 |
79 |
80 |
81 |
82 | ); 83 | } 84 | 85 | export default BannerCard; 86 | -------------------------------------------------------------------------------- /client/src/Pages/Book.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useParams } from 'react-router-dom'; 3 | 4 | const Book = () => { 5 | const { id } = useParams(); 6 | 7 | const [book, setBook] = useState(null); 8 | 9 | useEffect(() => { 10 | fetchBook() 11 | }, []) 12 | 13 | const fetchBook = async () => { 14 | const res = await fetch(`https://www.googleapis.com/books/v1/volumes/${id}`) 15 | 16 | const data = await res.json(); 17 | 18 | console.log(data); 19 | 20 | setBook(data.volumeInfo) 21 | } 22 | 23 | 24 | return ( 25 |
26 |
27 | image 32 |
33 |
34 |

{book?.title}

35 |

{book?.authors[0]}

36 |

{book?.description}

37 |
38 | Published: 39 | {book?.publishedDate} 40 |
41 |
42 |
43 | ) 44 | } 45 | 46 | export default Book -------------------------------------------------------------------------------- /client/src/Pages/CancelReturns.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Preloader from '../Components/Preloader'; 3 | 4 | const PrivacyPolicy = () => { 5 | useEffect(() => { 6 | window.scrollTo(0, 0); 7 | }, []); 8 | 9 | return ( 10 | <> 11 | 12 |
13 |
14 |
15 |

Cancellations/Returns

16 |
17 |

Last updated: August 8, 2024

18 |

Welcome to Online Book Sales - Book4U. We are committed to protecting your personal information and your right to privacy. If you have any questions or concerns about our policy or our practices with regard to your personal information, please contact us at supportbook4u@onlinebooksales.com.

19 |
20 | 21 |
22 |

Cancellations & Returns

23 |

We understand that sometimes you may need to cancel an order or return a product. Below are the details of our cancellation and return policy:

24 |
    25 |
  • Order Cancellations: Orders can be canceled within 24 hours of placement. After this window, cancellations may not be accepted if the book has already been shipped.
  • 26 |
  • Returns: You can return books within 14 days of delivery. Returned books must be in their original condition, without any damage or markings. Please contact our support team to initiate a return process.
  • 27 |
  • Non-returnable Items: Certain items, such as digital or e-books, are not eligible for returns. Please ensure you review your purchase before completing the order.
  • 28 |
  • Refund Process: Refunds for canceled or returned items will be processed within 7-10 business days after we receive the returned book in good condition.
  • 29 |
  • Return Shipping Costs: Customers are responsible for return shipping fees unless the return is due to a defect or an error on our part.
  • 30 |
31 |

If you have any further questions regarding cancellations or returns, feel free to contact us at supportbook4u@onlinebooksales.com.

32 |
33 | 34 |
35 |

Information We Collect

36 |

We collect personal information that you provide to us, such as your name, address, contact information, and payment details. We collect this information when you register for our services, make a purchase, or otherwise interact with our website.

37 |
38 | 39 |
40 |

How We Use Your Information

41 |

We use the personal information we collect to process transactions, manage your orders, and communicate with you about our products and services. We may also use this information for customer support, marketing purposes, and to improve our website and services.

42 |
43 | 44 |
45 |

Sharing Your Information

46 |

We may share your information in the following situations:

47 |
    48 |
  • With Service Providers: We may share your information with third-party service providers who assist us in operating our website and processing payments.
  • 49 |
  • For Legal Reasons: We may disclose your information if required to do so by law or in response to a valid request from law enforcement or other government authorities.
  • 50 |
  • Business Transfers: In the event of a merger, acquisition, or sale of our company, your information may be transferred as part of the transaction.
  • 51 |
52 |
53 | 54 |
55 |

Security of Your Information

56 |

We implement a variety of security measures to protect your personal information from unauthorized access, alteration, disclosure, or destruction. However, no security system is impenetrable, and we cannot guarantee the absolute security of your information.

57 |
58 | 59 |
60 |

Contact Us

61 |

If you have any questions or comments about this privacy policy or our cancellation and return policies, please contact us at supportbook4u@onlinebooksales.com.

62 |
63 |
64 |
65 |
66 | 67 | ); 68 | }; 69 | 70 | export default PrivacyPolicy; 71 | -------------------------------------------------------------------------------- /client/src/Pages/Careers.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Preloader from '../Components/Preloader'; 3 | 4 | const Careers = () => { 5 | useEffect(() => { 6 | window.scrollTo(0, 0); 7 | }, []); 8 | 9 | return ( 10 | <> 11 | 12 |
13 |
14 |
15 |
16 |

17 | Careers 18 |

19 |

Sorry we have no career openings right now!!!

20 |
21 |
22 |
23 |
24 | 25 | ); 26 | } 27 | 28 | export default Careers -------------------------------------------------------------------------------- /client/src/Pages/Cart.css: -------------------------------------------------------------------------------- 1 | 2 | .cart-container { 3 | max-width: 800px; 4 | margin: 20px auto; 5 | padding: 20px; 6 | border: 1px solid #ddd; 7 | border-radius: 8px; 8 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 9 | } 10 | 11 | .cart-header { 12 | font-size: 24px; 13 | margin-bottom: 10px; 14 | font-weight: bold; 15 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 16 | } 17 | 18 | 19 | 20 | .retry-button { 21 | background-color: #002147; 22 | color: #fff; 23 | border: none; 24 | padding-inline:30px; 25 | padding-block: 6px; 26 | font-size: 16px; 27 | cursor: pointer; 28 | margin-top: 10px; 29 | border-radius: 6px; 30 | text-align: center; 31 | border:2px solid #002147; 32 | 33 | } 34 | .retry-button:hover{ 35 | color:#002147; 36 | background-color: #F4F4F4; 37 | font-weight: 500; 38 | } 39 | 40 | .cart-table { 41 | width: 100%; 42 | border-collapse: collapse; 43 | margin-top: 20px; 44 | } 45 | 46 | .cart-table th, 47 | .cart-table td { 48 | border: 1px solid #ddd; 49 | padding: 10px; 50 | text-align: left; 51 | } 52 | 53 | 54 | .item-image { 55 | max-width: 60px; 56 | max-height: 60px; 57 | border-radius: 4px; 58 | } 59 | 60 | .quantity-button { 61 | background-color: #007bff; 62 | color: #fff; 63 | border: none; 64 | padding: 5px 10px; 65 | font-size: 14px; 66 | cursor: pointer; 67 | margin-left: 5px; 68 | border-radius: 4px; 69 | } 70 | 71 | .remove-button { 72 | background-color: #dc3545; 73 | color: #fff; 74 | border: none; 75 | padding: 5px 10px; 76 | font-size: 14px; 77 | cursor: pointer; 78 | margin-left: 5px; 79 | border-radius: 4px; 80 | } 81 | 82 | .cart-summary { 83 | margin-top: 20px; 84 | display: flex; 85 | justify-content: space-between; 86 | align-items: center; 87 | } 88 | 89 | .cart-total { 90 | font-size: 18px; 91 | } 92 | 93 | .checkout-button { 94 | background-color: #28a745; 95 | color: #fff; 96 | border: none; 97 | padding: 10px 20px; 98 | font-size: 16px; 99 | cursor: pointer; 100 | border-radius: 4px; 101 | transition: background-color 400ms ease, transform 200ms ease; 102 | } 103 | 104 | .checkout-button:hover { 105 | background-color: #218838; 106 | transform: scale(1.05); 107 | } 108 | 109 | 110 | .suggested-products { 111 | margin-top: 20px; 112 | } 113 | 114 | .suggested-products h2 { 115 | font-size: 20px; 116 | } 117 | 118 | .suggested-products ul { 119 | list-style-type: none; 120 | padding: 0; 121 | } 122 | 123 | .suggested-products li { 124 | margin-bottom: 5px; 125 | font-size: 16px; 126 | } 127 | 128 | @media (max-width: 600px) { 129 | .cart-table th, 130 | .cart-table td { 131 | font-size: 14px; 132 | } 133 | } 134 | 135 | .cart-table { 136 | width: 100%; 137 | border-collapse: collapse; 138 | font-family: poppins; 139 | margin-top: 20px; 140 | } 141 | 142 | .cart-table th, 143 | .cart-table td { 144 | border: none; 145 | padding: 15px; 146 | text-align: center; 147 | 148 | } 149 | 150 | .cart-table th { 151 | font-weight: bold; 152 | background-color:#dde8f9; 153 | 154 | } 155 | 156 | .cart-table tbody tr { 157 | 158 | margin-bottom: 10px; /* Adds space between rows */ 159 | border-bottom: 1px solid #e0e0e0; /* Optional separator between rows */ 160 | } 161 | 162 | .cart-table img { 163 | border-radius: 8px; /* Optional: Makes images rounded */ 164 | display: block; 165 | width:10vw; 166 | height:20vh; 167 | } 168 | .remove-btn{ 169 | background-color: #F95454; 170 | color: #fff; 171 | border: none; 172 | padding: 10px 20px; 173 | font-size: 12px; 174 | cursor: pointer; 175 | border-radius: 4px; 176 | transition: background-color 400ms ease, transform 200ms ease; 177 | } 178 | 179 | 180 | .remove-btn:hover { 181 | background-color: #C62E2E; 182 | transform: scale(1.05); 183 | } 184 | 185 | -------------------------------------------------------------------------------- /client/src/Pages/Contributors.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import axios from 'axios'; 3 | import './Contributors.css'; 4 | import '../Animations.css' 5 | import Preloader from '../Components/Preloader'; 6 | 7 | function Contributors() { 8 | const [contributors, setContributors] = useState([]); 9 | const [loading, setLoading] = useState(true); 10 | const [error, setError] = useState(null); // Added error state 11 | 12 | useEffect(() => { 13 | async function fetchContributors() { 14 | let allContributors = []; 15 | let page = 1; 16 | 17 | try { 18 | while (true) { 19 | const response = await axios.get( 20 | `https://api.github.com/repos/Trisha-tech/OnlineBookSales/contributors`, 21 | { 22 | params: { 23 | per_page: 100, 24 | page, 25 | }, 26 | } 27 | ); 28 | const data = response.data; 29 | if (data.length === 0) { 30 | break; 31 | } 32 | allContributors = [...allContributors, ...data]; 33 | page++; 34 | } 35 | setContributors(allContributors); 36 | } catch (error) { 37 | console.error('Error fetching contributors:', error.message); 38 | setError('Failed to load contributors. Please try again later.'); // Set error message 39 | } finally { 40 | setLoading(false); 41 | } 42 | } 43 | fetchContributors(); 44 | }, []); 45 | 46 | if (loading) { 47 | return ; // Show preloader while loading 48 | } 49 | 50 | if (error) { 51 | return ( 52 |
53 |

{error}

54 |
55 | ); // Show error message if there's an error 56 | } 57 | 58 | return ( 59 |
60 |

Our Contributors

61 |
62 | {contributors.length > 0 ? ( 63 | contributors.map((contributor) => ( 64 |
65 | 71 | {contributor.login} 76 | 77 |

{contributor.login}

78 |

79 | Contributions: {contributor.contributions} 80 |

81 |
82 | )) 83 | ) : ( 84 |

No contributors found.

85 | )} 86 |
87 |
88 | ); 89 | } 90 | 91 | export default Contributors; 92 | -------------------------------------------------------------------------------- /client/src/Pages/CustomerProfile.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Sidebar from "../Components/CustomerProfile/Sidebar"; 3 | import Profile from "../Components/CustomerProfile/Profile"; 4 | import Orders from "../Components/CustomerProfile/Orders"; 5 | import Pancard from "../Components/CustomerProfile/Pancard"; 6 | import DeliveryLocation from "../Components/CustomerProfile/DeliveryLocation"; 7 | import Giftcard from "../Components/CustomerProfile/Giftcard"; 8 | const CustomerProfile = () => { 9 | const [activeSection, setActiveSection] = useState("profile"); 10 | 11 | const renderSection = () => { 12 | switch (activeSection) { 13 | case "profile": 14 | return ; 15 | case "orders": 16 | return ; 17 | case "delivery-location": 18 | return ; 19 | case "pan-card": 20 | return ; 21 | case "gift-card": 22 | return ; 23 | default: 24 | return ; 25 | } 26 | }; 27 | return ( 28 |
29 |
30 | 31 |
32 | {renderSection()} 33 |
34 |
35 |
36 | ); 37 | }; 38 | 39 | export default CustomerProfile; 40 | -------------------------------------------------------------------------------- /client/src/Pages/FictionPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const FictionPage = () => { 4 | return ( 5 |
6 |

Action Page

7 |
8 | ); 9 | }; 10 | 11 | export default FictionPage; -------------------------------------------------------------------------------- /client/src/Pages/GiftCards.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Preloader from '../Components/Preloader'; 3 | 4 | const GiftCards = () => { 5 | useEffect(() => { 6 | window.scrollTo(0, 0); 7 | }, []); 8 | 9 | return ( 10 | <> 11 | 12 |
13 |
14 |
15 |
16 |

17 | GiftCards 18 |

19 |

Sorry we have no Giftcards right now!!!

20 |
21 |
22 |
23 |
24 | 25 | ); 26 | } 27 | 28 | export default GiftCards -------------------------------------------------------------------------------- /client/src/Pages/Licensing.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Preloader from '../Components/Preloader'; 3 | 4 | const License = () => { 5 | useEffect(() => { 6 | window.scrollTo(0, 0); 7 | }, []); 8 | 9 | return ( 10 | <> 11 | 12 |
13 |

MIT License

14 |

Copyright (c) 2024 Trisha Sahu

15 |

16 | Permission is hereby granted, free of charge, to any person obtaining a copy 17 | of this software and associated documentation files (the "Software"), to deal 18 | in the Software without restriction, including without limitation the rights 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | copies of the Software, and to permit persons to whom the Software is 21 | furnished to do so, subject to the following conditions: 22 |

23 |

24 | The above copyright notice and this permission notice shall be included in all 25 | copies or substantial portions of the Software. 26 |

27 |

28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 |

36 |
37 | 38 | ); 39 | }; 40 | 41 | // const LicenseWrapper = styled.div` 42 | // padding: 20px; 43 | // max-width: 800px; 44 | // margin: 0 auto; 45 | // color: #333; 46 | // line-height: 1.6; 47 | // margin-top: 20px; 48 | 49 | // h1 { 50 | // font-size: 2.5rem; 51 | // margin-bottom: 20px; 52 | // font-weight: bold; 53 | // color: #002147; 54 | // } 55 | 56 | // p { 57 | // margin: 10px 0; 58 | // } 59 | // `; 60 | 61 | export default License; 62 | -------------------------------------------------------------------------------- /client/src/Pages/MangaPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const MangaPage = () => { 4 | return ( 5 |
6 |

Manga Page

7 |
8 | ); 9 | }; 10 | 11 | export default MangaPage; -------------------------------------------------------------------------------- /client/src/Pages/Newarrivals.js: -------------------------------------------------------------------------------- 1 | 2 | import { Link } from 'react-router-dom'; 3 | // import BannerCard from '../Pages/BannerCard.js'; 4 | 5 | import React, { useState, useEffect, useRef } from 'react'; 6 | import BannerCard from '../Pages/BannerCard.js'; 7 | 8 | // import { set } from 'mongoose'; 9 | 10 | 11 | export const books = [ 12 | { 13 | _id:"1", 14 | bookTitle: "Fourth Wing", 15 | authorName: "Rebecca Yarros", 16 | imageURL: "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1701980900i/61431922.jpg", 17 | category: "Romantasy", 18 | }, 19 | { 20 | _id:"2", 21 | bookTitle: "The Maid", 22 | authorName: "Nita Prose", 23 | imageURL: "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1643228739i/55196813.jpg", 24 | category: "Fiction", 25 | }, 26 | { 27 | _id:"3", 28 | bookTitle: "Check & Mate", 29 | authorName: "Ali Hazelwood", 30 | imageURL: "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1678378842i/60683957.jpg", 31 | category: "YOUNG ADULT FANTASY & SCIENCE FICTION", 32 | }, 33 | { 34 | _id:"4", 35 | bookTitle: "Holly", 36 | authorName: "Stephen King", 37 | imageURL: "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1674418461i/65916344.jpg", 38 | category: "Horror", 39 | }, 40 | { 41 | _id:"5", 42 | bookTitle: "Tomorrow and Tomorrow", 43 | authorName: "Gabrielle Zevin ", 44 | imageURL: "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1636978687i/58784475.jpg", 45 | category: "Fiction", 46 | }, 47 | { 48 | _id:"6", 49 | bookTitle: "Hidden Pictures", 50 | authorName: "Jason Rekulak", 51 | imageURL: "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1635260162i/58724923.jpg", 52 | category: "Horror", 53 | }, 54 | 55 | { 56 | _id:"7", 57 | bookTitle: "Holly", 58 | authorName: "Stephen King", 59 | imageURL: "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1674418461i/65916344.jpg", 60 | category: "Horror", 61 | }, 62 | 63 | ]; 64 | 65 | function Newarrivals({ onBookClick, highlightedBookId }) { 66 | // const bookRefs = useRef({}); 67 | 68 | 69 | // const fetchRandomBooks = async () => { 70 | // setIsLoading(true); 71 | // try { 72 | // const randomStartIndex = Math.floor(Math.random() * 100); 73 | // const response = await fetch(`https://www.googleapis.com/books/v1/volumes?q=subject:fiction&maxResults=7&startIndex=${randomStartIndex}`); 74 | 75 | // if (!response.ok) { 76 | // throw new Error('HTTP Error! Status: ' + response.status); 77 | // } 78 | 79 | // const data = await response.json(); 80 | // console.log(data); 81 | 82 | 83 | // if (data.items) { 84 | 85 | // const formattedBooks = data.items.map(item => ({ 86 | // id: item.id, 87 | // bookTitle: item.volumeInfo.title, 88 | // authorName: item.volumeInfo.authors ? item.volumeInfo.authors[0] : 'Unknown Author', 89 | // imageURL: item.volumeInfo.imageLinks ? item.volumeInfo.imageLinks.thumbnail : 'https://via.placeholder.com/128x193.png', 90 | // category: item.volumeInfo.categories ? item.volumeInfo.categories[0] : 'Fiction' 91 | // })); 92 | // setBooks(formattedBooks); 93 | // } 94 | // else { 95 | // setError('No books found'); 96 | // } 97 | // } 98 | // catch (error) { 99 | // console.error('Error fetching books: ', error); 100 | // setError('Falied to fetch books'); 101 | // } 102 | // finally { 103 | // setIsLoading(false); 104 | // } 105 | // }; 106 | 107 | // if (isLoading) { 108 | // return
Loading...
; 109 | // } 110 | // if (error) { 111 | // return
Error: {error}
; 112 | // } 113 | 114 | return ( 115 |
116 | {books.map((book, index) => ( 117 | 118 |
119 | {book.bookTitle} 120 |
121 |

{book.bookTitle}

122 |

{book.authorName}

123 |

{book.category}

124 |
125 |
126 | 127 | ))} 128 | 129 |
130 | ); 131 | } 132 | 133 | export default Newarrivals; 134 | -------------------------------------------------------------------------------- /client/src/Pages/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import {Link} from "react-router-dom" 2 | 3 | const NotFound = () => { 4 | return ( 5 |
6 |
7 |

404

8 |

Maybe you have lost your way. Home

9 |
10 |
11 | ) 12 | } 13 | 14 | export default NotFound -------------------------------------------------------------------------------- /client/src/Pages/PhilosophyPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const PhilosophyPage = () => { 4 | return ( 5 |
6 |

Philosophy Page

7 |
8 | ); 9 | }; 10 | 11 | export default PhilosophyPage; -------------------------------------------------------------------------------- /client/src/Pages/PrivacyPolicy.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | // import styled from 'styled-components'; 3 | import Preloader from '../Components/Preloader'; 4 | 5 | const PrivacyPolicy = () => { 6 | useEffect(() => { 7 | window.scrollTo(0, 0); 8 | }, []); 9 | 10 | return ( 11 | <> 12 | 13 |
14 |
15 |
16 |

Privacy Policy

17 |
18 |

Last updated: August 8, 2024

19 |

Welcome to Online Book Sales - Book4U. We are committed to protecting your personal information and your right to privacy. If you have any questions or concerns about our policy or our practices with regard to your personal information, please contact us supportbook4u@onlinebooksales.com.

20 |
21 |
22 |

Information We Collect

23 |

We collect personal information that you provide to us, such as your name, address, contact information, and payment details. We collect this information when you register for our services, make a purchase, or otherwise interact with our website.

24 |
25 |
26 |

How We Use Your Information

27 |

We use the personal information we collect to process transactions, manage your orders, and communicate with you about our products and services. We may also use this information for customer support, marketing purposes, and to improve our website and services.

28 |
29 |
30 |

Sharing Your Information

31 |

We may share your information in the following situations:

32 |
    33 |
  • With Service Providers: We may share your information with third-party service providers who assist us in operating our website and processing payments.
  • 34 |
  • For Legal Reasons: We may disclose your information if required to do so by law or in response to a valid request from law enforcement or other government authorities.
  • 35 |
  • Business Transfers: In the event of a merger, acquisition, or sale of our company, your information may be transferred as part of the transaction.
  • 36 |
37 |
38 |
39 |

Security of Your Information

40 |

We implement a variety of security measures to protect your personal information from unauthorized access, alteration, disclosure, or destruction. However, no security system is impenetrable, and we cannot guarantee the absolute security of your information.

41 |
42 |
43 |

Contact Us

44 |

If you have any questions or comments about this privacy policy, please contact us at: supportbook4u@onlinebooksales.com.

45 |
46 |
47 |
48 |
49 | 50 | ); 51 | }; 52 | 53 | // const PolicyWrapper = styled.div` 54 | // padding: 20px; 55 | // max-width: 800px; 56 | // margin: 0 auto; 57 | // color: #333; 58 | // line-height: 1.6; 59 | // margin-top: -65px; 60 | 61 | 62 | // h1 { 63 | // font-size: 2.5rem; 64 | // margin-bottom: 20px; 65 | // font-weight: bold; 66 | // color: #002147; 67 | // } 68 | 69 | // h2 { 70 | // font-size: 1.5rem; 71 | // } 72 | 73 | // p { 74 | // margin: 10px 0; 75 | // } 76 | 77 | // ul { 78 | // list-style-type: disc; 79 | // margin-left: 20px; 80 | // } 81 | 82 | // li { 83 | // margin: 10px 0; 84 | // } 85 | // `; 86 | 87 | export default PrivacyPolicy; 88 | -------------------------------------------------------------------------------- /client/src/Pages/Profile.jsx: -------------------------------------------------------------------------------- 1 | function Profile(){ 2 | return( 3 |
4 | user profile page visible after loggedin Only. 5 |
6 | ) 7 | } 8 | 9 | export default Profile; -------------------------------------------------------------------------------- /client/src/Pages/RomancePage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const RomancePage = () => { 4 | return ( 5 |
6 |

Romance Page

7 |
8 | ); 9 | }; 10 | 11 | export default RomancePage; -------------------------------------------------------------------------------- /client/src/Pages/Shop.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Productitem from './ProductItem' 3 | import Preloader from '../Components/Preloader' 4 | 5 | const Shop = () => { 6 | return ( 7 |
8 | 9 | 10 |
11 | ) 12 | } 13 | 14 | export default Shop 15 | -------------------------------------------------------------------------------- /client/src/Pages/Spinner.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import loading from './preLoader.gif' 3 | const Spinner = () => { 4 | return ( 5 |
6 | loading 7 |
8 | ) 9 | } 10 | 11 | export default Spinner 12 | -------------------------------------------------------------------------------- /client/src/Pages/Spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function Spinner() { 4 | return
Loading...
; 5 | } 6 | 7 | export default Spinner; 8 | -------------------------------------------------------------------------------- /client/src/Pages/TechPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TechPage = () => { 4 | return ( 5 |
6 |

Tech Page

7 |
8 | ); 9 | }; 10 | 11 | export default TechPage; -------------------------------------------------------------------------------- /client/src/Pages/ThrillerPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ThrillerPage = () => { 4 | return ( 5 |
6 |

Thriller Page

7 |
8 | ); 9 | }; 10 | 11 | export default ThrillerPage; -------------------------------------------------------------------------------- /client/src/Pages/preLoader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/Pages/preLoader.gif -------------------------------------------------------------------------------- /client/src/Toast/Toast.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect } from 'react'; 2 | import { useToast } from '../Context/ToastContext'; 3 | 4 | function Toast({ position }) { 5 | const { toastList, setToastList } = useToast(); 6 | 7 | const deleteToast = useCallback( 8 | (toastId) => { 9 | const newToastListItem = toastList.filter((toast) => toast.id !== toastId); 10 | setToastList((prevToastList) => newToastListItem); 11 | }, 12 | [toastList, setToastList] 13 | ); 14 | 15 | useEffect(() => { 16 | const interval = setInterval(() => { 17 | if (toastList.length) { 18 | deleteToast(toastList[0].id); 19 | } 20 | }, 3000); 21 | 22 | return () => { 23 | clearInterval(interval); 24 | }; 25 | }, [toastList, deleteToast]); 26 | 27 | const getColor = (type) => { 28 | switch (type) { 29 | case 'success': 30 | return 'bg-green-500'; 31 | case 'error': 32 | return 'bg-red-500'; 33 | case 'warning': 34 | return 'bg-yellow-500'; 35 | case 'info': 36 | return 'bg-blue-500'; 37 | default: 38 | return 'bg-gray-500'; 39 | } 40 | }; 41 | 42 | return ( 43 |
44 | {toastList.map((toast, index) => ( 45 |
49 |
50 |

{toast.title}

51 |

{toast.description}

52 |
53 | 59 |
60 | ))} 61 |
62 | ); 63 | } 64 | 65 | export { Toast }; 66 | -------------------------------------------------------------------------------- /client/src/api/api.js: -------------------------------------------------------------------------------- 1 | export const fetchCartData = async () => { 2 | const response = await fetch('/api/cart'); 3 | const data = await response.json(); 4 | return data; 5 | }; 6 | 7 | export const addItemToCart = async (item) => { 8 | const response = await fetch('/api/cart', { 9 | method: 'POST', 10 | headers: { 11 | 'Content-Type': 'application/json' 12 | }, 13 | body: JSON.stringify(item) 14 | }); 15 | const data = await response.json(); 16 | return data; 17 | }; 18 | 19 | export const removeItemFromCart = async (itemId) => { 20 | const response = await fetch(`/api/cart/${itemId}`, { 21 | method: 'DELETE' 22 | }); 23 | const data = await response.json(); 24 | return data; 25 | }; 26 | -------------------------------------------------------------------------------- /client/src/assets/Giftcard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/Giftcard.jpg -------------------------------------------------------------------------------- /client/src/assets/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/Logo.png -------------------------------------------------------------------------------- /client/src/assets/google_icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/google_icon.jpg -------------------------------------------------------------------------------- /client/src/assets/image/B1Child.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/B1Child.jpeg -------------------------------------------------------------------------------- /client/src/assets/image/Dune.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/Dune.jpg -------------------------------------------------------------------------------- /client/src/assets/image/WhereTheCrawdadsSings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/WhereTheCrawdadsSings.jpg -------------------------------------------------------------------------------- /client/src/assets/image/author1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/author1.jpeg -------------------------------------------------------------------------------- /client/src/assets/image/contactus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/contactus.jpg -------------------------------------------------------------------------------- /client/src/assets/image/klaraAndtheSun.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/klaraAndtheSun.jpg -------------------------------------------------------------------------------- /client/src/assets/image/malibuRising.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/malibuRising.jpg -------------------------------------------------------------------------------- /client/src/assets/image/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/profile.jpg -------------------------------------------------------------------------------- /client/src/assets/image/projectHailMary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/projectHailMary.jpg -------------------------------------------------------------------------------- /client/src/assets/image/theMidnightLibrary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/theMidnightLibrary.jpg -------------------------------------------------------------------------------- /client/src/assets/image/theVanishingHalf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/image/theVanishingHalf.jpg -------------------------------------------------------------------------------- /client/src/assets/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/moon.png -------------------------------------------------------------------------------- /client/src/assets/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/client/src/assets/sun.png -------------------------------------------------------------------------------- /client/src/chatbot.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | const ChatBotEmbed = () => { 4 | useEffect(() => { 5 | const script1 = document.createElement('script'); 6 | script1.innerHTML = ` 7 | window.embeddedChatbotConfig = { 8 | chatbotId: "701s6dCoJK8uMimSHx9Gk", 9 | domain: "www.chatbase.co" 10 | }; 11 | `; 12 | document.body.appendChild(script1); 13 | 14 | const script2 = document.createElement('script'); 15 | script2.src = "https://www.chatbase.co/embed.min.js"; 16 | script2.setAttribute('chatbotId', "701s6dCoJK8uMimSHx9Gk"); 17 | script2.setAttribute('domain', "www.chatbase.co"); 18 | script2.defer = true; 19 | document.body.appendChild(script2); 20 | 21 | return () => { 22 | document.body.removeChild(script1); 23 | document.body.removeChild(script2); 24 | }; 25 | }, []); 26 | 27 | return null; // No visual component needed, just embedding scripts. 28 | }; 29 | 30 | export default ChatBotEmbed; 31 | -------------------------------------------------------------------------------- /client/src/components/AI-BookRecommdation/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # poetry 102 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 103 | # This is especially recommended for binary packages to ensure reproducibility, and is more 104 | # commonly ignored for libraries. 105 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 106 | #poetry.lock 107 | 108 | # pdm 109 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 110 | #pdm.lock 111 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 112 | # in version control. 113 | # https://pdm.fming.dev/#use-with-ide 114 | .pdm.toml 115 | 116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 117 | __pypackages__/ 118 | 119 | # Celery stuff 120 | celerybeat-schedule 121 | celerybeat.pid 122 | 123 | # SageMath parsed files 124 | *.sage.py 125 | 126 | # Environments 127 | .env 128 | .venv 129 | env/ 130 | venv/ 131 | ENV/ 132 | env.bak/ 133 | venv.bak/ 134 | 135 | # Spyder project settings 136 | .spyderproject 137 | .spyproject 138 | 139 | # Rope project settings 140 | .ropeproject 141 | 142 | # mkdocs documentation 143 | /site 144 | 145 | # mypy 146 | .mypy_cache/ 147 | .dmypy.json 148 | dmypy.json 149 | 150 | # Pyre type checker 151 | .pyre/ 152 | 153 | # pytype static type analyzer 154 | .pytype/ 155 | 156 | # Cython debug symbols 157 | cython_debug/ 158 | 159 | # PyCharm 160 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 161 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 162 | # and can be added to the global gitignore or merged into this file. For a more nuclear 163 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 164 | #.idea/ 165 | 166 | ### Python Patch ### 167 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 168 | poetry.toml 169 | 170 | # ruff 171 | .ruff_cache/ 172 | 173 | # LSP config files 174 | pyrightconfig.json 175 | 176 | # End of https://www.toptal.com/developers/gitignore/api/python 177 | test.py 178 | config.toml 179 | logs 180 | main.ipynb 181 | temp.** 182 | README.md.** 183 | gh-md-toc 184 | -------------------------------------------------------------------------------- /client/src/components/AI-BookRecommdation/PREREQUISITES.md: -------------------------------------------------------------------------------- 1 | # PREREQUISITES 2 | 3 | 4 | - Python3 5 | 6 | - Install from [here](https://www.python.org/downloads/). 7 | - 🔐 OpenAI API Key 8 | - Go to the [OpenAI website](https://platform.openai.com/). 9 | - Click the "Sign up for free" button. 10 | - Fill out the registration form with your information and agree to the terms of service. 11 | - Once logged in, click on the "API" tab. 12 | - Follow the instructions to create a new API key. 13 | - Copy the API key and update in the `config.toml` file. 14 | 15 | - 📚 Git 16 | - Install from [here](https://git-scm.com/downloads). 17 | 18 | - ⚡️ PIP 19 | - Install from [here](https://pip.pypa.io/en/stable/installation/#get-pip-py). 20 | 21 | - ✅ Virtual Environment (`venv`) 22 | - Install from [here](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#installing-virtualenv). -------------------------------------------------------------------------------- /client/src/components/AI-BookRecommdation/README.md: -------------------------------------------------------------------------------- 1 | ## Description **📚** 2 | 3 | The AI Book Recommender 🤖 works by interacting with two main components: Goodreads 📖 for fetching book data, and OpenAI's ChatGPT for generating recommendations. 4 | 5 | - **Goodreads :** We're using Goodreads to fetch details about your favorite books 📚, including the book title, author, genres, and synopsis. The script then extracts this data and uses it to generate personalized book recommendations. 6 | 7 | - **OpenAI's ChatGPT :** We're using ChatGPT to generate book recommendations based on the book data fetched from Goodreads . We feed ChatGPT with a conversation context where the user talks about their favorite books (the data fetched from Goodreads). The AI then responds with book recommendations in a conversational manner. 8 | 9 | 10 | ### Getting Started 🚀 11 | 12 | 1. Clone this repository on your local machine. 13 | 14 | ```bash 15 | git clone https://github.com/ArpitaAgrahari/OnlineBookSales.git 16 | ``` 17 | 18 | 2. Navigate to the repository. 19 | 20 | ```bash 21 | cd https://github.com/ArpitaAgrahari/OnlineBookSales.git 22 | ``` 23 | 24 | 3. Create a Python virtual environment and activate it. 25 | 26 | ```bash 27 | python3 -m venv venv 28 | source venv/bin/activate 29 | ``` 30 | 31 | 4. Install the necessary dependencies. 32 | 33 | ```bash 34 | pip install -r requirements.txt 35 | ``` 36 | 37 | 5. Rename the `sample.config.toml` file to `config.toml`: 38 | 39 | ```bash 40 | mv sample.config.toml config.toml 41 | ``` 42 | 43 | 6. Open the `config.toml` file and update your OpenAI API key in the appropriate field. 44 | 45 | 7. Update your favorite books info in the `config.toml` file. Follow the provided format and add the titles and authors of your favorite books. 46 | 47 | ```toml 48 | [books] 49 | favorites = """ 50 | Book Title, Author 51 | Book Title, Author 52 | Book Title, Author 53 | """ 54 | ``` 55 | 56 | 8. Run the `main.py` script: 57 | 58 | ``` 59 | python3 main.py 60 | ``` 61 | 62 | 9. The script gives reading recommendations based on your favorite books. The suggestions are also saved in the `logs` folder. 63 | 64 | ## Configuration ⚙️ 65 | 66 | The configuration file, `config.toml`, has various settings used by the script: 67 | 68 | - OpenAI settings, including the model name, temperature, and API key 🌡️. 69 | - A list of your favorite books 📚. 70 | - Message templates used when interacting with ChatGPT 💌. 71 | -------------------------------------------------------------------------------- /client/src/components/AI-BookRecommdation/requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.12.2 2 | fake_useragent==1.1.3 3 | langchain==0.1.11 4 | requests==2.31.0 5 | tomli==2.0.1 6 | openai==0.27.4 -------------------------------------------------------------------------------- /client/src/components/AI-BookRecommdation/sample.config.toml: -------------------------------------------------------------------------------- 1 | [openai] 2 | api_key = "your-openai-key" 3 | # Choose model_name between gpt-4, gpt-3.5-turbo 4 | model_name = "gpt-4" 5 | temperature = 0.5 6 | 7 | [books] 8 | favorites = """ 9 | Project Hail Mary, Andy Weir 10 | Home Before Dark, Riley Sager 11 | The Invisible Life of Addie LaRue, V.E. Schwab 12 | """ 13 | 14 | [prompts] 15 | system_message = "You are an AI Literary Recommendation Engine, providing book suggestions based on the given book titles, genres, and descriptions." 16 | human_message = """ 17 | %INSTRUCTIONS: 18 | Task: 19 | As an AI literary recommendation engine, your task is to analyze one or more {book titles with their descriptions} provided by the user. 20 | Based on the title, genres, and synopsis of each given book, you should suggest similar lesser-known books that the user might enjoy. 21 | Your recommendations should come from a diverse range of authors and prioritize books that aren't mainstream or widely popular but that are equally good or even better than the provided books. 22 | Your recommendations should not be bound by publication date or a fiction/non-fiction distinction unless specified in the input. 23 | For every recommended book, provide a brief synopsis (up-to 3 lines) and the reason for your choice. 24 | Provide up-to 3 book suggestions. 25 | When suggesting books, use below format. 26 | Title: 27 | Author: 28 | Synopsis: 29 | Reason for Recommendation: 30 | 31 | %BOOK TITLES WITH THEIR DESCRIPTIONS: 32 | """ -------------------------------------------------------------------------------- /client/src/components/Card/Book.jsx: -------------------------------------------------------------------------------- 1 | export default function Book(props) { 2 | return ( 3 |
4 |

{props.genre}

5 |

{props.description}

6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /client/src/components/Card/ProductCard.css: -------------------------------------------------------------------------------- 1 | .fa-heart 2 | { 3 | overflow: hidden; 4 | color: var(--red-color); 5 | } 6 | 7 | .added-to-wishlist-btn 8 | { 9 | position: absolute; 10 | top: 0; 11 | right: 0; 12 | border: none; 13 | border-radius: 50%; 14 | margin: 0.8rem 1.1rem 0 0; 15 | } 16 | 17 | .added-to-wishlist-btn:hover 18 | { 19 | background-color: var(--grey); 20 | } -------------------------------------------------------------------------------- /client/src/components/Card/ProductCard.jsx: -------------------------------------------------------------------------------- 1 | import React,{ useEffect, useState} from 'react' 2 | import { Link, useNavigate } from "react-router-dom" 3 | import axios from "axios" 4 | import jwt_decode from "jwt-decode"; 5 | import './ProductCard.css' 6 | 7 | 8 | export default function ProductCard({ productdetails }) 9 | { 10 | const navigate = useNavigate() 11 | 12 | const { 13 | _id, 14 | bookName, 15 | author, 16 | originalPrice, 17 | discountedPrice, 18 | discountPercent, 19 | imgSrc, 20 | imgAlt, 21 | badgeText, 22 | outOfStock 23 | } = productdetails 24 | 25 | 26 | 27 | 28 | 29 | 30 | return ( 31 | localStorage.setItem(`${_id}`, JSON.stringify(productdetails))} 34 | target="_blank" 35 | rel="noopener noreferrer" 36 | > 37 |
38 | {imgAlt}/ 39 |
40 |
41 |

{bookName}

42 |
43 |
- By  {author}
44 |

Rs. {discountedPrice}   Rs. {originalPrice}    45 | ({discountPercent}% off) 46 |

47 |
48 | 57 |
58 |
59 | {badgeText} 60 |
61 | { 62 | outOfStock && ( 63 |
64 |

Out of Stock

65 |
66 | ) 67 | } 68 |
69 |
70 | 71 | ) 72 | } 73 | 74 | export { ProductCard }; -------------------------------------------------------------------------------- /client/src/components/CustomerProfile/DeliveryLocation.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | const DeliveryLocation = () => { 4 | const [location, setLocation] = useState({ 5 | address: "", 6 | city: "", 7 | postalCode: "", 8 | }); 9 | 10 | const handleLocationChange = (e) => { 11 | setLocation({ ...location, [e.target.name]: e.target.value }); 12 | }; 13 | 14 | const handleSubmit = (e) => { 15 | e.preventDefault(); 16 | console.log("Delivery location added:", location); 17 | }; 18 | 19 | return ( 20 |
21 |

Manage Addresses

22 |
23 |
24 |
25 | 45 | ADD NEW ADDRESS 46 |
47 |
48 | 61 | 72 | 85 | 91 |
92 |
93 | ); 94 | }; 95 | 96 | export default DeliveryLocation; 97 | -------------------------------------------------------------------------------- /client/src/components/CustomerProfile/Orders.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Orders = () => { 4 | const orders = [ 5 | { id: 1, book: 'Book Title 1', date: '2023-09-15', status: 'Delivered' }, 6 | { id: 2, book: 'Book Title 2', date: '2023-08-12', status: 'Shipped' }, 7 | ]; 8 | 9 | return ( 10 |
11 |

My Orders

12 |
    13 | {orders.map((order) => ( 14 |
  • 15 |

    {order.book}

    16 |

    Date: {order.date}

    17 |

    Status: {order.status}

    18 |
  • 19 | ))} 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default Orders; 26 | -------------------------------------------------------------------------------- /client/src/components/CustomerProfile/Pancard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | const Pancard = () => { 4 | const [formData, setFormData] = useState({ 5 | pancardNumber: "", 6 | fullName: "", 7 | pancardFile: null, 8 | termsAccepted: false, 9 | }); 10 | 11 | const handleChange = (e) => { 12 | const { name, value, type, checked, files } = e.target; 13 | if (type === "checkbox") { 14 | setFormData({ ...formData, [name]: checked }); 15 | } else if (type === "file") { 16 | setFormData({ ...formData, [name]: files[0] }); 17 | } else { 18 | setFormData({ ...formData, [name]: value }); 19 | } 20 | }; 21 | 22 | const handleSubmit = (e) => { 23 | e.preventDefault(); 24 | console.log("Form data submitted:", formData); 25 | }; 26 | 27 | return ( 28 |
29 |

PAN Card Information

30 |
31 |
32 |
33 |
34 | 42 |
43 |
44 | 52 |
53 |
54 | 57 | 64 |
65 |
66 | 74 | 81 |
82 |
83 | 89 |
90 |
91 |
92 |
93 |
94 | ); 95 | }; 96 | 97 | export default Pancard; -------------------------------------------------------------------------------- /client/src/components/GoToTop.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import styled from "styled-components"; 3 | import { FaChevronUp } from "react-icons/fa"; 4 | 5 | const GoToTop = () => { 6 | const [isVisible, setIsVisible] = useState(false); 7 | 8 | useEffect(() => { 9 | const handleScroll = () => { 10 | const currentScrollY = window.scrollY; 11 | 12 | setIsVisible(currentScrollY > 200); // Change 200 to your desired scroll distance 13 | }; 14 | 15 | window.addEventListener("scroll", handleScroll); 16 | 17 | return () => { 18 | window.removeEventListener("scroll", handleScroll); 19 | }; 20 | }, []); 21 | 22 | const goToTop = () => { 23 | window.scrollTo({ top: 0, left: 0, behavior: "smooth" }); 24 | }; 25 | 26 | return ( 27 | <> 28 | {isVisible && ( 29 | 30 |
31 | 32 | 33 |
34 |
35 | )} 36 | 37 | ); 38 | }; 39 | 40 | const Wrapper = styled.div` 41 | display: flex; 42 | justify-content: center; 43 | align-items: center; 44 | position: fixed; 45 | bottom: 6rem; 46 | right: 2rem; 47 | color: white; 48 | background: #318CE7; 49 | width: 48px; 50 | height: 48px; 51 | border-radius: 50%; 52 | cursor: pointer; 53 | transition: transform 0.3s ease; 54 | 55 | &:hover { 56 | background: #0269b8; 57 | transform: scale(1.1); 58 | } 59 | 60 | .icon-container { 61 | display: flex; 62 | flex-direction: column; 63 | align-items: center; 64 | justify-content: center; 65 | } 66 | 67 | .top-btn--icon { 68 | color: white; 69 | transition: transform 0.3s ease; 70 | } 71 | 72 | .top-btn--icon.double { 73 | margin-top: -8px; 74 | } 75 | 76 | &:hover .top-btn--icon { 77 | transform: translateY(-2px); 78 | } 79 | 80 | @media (max-width: 481px) { 81 | bottom: 68px; 82 | left: 86%; 83 | } 84 | `; 85 | 86 | export default GoToTop; -------------------------------------------------------------------------------- /client/src/components/GoogleLogin.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import googleIcon from '../assets/google_icon.jpg' 3 | const GoogleLogin = () => { 4 | const handleGoogleLogin = () => { 5 | // Redirect to the backend Google OAuth route 6 | // Make sure to create the google client id or else nothing will work 7 | // NK-Works (says): I have not worked on the backend for it so i can't guarantee the working of the login functionality 8 | window.location.href = 'http://localhost:8080/auth/google'; 9 | }; 10 | 11 | return ( 12 | 13 | 20 | ); 21 | }; 22 | 23 | export default GoogleLogin; 24 | -------------------------------------------------------------------------------- /client/src/components/GoogleTranslate.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | 3 | const GoogleTranslate = () => { 4 | useEffect(() => { 5 | window.googleTranslateInit = () => { 6 | if (!window.google?.translate?.TranslateElement) { 7 | setTimeout(window.googleTranslateInit, 100); 8 | } else { 9 | new window.google.translate.TranslateElement({ 10 | pageLanguage: 'en', 11 | includedLanguages: 'en,hi,pa,sa,mr,ur,bn,es,ja,ko,zh-CN,es,nl,fr,de,it,ta,te', 12 | layout: window.google.translate.TranslateElement.InlineLayout.HORIZONTAL, 13 | defaultLanguage: 'en', 14 | autoDisplay: false, 15 | }, 'google_element'); 16 | } 17 | }; 18 | 19 | const loadGoogleTranslateScript = () => { 20 | if (!document.getElementById("google_translate_script")) { 21 | const script = document.createElement("script"); 22 | script.type = "text/javascript"; 23 | script.src = "https://translate.google.com/translate_a/element.js?cb=googleTranslateInit"; 24 | script.id = "google_translate_script"; 25 | script.onerror = () => console.error('Error loading Google Translate script'); 26 | document.body.appendChild(script); 27 | } 28 | }; 29 | 30 | loadGoogleTranslateScript(); 31 | 32 | if (window.google && window.google.translate) { 33 | window.googleTranslateInit(); 34 | } 35 | 36 | return () => { 37 | // Cleanup logic if necessary 38 | }; 39 | }, []); 40 | 41 | return ( 42 |
43 | 120 |
121 | ); 122 | }; 123 | 124 | export default GoogleTranslate; 125 | -------------------------------------------------------------------------------- /client/src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import { 2 | PaperAirplaneIcon, 3 | SunIcon, 4 | Bars3Icon, 5 | ShoppingCartIcon, 6 | HeartIcon, 7 | // XMarkIcon, 8 | } from "@heroicons/react/24/outline"; 9 | import { useState } from "react"; 10 | import { useNavigate } from "react-router-dom"; 11 | 12 | function Header(){ 13 | const [ toggleMenu, setToggleMenu ] = useState(false) 14 | const navigate = useNavigate(); 15 | 16 | const navItems = [ 17 | { 18 | "title": "Home", 19 | "url": "/", 20 | }, 21 | { 22 | "title": "Book Store", 23 | "url": "/", 24 | }, 25 | { 26 | "title": "Order", 27 | "url": "/", 28 | }, 29 | { 30 | "title": "Profile", 31 | "url": "/profile", 32 | }, 33 | ] 34 | return( 35 |
36 | 104 |
105 | ) 106 | } 107 | 108 | export default Header; -------------------------------------------------------------------------------- /client/src/components/HomePage.js: -------------------------------------------------------------------------------- 1 | function HomePage(){ 2 | return( 3 |
Home Page
4 | ) 5 | } 6 | 7 | export default HomePage; -------------------------------------------------------------------------------- /client/src/components/NavBar/ScrollProgressBar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { styled } from '@mui/material/styles'; 3 | 4 | const ProgressBar = styled('div')({ 5 | position: 'fixed', 6 | top: 0, 7 | left: 0, 8 | height: '5px', 9 | background: 'linear-gradient(90deg, #00BFFF, #FFD700)', // Gradient from blue to yellow 10 | zIndex: 1200, 11 | boxShadow: '0 0 10px #00BFFF, 0 0 20px #FFD700', // Glowing effect with blue and yellow 12 | transition: 'width 0.2s ease-out', 13 | borderRadius: '2px', // Rounded edges 14 | transform: 'translateZ(0)', // Fix any potential rendering issues 15 | animation: 'pulse 2s infinite', // Pulse animation for extra glow effect 16 | scrollBehavior:'smooth' 17 | }); 18 | 19 | const ScrollProgressBar = () => { 20 | const [scrollProgress, setScrollProgress] = useState(0); 21 | 22 | const updateProgressBar = () => { 23 | const scrollTop = window.pageYOffset || document.documentElement.scrollTop; 24 | const scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight; 25 | const progress = (scrollTop / scrollHeight) * 100; 26 | setScrollProgress(progress); 27 | }; 28 | 29 | useEffect(() => { 30 | window.addEventListener('scroll', updateProgressBar); 31 | return () => window.removeEventListener('scroll', updateProgressBar); 32 | }, []); 33 | 34 | return ; 35 | }; 36 | 37 | // CSS keyframe animation for pulse effect 38 | const styles = document.createElement('style'); 39 | styles.innerHTML = ` 40 | @keyframes pulse { 41 | 0% { 42 | box-shadow: 0 0 5px #00BFFF, 0 0 10px #FFD700; 43 | } 44 | 50% { 45 | box-shadow: 0 0 20px #00BFFF, 0 0 30px #FFD700; 46 | } 47 | 100% { 48 | box-shadow: 0 0 5px #00BFFF, 0 0 10px #FFD700; 49 | } 50 | } 51 | `; 52 | document.head.appendChild(styles); 53 | 54 | export default ScrollProgressBar; 55 | -------------------------------------------------------------------------------- /client/src/components/Preloader.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | // import './Preloader.css'; 3 | 4 | const Preloader = () => { 5 | const [isVisible, setIsVisible] = useState(true); 6 | const [fadeOut, setFadeOut] = useState(false); 7 | 8 | useEffect(() => { 9 | const timer = setTimeout(() => { 10 | setFadeOut(true); 11 | setTimeout(() => setIsVisible(false), 300); 12 | }, 2500); 13 | 14 | return () => clearTimeout(timer); 15 | }, []); 16 | 17 | return ( 18 | isVisible && ( 19 |
20 | 24 |
25 | ) 26 | ); 27 | }; 28 | 29 | export default Preloader; 30 | -------------------------------------------------------------------------------- /client/src/components/Product.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Product() { 4 | return ( 5 |
Product 6 | 7 |
8 | ) 9 | } 10 | 11 | export default Product -------------------------------------------------------------------------------- /client/src/components/Profile/ProfilePage.js: -------------------------------------------------------------------------------- 1 | function ProfilePage(){ 2 | return( 3 |
4 |

Profile page

5 | 6 |
7 | 8 | ) 9 | } 10 | 11 | export default ProfilePage -------------------------------------------------------------------------------- /client/src/components/SearchBar.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React, { useState } from "react"; 4 | import { useNavigate } from "react-router-dom"; 5 | // Example data list for suggestions 6 | const data = [ 7 | { name: "Romance", url: "/romance" }, 8 | { name:"Action", url: "/action" }, 9 | { name: "Thriller", url: "/thriller" }, 10 | { name: "Fiction", url: "/fiction" }, 11 | { name: "Technology", url: "/tech" }, 12 | { name: "Philosophy", url: "philosophy" }, 13 | { name: "Manga", url: "/manga" }, 14 | // { name: "Honeydew", url: "/books/romance/honeydew" }, 15 | ]; 16 | 17 | const SearchBar = () => { 18 | const [query, setQuery] = useState(""); // For storing user input 19 | const [filteredData, setFilteredData] = useState([]); // For storing filtered results 20 | const [showSuggestions, setShowSuggestions] = useState(false); // Toggle suggestions 21 | const navigate = useNavigate(); // Hook to navigate to different routes 22 | 23 | // Function to handle input change 24 | const handleInputChange = (e) => { 25 | const userInput = e.target.value; 26 | setQuery(userInput); 27 | 28 | if (userInput) { 29 | const suggestions = data.filter((item) => 30 | item.name.toLowerCase().includes(userInput.toLowerCase()) // Access the name field 31 | ); 32 | setFilteredData(suggestions); 33 | setShowSuggestions(true); 34 | } else { 35 | setFilteredData([]); 36 | setShowSuggestions(false); 37 | } 38 | }; 39 | 40 | // Function to handle suggestion click 41 | const handleSuggestionClick = (suggestion) => { 42 | setQuery(suggestion); 43 | setShowSuggestions(false); // Hide suggestions after selection 44 | navigate(suggestion.url); 45 | }; 46 | 47 | 48 | return ( 49 |
50 | 63 | {showSuggestions && filteredData.length > 0 && ( 64 |
    75 | {filteredData.map((suggestion, index) => ( 76 |
  • handleSuggestionClick(suggestion)} 79 | style={{ 80 | padding: "10px", 81 | cursor: "pointer", 82 | backgroundColor: "#f9f9f9", 83 | borderBottom: "1px solid #ccc", 84 | }} 85 | > 86 | {suggestion.name} {/* Render the name, not the whole object */} 87 |
  • 88 | ))} 89 |
90 | )} 91 | 92 |
93 | ); 94 | }; 95 | export default SearchBar; 96 | -------------------------------------------------------------------------------- /client/src/components/Trending.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import BannerCard from "../Pages/BannerCard"; 3 | import axios from "axios"; 4 | 5 | function Trending() { 6 | const apiKey = process.env.REACT_APP_NY_TIMES_APIKEY; // API key 7 | const [books, setBooks] = useState([]); 8 | 9 | // Hardcoded fallback or initial state books 10 | const fallbackBooks = [ 11 | { 12 | bookTitle: "Fourth Wing", 13 | authorName: "Rebecca Yarros", 14 | imageURL: 15 | "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1701980900i/61431922.jpg", 16 | category: "Romantasy", 17 | }, 18 | { 19 | bookTitle: "The Maid", 20 | authorName: "Nita Prose", 21 | imageURL: 22 | "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1643228739i/55196813.jpg", 23 | category: "Fiction", 24 | }, 25 | { 26 | bookTitle: "Check & Mate", 27 | authorName: "Ali Hazelwood", 28 | imageURL: 29 | "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1678378842i/60683957.jpg", 30 | category: "YOUNG ADULT FANTASY & SCIENCE FICTION", 31 | }, 32 | { 33 | bookTitle: "Holly", 34 | authorName: "Stephen King", 35 | imageURL: 36 | "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1674418461i/65916344.jpg", 37 | category: "Horror", 38 | }, 39 | { 40 | bookTitle: "Tomorrow and Tomorrow", 41 | authorName: "Gabrielle Zevin ", 42 | imageURL: 43 | "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1636978687i/58784475.jpg", 44 | category: "Fiction", 45 | }, 46 | { 47 | bookTitle: "Hidden Pictures", 48 | authorName: "Jason Rekulak", 49 | imageURL: 50 | "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1635260162i/58724923.jpg", 51 | category: "Horror", 52 | }, 53 | { 54 | bookTitle: "Holly", 55 | authorName: "Stephen King", 56 | imageURL: 57 | "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/1674418461i/65916344.jpg", 58 | category: "Horror", 59 | }, 60 | ]; 61 | 62 | // Fetch trending books from the NY Times API 63 | const fetchTrendingBooks = async () => { 64 | try { 65 | const response = await axios.get( 66 | `https://api.nytimes.com/svc/books/v3/lists/overview.json?api-key=${apiKey}` 67 | ); 68 | 69 | console.log(response.data.results); 70 | 71 | const nyTimesBooks = response.data.results.lists 72 | .slice(0, 2) // Limiting to first 2 lists 73 | .flatMap((list) => 74 | list.books.map((book) => ({ 75 | bookTitle: book.title, 76 | authorName: book.author, 77 | imageURL: book.book_image, 78 | category: list.list_name, // Using list name as category 79 | })) 80 | ).slice(0, 7); 81 | 82 | 83 | setBooks(nyTimesBooks); 84 | } catch (error) { 85 | console.error("Error fetching books:", error); 86 | } 87 | }; 88 | 89 | useEffect(() => { 90 | console.log(`API key is ${apiKey}`); 91 | fetchTrendingBooks(); 92 | }, [apiKey]); 93 | 94 | return ( 95 |
96 | 0 ? books : fallbackBooks} /> 97 |
98 | ); 99 | } 100 | 101 | export default Trending; 102 | -------------------------------------------------------------------------------- /client/src/components/index.js: -------------------------------------------------------------------------------- 1 | import Header from "./Header/Header.js"; 2 | import HomePage from "./HomePage"; 3 | import Product from "./Product.jsx" 4 | import Footer from "./Footer/Footer.jsx" 5 | import Navbar from "./NavBar/NavBar.jsx"; 6 | import Profile from "./CustomerProfile/Profile.jsx"; 7 | 8 | export { 9 | Header, 10 | Profile, 11 | HomePage, 12 | Product, 13 | Footer, 14 | Navbar, 15 | } -------------------------------------------------------------------------------- /client/src/components/temp.js: -------------------------------------------------------------------------------- 1 | 2 | import Profile from "./CustomerProfile/Profile.jsx"; 3 | import Product from "./Product.jsx" 4 | import Footer from "./Footer/Footer.jsx" 5 | import Navbar from "./NavBar/NavBar.jsx" 6 | export { 7 | Profile, 8 | Product, 9 | Footer, 10 | Navbar 11 | } -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | overflow-x: hidden; 9 | overflow-y: auto; 10 | } 11 | 12 | /* Custom Scrollbar Styles */ 13 | ::-webkit-scrollbar { 14 | width: 7px; /* Width of the scrollbar */ 15 | z-index: 9999; 16 | } 17 | 18 | ::-webkit-scrollbar-track { 19 | background: rgb(252, 252, 253); /* Color of the track (the empty space behind the scrollbar) */ 20 | } 21 | 22 | ::-webkit-scrollbar-thumb { 23 | background: linear-gradient(45deg, #abffee, #3d00a6, #000e17); /* Gradient color combination */ 24 | border-radius: 10px; /* Rounded corners */ 25 | } 26 | 27 | ::-webkit-scrollbar-thumb:hover { 28 | background: linear-gradient(45deg, #000e17, #3d00a6, #abffee); /* Inverted gradient color on hover */ 29 | } 30 | 31 | code { 32 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 33 | monospace; 34 | } 35 | 36 | /* Custom keyframes for toast animation */ 37 | @keyframes toast-from-right { 38 | from { 39 | transform: translateX(100%); 40 | } 41 | to { 42 | transform: translateX(0%); 43 | } 44 | } 45 | 46 | @tailwind base; 47 | @tailwind components; 48 | @tailwind utilities; 49 | 50 | /* .css-18xdh7s-MuiButtonBase-root-MuiIconButton-root { 51 | background-color: rgb(255, 255, 255) !important; 52 | } */ 53 | 54 | .MuiIconButton-root svg { 55 | fill: white /* Set the initial color of the icon */ 56 | } -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { AuthContextProvider } from './Context/AuthContext.js'; 6 | import { ToastContextProvider } from './Context/ToastContext.js'; 7 | import reportWebVitals from './reportWebVitals'; 8 | 9 | 10 | 11 | 12 | // const router = createBrowserRouter([ 13 | // { 14 | // path: '/', 15 | // element: , 16 | // children: [ 17 | // { 18 | // path: '/', 19 | // element: , 20 | // }, 21 | // { 22 | // path: '/cart', 23 | // element: , 24 | // }, 25 | // { 26 | // path: '/orders', 27 | // element: , 28 | // }, 29 | // { 30 | // path: '/profile', 31 | // element: , 32 | // }, 33 | // { 34 | // path: '/signup', 35 | // element: , 36 | // }, 37 | // { 38 | // path: '/login', 39 | // element: , 40 | // }, 41 | // { 42 | // path: '/product', 43 | // element: , 44 | // }, 45 | // { 46 | // path: '/wishlist', 47 | // element: , 48 | // }, 49 | // ], 50 | // }, 51 | // ]); 52 | 53 | const root = ReactDOM.createRoot(document.getElementById('root')); 54 | root.render( 55 | 56 | 57 | 58 | 59 | 60 | ); 61 | 62 | // If you want to start measuring performance in your app, pass a function 63 | // to log results (for example: reportWebVitals(console.log)) 64 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 65 | reportWebVitals(); 66 | -------------------------------------------------------------------------------- /client/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /client/src/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cors = require('cors'); 3 | const passport = require('passport'); 4 | const GoogleStrategy = require('passport-google-oauth20').Strategy; 5 | const session = require('express-session'); 6 | const mongoose = require('mongoose'); 7 | require('dotenv').config(); 8 | 9 | const User = require('../../models/customerSchema'); // Import User model 10 | 11 | const app = express(); 12 | const port = process.env.PORT || 5000; // Use port from .env or fallback to 5000 13 | 14 | // Enable CORS to allow requests from the frontend 15 | app.use(cors({ 16 | origin: 'http://localhost:3000', // Frontend URL 17 | credentials: true // To allow cookies (for session management) 18 | })); 19 | 20 | // MongoDB connection 21 | mongoose.connect(process.env.MONGO_URL, { 22 | useNewUrlParser: true, 23 | useUnifiedTopology: true, 24 | }).then(() => console.log('MongoDB connected')) 25 | .catch(err => console.error(err)); 26 | 27 | // Setup session 28 | app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: true })); 29 | 30 | // Initialize Passport and session 31 | app.use(passport.initialize()); 32 | app.use(passport.session()); 33 | 34 | // Passport configuration for Google OAuth 35 | passport.use(new GoogleStrategy({ 36 | clientID: process.env.GOOGLE_CLIENT_ID, 37 | clientSecret: process.env.GOOGLE_CLIENT_SECRET, 38 | callbackURL: "http://localhost:5000/auth/google/callback" 39 | }, 40 | async (accessToken, refreshToken, profile, done) => { 41 | try { 42 | // Check if the user exists in the database 43 | let user = await User.findOne({ googleId: profile.id }); 44 | 45 | if (user) { 46 | // If user exists, return the user 47 | return done(null, user); 48 | } else { 49 | // If user doesn't exist, create a new user 50 | user = new User({ 51 | googleId: profile.id, 52 | name: profile.displayName, 53 | email: profile.emails[0].value, 54 | avatar: profile.photos[0].value 55 | }); 56 | await user.save(); // Save the new user in the database 57 | return done(null, user); 58 | } 59 | } catch (err) { 60 | console.error(err); 61 | return done(err, null); 62 | } 63 | })); 64 | 65 | // Serialize and deserialize user for session management 66 | passport.serializeUser((user, done) => { 67 | done(null, user.id); 68 | }); 69 | 70 | passport.deserializeUser(async (id, done) => { 71 | try { 72 | const user = await User.findById(id); 73 | done(null, user); 74 | } catch (err) { 75 | done(err, null); 76 | } 77 | }); 78 | 79 | // Route to initiate Google OAuth flow 80 | app.get('/auth/google', 81 | passport.authenticate('google', { scope: ['profile', 'email'] }) 82 | ); 83 | 84 | // Google OAuth callback route 85 | app.get('/auth/google/callback', 86 | passport.authenticate('google', { failureRedirect: '/login' }), // Change to /login or /auth/failure 87 | (req, res) => { 88 | console.log('Google authentication successful for user:', req.user); 89 | res.redirect('http://localhost:3000'); 90 | } 91 | ); 92 | 93 | // Endpoint to check the current user 94 | app.get('/auth/current_user', (req, res) => { 95 | res.send(req.user); // Send the current authenticated user 96 | }); 97 | 98 | // Sample order data 99 | const orders = [ 100 | { id: 1, item: 'Item 1' }, 101 | { id: 2, item: 'Item 2' }, 102 | { id: 3, item: 'Item 3' } 103 | ]; 104 | 105 | // Endpoint to fetch orders 106 | app.get('/api/orders', (req, res) => { 107 | res.json(orders); 108 | }); 109 | 110 | // Start the server 111 | app.listen(port, () => { 112 | console.log(`Server is running on http://localhost:${port}`); 113 | }); 114 | -------------------------------------------------------------------------------- /client/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /client/src/validations/validation.js: -------------------------------------------------------------------------------- 1 | import * as yup from "yup"; 2 | 3 | export const loginValidation = yup.object().shape({ 4 | email: yup 5 | .string() 6 | .email("Email format invalid") 7 | .required("Email is required"), 8 | password: yup.string().required("Password is required"), 9 | }); 10 | 11 | export const registerValidation = yup.object().shape({ 12 | name: yup.string().required("Name is required"), 13 | email: yup 14 | .string() 15 | .email("Email format invalid") 16 | .required("Email is required"), 17 | password: yup 18 | .string() 19 | .min(6, "Minimum length should be 6") 20 | .required("Password is required"), 21 | confirmPassword: yup 22 | .string() 23 | .oneOf([yup.ref("password"), null], "Passwords must match") 24 | .required("Confirm password is required"), 25 | }); 26 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: 'selector', 4 | content: [ 5 | './index.html', 6 | './src/**/*.{js,ts,jsx,tsx}' 7 | ], 8 | theme: { 9 | extend: { 10 | colors: { 11 | primary: { 12 | DEFAULT: '#FF592C;', 13 | purple:'#9757D7', 14 | pink: '#EF466F', 15 | green:'#45B36B', 16 | 17 | }, 18 | boxShadow: { 19 | custom: '0 4px 6px rgba(5, 205, 208, 0.752)', 20 | }, 21 | secondary:{ 22 | DEFAULT:'#3772FF', 23 | pink: '#E4D7CF', 24 | yellow: '#FFD166', 25 | purple:'#CDB4DB', 26 | }, 27 | gray: { 28 | DEFAULT:'#ffffff', 29 | 100:'#FCFCFD', 30 | 200: '#F4F5F6', 31 | 300:'#E6E8EC', 32 | 400:'#B1B5C4', 33 | 500:'#777E91', 34 | 600:'#353945', 35 | 700:'#23262F', 36 | 800:'#141416' 37 | } 38 | }, 39 | fontFamily: { 40 | poppins: ['Poppins' ,'sans-sarif'], 41 | dmsans: ['DM Sans', 'sans-sarif'] 42 | } 43 | }, 44 | screens: { 45 | xs: '480px', 46 | sm: '640px', 47 | md: '768px', 48 | lg: '1024px', 49 | xl: '1280px', 50 | '2xl': '1440px', 51 | '3xl': '1780px', 52 | '4xl': '2160px', // only need to control product grid mode in ultra 4k device 53 | }, 54 | }, 55 | plugins: [], 56 | } -------------------------------------------------------------------------------- /controllers/adminController.js: -------------------------------------------------------------------------------- 1 | const catchAsyncErrors = require("../middlewares/catchAsyncErrors"); 2 | const Feedback = require("../models/feebackSchema"); 3 | const errorHandler = require("../utils/errorHandler"); 4 | const responseHandler = require("../utils/responseHandler"); 5 | 6 | //get all feedbacks 7 | exports.retriveFeedbacks = catchAsyncErrors(async (req, res, next) => { 8 | const feedbacks = await Feedback.find(); 9 | 10 | return res.status(200).send({ 11 | response: { 12 | data: { feedbacks }, 13 | title: "Feedbacks Retrieved", 14 | message: "Feedbacks Retrieved Successfully!", 15 | status: 200, 16 | }, 17 | errors: {}, 18 | }); 19 | }); 20 | 21 | //get feedback by id 22 | exports.retriveFeedbacksById = catchAsyncErrors(async (req, res, next) => { 23 | const feedback = await Feedback.findById(req.params.id); 24 | 25 | if (!feedback) { 26 | return res 27 | .status(404) 28 | .send( 29 | errorHandler(404, "Invalid request", "Feedback not found for given id") 30 | ); 31 | } 32 | 33 | return res.status(200).send({ 34 | response: { 35 | data: { feedback }, 36 | title: "Feedbacks Retrieved", 37 | message: "Feedbacks Retrieved Successfully!", 38 | status: 200, 39 | }, 40 | errors: {}, 41 | }); 42 | }); 43 | 44 | //update feedback 45 | exports.updateFeeback = catchAsyncErrors(async (req, res, next) => { 46 | let feedback; 47 | feedback = await Feedback.findById(req.params.id); 48 | if (!feedback) { 49 | return res 50 | .status(404) 51 | .send( 52 | errorHandler(404, "Invalid request", "Feedback not found for given id") 53 | ); 54 | } 55 | feedback = await Feedback.findByIdAndUpdate(req.params.id, req.body, { 56 | new: true, 57 | runValidators: true, 58 | useFindAndModify: false, 59 | }); 60 | return res.status(200).send({ 61 | response: { 62 | data: { feedback }, 63 | title: "Feedbacks Retrieved", 64 | message: "Feedbacks Retrieved Successfully!", 65 | status: 200, 66 | }, 67 | errors: {}, 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /controllers/cartController.js: -------------------------------------------------------------------------------- 1 | 2 | const Cart = require('../models/cartSchema'); 3 | const catchAsyncErrors = require('../middlewares/catchAsyncErrors'); 4 | 5 | 6 | // Add to cart => /customer/cart/add-product 7 | exports.addTocart = catchAsyncErrors(async (req, res, next) => { 8 | let cartItems = req.body.cartItems; 9 | const userId=req.body.user._id ; 10 | 11 | let cart = await Cart.findOne({ user:userId}); 12 | if (cart) { 13 | // Ensure cartItems is an array 14 | if (!Array.isArray(cartItems)) { 15 | cartItems = [cartItems]; 16 | } 17 | // Push new items to cartItems array 18 | cart.cartItems.push(...cartItems); 19 | // Save the cart 20 | await cart.save(); 21 | // Send response 22 | res.status(200).json({ success: true, length: cart.cartItems.length }); 23 | }else { 24 | const newCart = await Cart.create({ 25 | user: userId, 26 | cartItems, 27 | }); 28 | res.status(200).json({ success: true,length:newCart.cartItems.length }); 29 | } 30 | }); 31 | 32 | 33 | // Get cart items => /customer/cart 34 | exports.getCartItems = catchAsyncErrors(async (req, res, next) => { 35 | const cart = await Cart.findOne({ user: req.body.user._id }); 36 | let cartItems =[]; 37 | if(cart){ 38 | cartItems = cart.cartItems; 39 | } 40 | res.status(200).json({ success: true, cartItems }); 41 | } 42 | ); //getCartItems 43 | 44 | 45 | 46 | // Delete cart item => /customer/cart/remove-product 47 | exports.deleteCartItem = catchAsyncErrors(async (req, res, next) => { 48 | const cart = await Cart.findOne({ user: req.body.user._id }); 49 | const productId=req.body.productId; 50 | const cartItems = cart.cartItems; 51 | const index = cartItems.findIndex(item => item.product.toString() === productId.toString()); 52 | cartItems.splice(index, 1); 53 | await cart.save(); 54 | res.status(200).json({ success: true,length:cart.cartItems.length }); 55 | });//deleteCartItem -------------------------------------------------------------------------------- /controllers/mailcontroller.js: -------------------------------------------------------------------------------- 1 | 2 | const nodemailer = require('nodemailer'); 3 | 4 | exports.sendContactEmail = async (req, res) => { 5 | const { name, email, message } = req.body; 6 | 7 | try { 8 | 9 | const transporter = nodemailer.createTransport({ 10 | service: 'gmail', 11 | auth: { 12 | user: process.env.SMTP_EMAIL, 13 | pass: process.env.SMTP_PASSWORD, 14 | }, 15 | }); 16 | 17 | 18 | const mailOptions = { 19 | from: email, 20 | to: "1234trishasahu@gmail.com", 21 | subject: `Contact Us Message from ${name}`, 22 | text: message, 23 | html: ` 24 |

You have received a new message from the Contact Us form:

25 |

Contact Details:

26 |
    27 |
  • Name: ${name}
  • 28 |
  • Email: ${email}
  • 29 |
30 |

Message:

31 |

${message}

32 | `, // HTML body 33 | }; 34 | 35 | await transporter.sendMail(mailOptions); 36 | 37 | return res.status(200).json({ success: true, message: 'Message sent successfully!' }); 38 | } catch (error) { 39 | console.error('Error sending email:', error); 40 | 41 | return res.status(500).json({ success: false, message: 'Error sending email' }); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /controllers/orderController.js: -------------------------------------------------------------------------------- 1 | const Order = require("../models/orderSchema.js"); 2 | const Product = require("../models/productSchema.js"); 3 | const errorHandler = require("../utils/errorHandler"); 4 | const responseHandler = require("../utils/responseHandler"); 5 | const catchAsyncErrors = require("../middlewares/catchAsyncErrors.js"); 6 | 7 | // CREATE NEW ORDER 8 | exports.newOrder = catchAsyncErrors(async (req, res, next) => { 9 | const { 10 | shippingInfo, 11 | orderItems, 12 | paymentInfo, 13 | itemsPrice, 14 | taxPrice, 15 | shippingPrice, 16 | totalPrice, 17 | } = req.body; 18 | 19 | const order = await Order.create({ 20 | shippingInfo, 21 | orderItems, 22 | paymentInfo, 23 | itemsPrice, 24 | taxPrice, 25 | shippingPrice, 26 | totalPrice, 27 | paidAt: Date.now(), 28 | user: req.user._id, 29 | }); 30 | 31 | return res.status(200).send({ 32 | response: { 33 | data: { order }, 34 | title: "Customer Fetched", 35 | message: "Customer Fetched Successfully!", 36 | status: 200, 37 | }, 38 | errors: {}, 39 | }); 40 | }); 41 | 42 | // GET SINGLE ORDER 43 | exports.getSingleOrder = catchAsyncErrors(async (req, res, next) => { 44 | const order = await Order.findById(req.params.id).populate( 45 | "user", 46 | "name email" 47 | ); 48 | 49 | if (!order) { 50 | return res 51 | .status(404) 52 | .send(errorHandler(404, "Not Found", "Order Not Found")); 53 | } 54 | 55 | return res.status(200).send({ 56 | response: { 57 | data: { order }, 58 | title: "Customer Fetched", 59 | message: "Customer Fetched Successfully!", 60 | status: 200, 61 | }, 62 | errors: {}, 63 | }); 64 | }); 65 | 66 | // GET LOGGED IN CUSTOMER ORDERS 67 | exports.myOrders = catchAsyncErrors(async (req, res, next) => { 68 | const orders = await Order.find({ user: req.user._id }); 69 | 70 | return res.status(200).send({ 71 | response: { 72 | data: { orders }, 73 | title: "Customer Fetched", 74 | message: "Customer Fetched Successfully!", 75 | status: 200, 76 | }, 77 | errors: {}, 78 | }); 79 | }); 80 | 81 | // UPDATE ORDER STATUS -- ADMIN 82 | exports.updateOrder = catchAsyncErrors(async (req, res, next) => { 83 | const order = await Order.findById(req.params.id); 84 | 85 | if (!order) { 86 | return res 87 | .status(404) 88 | .send(errorHandler(404, "Not Found", "Order Not Found")); 89 | } 90 | 91 | if (order.orderStatus === "Delivered") { 92 | return res 93 | .status(404) 94 | .send(errorHandler(404, "Order Delivered", "This order has already been deliverers")); 95 | } 96 | 97 | if (req.body.status === "Shipped") { 98 | order.orderItems.forEach(async (o) => { 99 | await updateStock(o.product, o.quantity); 100 | }); 101 | } 102 | order.orderStatus = req.body.status; 103 | 104 | if (req.body.status === "Delivered") { 105 | order.deliveredAt = Date.now(); 106 | } 107 | 108 | await order.save({ validateBeforeSave: false }); 109 | return res.status(200).send({ 110 | response: { 111 | data: {}, 112 | title: "Order Updated", 113 | message: "Order Status Updated Successfully!", 114 | status: 200, 115 | }, 116 | errors: {}, 117 | }); 118 | }); 119 | 120 | async function updateStock(id, quantity) { 121 | const product = await Product.findById(id); 122 | 123 | product.Stock -= quantity; 124 | 125 | await product.save({ validateBeforeSave: false }); 126 | } 127 | 128 | // DELETE ORDER -- ADMIN 129 | exports.deleteOrder = catchAsyncErrors(async (req, res, next) => { 130 | const order = await Order.findById(req.params.id); 131 | 132 | if (!order) { 133 | return res 134 | .status(404) 135 | .send(errorHandler(404, "Not Found", "Order Not Found")); 136 | } 137 | 138 | await order.deleteOne(); 139 | 140 | res.status(200).json({ 141 | success: true, 142 | }); 143 | }); 144 | -------------------------------------------------------------------------------- /controllers/productController.js: -------------------------------------------------------------------------------- 1 | const Product = require("../models/productSchema.js"); 2 | const catchAsyncErrors = require("../middlewares/catchAsyncErrors.js"); 3 | const errorHandler = require("../utils/errorHandler"); 4 | const responseHandler = require("../utils/responseHandler"); 5 | 6 | // CREATE PRODUCT 7 | exports.createProduct = catchAsyncErrors(async (req, res, next) => { 8 | const product = await Product.create(req.body); 9 | 10 | await product.save(); 11 | const { url } = req.body; 12 | if (url) { 13 | product.shareableLink = url; 14 | await product.save(); 15 | } 16 | 17 | return res.status(200).send({ 18 | response: { 19 | data: { product }, 20 | title: "Product Created", 21 | message: "Product Created Successfully!", 22 | status: 200, 23 | }, 24 | errors: {}, 25 | }); 26 | }); 27 | 28 | // GET ALL PRODUCTS (ADMIN) 29 | exports.getAdminProducts = catchAsyncErrors(async (req, res, next) => { 30 | const products = await Product.find(); 31 | 32 | return res.status(200).send({ 33 | response: { 34 | data: { products }, 35 | title: "Product Fetched", 36 | message: "Product Fetched Successfully!", 37 | status: 200, 38 | }, 39 | errors: {}, 40 | }); 41 | }); 42 | 43 | // GET PRODUCT DETAILS 44 | exports.getProductDetails = catchAsyncErrors(async (req, res, next) => { 45 | const product = await Product.findById(req.params.id); 46 | 47 | if (!product) { 48 | return res 49 | .status(404) 50 | .send(errorHandler(404, "Not Found", "Product Not Found")); 51 | } 52 | 53 | return res.status(200).send({ 54 | response: { 55 | data: { product }, 56 | title: "Product Fetched", 57 | message: "Product Fetched Successfully!", 58 | status: 200, 59 | }, 60 | errors: {}, 61 | }); 62 | }); 63 | 64 | // UPDATE PRODUCT -- ADMIN 65 | 66 | exports.updateProduct = catchAsyncErrors(async (req, res, next) => { 67 | let product = await Product.findById(req.params.id); 68 | 69 | if (!product) { 70 | return res 71 | .status(404) 72 | .send(errorHandler(404, "Not Found", "Product Not Found")); 73 | } 74 | 75 | product = await Product.findByIdAndUpdate(req.params.id, req.body, { 76 | new: true, 77 | runValidators: true, 78 | useFindAndModify: false, 79 | }); 80 | 81 | return res.status(200).send({ 82 | response: { 83 | data: { product }, 84 | title: "Product Fetched", 85 | message: "Product Fetched Successfully!", 86 | status: 200, 87 | }, 88 | errors: {}, 89 | }); 90 | }); 91 | 92 | // DELETE PRODUCT 93 | 94 | exports.deleteProduct = catchAsyncErrors(async (req, res, next) => { 95 | const product = await Product.findById(req.params.id); 96 | 97 | if (!product) { 98 | return res 99 | .status(404) 100 | .send(errorHandler(404, "Not Found", "Product Not Found")); 101 | } 102 | 103 | await product.deleteOne(); 104 | 105 | return res.status(200).send({ 106 | response: { 107 | data: { product }, 108 | title: "Product Deleted", 109 | message: "Product Deleted Successfully!", 110 | status: 200, 111 | }, 112 | errors: {}, 113 | }); 114 | }); 115 | 116 | // Search Product 117 | 118 | exports.searchProduct = catchAsyncErrors(async (req, res, next) => { 119 | const products = await Product.findById(req.params.id); 120 | 121 | if (!products) { 122 | return res 123 | .status(404) 124 | .send(errorHandler(404, "Not Found", "Product Not Found")); 125 | } 126 | res.status(200).json({ 127 | success: true, 128 | products, 129 | }); 130 | }); 131 | 132 | // Filter Product 133 | 134 | exports.filterProduct = catchAsyncErrors(async (req, res, next) => { 135 | const { category, numOfReviews } = req.body; 136 | if (!category || !numOfReviews) { 137 | return res 138 | .status(404) 139 | .send( 140 | errorHandler( 141 | 404, 142 | "Invalid Request", 143 | "Please select either category or numOfReviews" 144 | ) 145 | ); 146 | } 147 | 148 | let filterCriteria = {}; 149 | if (category) { 150 | filterCriteria.category = category; 151 | } else if (numOfReviews) { 152 | filterCriteria.numOfReviews = numOfReviews; 153 | } 154 | const products = await Product.find(filterCriteria); 155 | if (!products) { 156 | return res 157 | .status(404) 158 | .send(errorHandler(404, "Not Found", "Product Not Found")); 159 | } 160 | res.status(200).json({ 161 | success: true, 162 | products, 163 | }); 164 | }); 165 | 166 | exports.getShareableLink = async (req, res) => { 167 | try { 168 | const { Id } = req.params; 169 | const product = await Product.findById(Id); 170 | if (!product) { 171 | return res.status(404).json({ error: "Product not found" }); 172 | } 173 | const shareableLink = product.shareableLink; 174 | res.json({ success: true, shareableLink }); 175 | } catch (error) { 176 | console.error(error); 177 | res.status(500).json({ error: "Internal server error" }); 178 | } 179 | }; 180 | -------------------------------------------------------------------------------- /cover-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trisha-tech/OnlineBookSales/3711c3ab744d79602b7ba4ff57ec27e55687e99f/cover-page.png -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | mongo: 5 | image: mongo:6.0 6 | container_name: mongo 7 | environment: 8 | MONGO_INITDB_ROOT_USERNAME: root 9 | MONGO_INITDB_ROOT_PASSWORD: devpassword 10 | volumes: 11 | - mongo-data:/data/db 12 | ports: 13 | - "27017:27017" 14 | 15 | expressapp: 16 | build: 17 | context: . 18 | dockerfile: Dockerfile 19 | container_name: expressapp 20 | environment: 21 | MONGO_URL: mongodb://root:devpassword@mongo:27017/bookDB?authSource=admin 22 | NODE_ENV: development 23 | ports: 24 | - "3000:3000" 25 | depends_on: 26 | - mongo 27 | volumes: 28 | - .:/expressapp 29 | command: ["npm", "run", "start"] 30 | 31 | volumes: 32 | mongo-data: 33 | -------------------------------------------------------------------------------- /middlewares/auth.js: -------------------------------------------------------------------------------- 1 | const ErrorHandler = require("../utils/errorHandler.js"); 2 | const catchAsyncErrors = require("./catchAsyncErrors"); 3 | const jwt = require("jsonwebtoken"); 4 | const Customer = require("../models/customerSchema.js"); 5 | 6 | exports.isAuthenticatedUser = catchAsyncErrors(async (req, res, next) => { 7 | const token = req.headers["auth-token"]; 8 | 9 | if (!token) { 10 | return next(new ErrorHandler("Please Login to access this resource", 401)); 11 | } 12 | 13 | const decodedData = jwt.verify(token, process.env.JWT_SECRET); 14 | req.body.user = await Customer.findById(decodedData.id); 15 | console.log("verified successfully"); 16 | 17 | next(); 18 | }); 19 | 20 | exports.authorizeRoles = (...roles) => { 21 | return (req, res, next) => { 22 | if (!roles.includes(req.user.role)) { 23 | return next( 24 | new ErrorHandler( 25 | `Role: ${req.user.role} is not allowed to access this resouce `, 26 | 403 27 | ) 28 | ); 29 | } 30 | 31 | next(); 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /middlewares/catchAsyncErrors.js: -------------------------------------------------------------------------------- 1 | module.exports = (theFunc) => (req, res, next) => { 2 | Promise.resolve(theFunc(req, res, next)).catch(next); 3 | }; 4 | -------------------------------------------------------------------------------- /middlewares/error.js: -------------------------------------------------------------------------------- 1 | const ErrorHandler = require("../utils/errorHandler.js"); 2 | 3 | module.exports = (err, req, res, next) => { 4 | err.statusCode = err.statusCode || 500; 5 | err.message = err.message || "Internal Server Error"; 6 | 7 | // Handle undefined payload error 8 | if (err.message === "payload is not defined") { 9 | const message = "Payload is not defined"; 10 | err = new ErrorHandler(message, 500); 11 | } 12 | 13 | // Handle undefined jwtSecret error 14 | if (err.message === "jwtSecret is not defined") { 15 | const message = "JWT secret key is not defined"; 16 | err = new ErrorHandler(message, 500); 17 | } 18 | 19 | // Wrong Mongodb Id error 20 | if (err.name === "CastError") { 21 | const message = `Resource not found. Invalid: ${err.path}`; 22 | err = new ErrorHandler(message, 400); 23 | } 24 | 25 | // Mongoose duplicate key error 26 | if (err.code === 11000) { 27 | const message = `Duplicate ${Object.keys(err.keyValue)} Entered`; 28 | err = new ErrorHandler(message, 400); 29 | } 30 | 31 | // Wrong JWT error 32 | if (err.name === "JsonWebTokenError") { 33 | const message = `Json Web Token is invalid, Try again `; 34 | err = new ErrorHandler(message, 400); 35 | } 36 | 37 | // JWT EXPIRE error 38 | if (err.name === "TokenExpiredError") { 39 | const message = `Json Web Token is Expired, Try again `; 40 | err = new ErrorHandler(message, 400); 41 | } 42 | 43 | if (!res.headersSent) { 44 | return res.status(err.statusCode).json({ 45 | success: false, 46 | message: err.message, 47 | }); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /middlewares/models/cartSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const cartSchema = new mongoose.Schema({ 4 | user: { 5 | type: mongoose.Schema.ObjectId, 6 | ref: 'Customer', 7 | required: true 8 | }, 9 | cartItems: { 10 | type: [{ 11 | product: { 12 | type: mongoose.Schema.ObjectId, 13 | ref: 'Product', 14 | required: [true, 'Please Enter Product'] 15 | }, 16 | price: { 17 | type: Number, 18 | required: [true, 'Please Enter Product Price'], 19 | } 20 | }], 21 | default: [] 22 | } 23 | }); 24 | 25 | module.exports = mongoose.model('Cart', cartSchema); 26 | -------------------------------------------------------------------------------- /middlewares/models/customerSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const validator = require("validator"); 3 | const bcrypt = require("bcryptjs"); 4 | const jwt = require("jsonwebtoken"); 5 | const dotenv = require(`dotenv`); 6 | dotenv.config({ path: `.env` }); 7 | 8 | const customerSchema = new mongoose.Schema({ 9 | name: { 10 | type: String, 11 | required: [true, "Please Enter Your Name"], 12 | maxLength: [30, "Name cannot exceed 30 characters"], 13 | minLength: [4, "Name should have more than 4 characters"], 14 | }, 15 | email: { 16 | type: String, 17 | required: [true, "Please Enter Your Email"], 18 | unique: true, 19 | validate: [validator.isEmail, "Please Enter a valid Email"], 20 | }, 21 | password: { 22 | type: String, 23 | required: [true, "Please Enter Your Password"], 24 | minLength: [8, "Password should be greater than 8 characters"], 25 | select: false, 26 | }, 27 | avatar: { 28 | public_id: { 29 | type: String, 30 | required: true, 31 | }, 32 | url: { 33 | type: String, 34 | required: true, 35 | }, 36 | }, 37 | role: { 38 | type: String, 39 | default: "user", 40 | }, 41 | googleId: { 42 | type: String, 43 | unique: true, 44 | sparse: true, // This allows either Google login or email/password login 45 | }, 46 | refreshToken: { 47 | type: String, 48 | required: false, 49 | }, 50 | createdAt: { 51 | type: Date, 52 | default: Date.now, 53 | }, 54 | resetPasswordToken: String, 55 | resetPasswordExpire: Date, 56 | }); 57 | 58 | customerSchema.pre("save", async function (next) { 59 | if (!this.isModified("password")) { 60 | next(); 61 | } 62 | 63 | this.password = await bcrypt.hash(this.password, 10); 64 | }); 65 | 66 | // JWT TOKEN 67 | customerSchema.methods.getJWTToken = function () { 68 | return jwt.sign({ id: this._id }, process.env.JWT_SECRET); 69 | }; 70 | 71 | // Compare Password 72 | customerSchema.methods.comparePassword = async function (password) { 73 | return await bcrypt.compare(password, this.password); 74 | }; 75 | 76 | module.exports = mongoose.model("Customer", customerSchema); 77 | -------------------------------------------------------------------------------- /middlewares/models/feebackSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const { assign } = require('nodemailer/lib/shared'); 3 | 4 | const feedbackSchema = new mongoose.Schema({ 5 | user: { 6 | type: mongoose.Schema.ObjectId, 7 | ref: 'Customer', 8 | required: true 9 | }, 10 | topic: { 11 | type: String, 12 | default: 'Customer support' 13 | }, 14 | feedback: { 15 | type: String, 16 | required: [true, 'Please Enter Feedback'] 17 | }, 18 | createdAt: { 19 | type: Date, 20 | default: Date.now 21 | }, 22 | assignedTo: { 23 | type: String, 24 | default: 'Customer support' 25 | }, 26 | resolvedAt: { 27 | type: Date 28 | }, 29 | recentUpdate: { 30 | type: Date 31 | }, 32 | resolvedBy: { 33 | type: mongoose.Schema.ObjectId, 34 | ref: 'Admin' 35 | }, 36 | resolvedFeedback: { 37 | type: String 38 | }, 39 | resolvedStatus: { 40 | type: String, 41 | default: 'Pending' 42 | } 43 | }); 44 | 45 | module.exports = mongoose.model('Feedback', feedbackSchema); -------------------------------------------------------------------------------- /middlewares/models/orderSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const orderSchema = new mongoose.Schema({ 4 | shippingInfo: { 5 | address: { 6 | type: String, 7 | required: true, 8 | }, 9 | city: { 10 | type: String, 11 | required: true, 12 | }, 13 | 14 | state: { 15 | type: String, 16 | required: true, 17 | }, 18 | 19 | country: { 20 | type: String, 21 | required: true, 22 | }, 23 | pinCode: { 24 | type: Number, 25 | required: true, 26 | }, 27 | phoneNo: { 28 | type: Number, 29 | required: true, 30 | }, 31 | }, 32 | orderItems: [ 33 | { 34 | name: { 35 | type: String, 36 | required: true, 37 | }, 38 | price: { 39 | type: Number, 40 | required: true, 41 | }, 42 | quantity: { 43 | type: Number, 44 | required: true, 45 | }, 46 | image: { 47 | type: String, 48 | required: true, 49 | }, 50 | product: { 51 | type: mongoose.Schema.ObjectId, 52 | ref: "Product", 53 | required: true, 54 | }, 55 | }, 56 | ], 57 | user: { 58 | type: mongoose.Schema.ObjectId, 59 | ref: "Customer", 60 | required: true, 61 | }, 62 | paymentInfo: { 63 | id: { 64 | type: String, 65 | required: true, 66 | }, 67 | status: { 68 | type: String, 69 | required: true, 70 | }, 71 | }, 72 | paidAt: { 73 | type: Date, 74 | required: true, 75 | }, 76 | itemsPrice: { 77 | type: Number, 78 | required: true, 79 | default: 0, 80 | }, 81 | taxPrice: { 82 | type: Number, 83 | required: true, 84 | default: 0, 85 | }, 86 | shippingPrice: { 87 | type: Number, 88 | required: true, 89 | default: 0, 90 | }, 91 | totalPrice: { 92 | type: Number, 93 | required: true, 94 | default: 0, 95 | }, 96 | orderStatus: { 97 | type: String, 98 | required: true, 99 | default: "Processing", 100 | }, 101 | deliveredAt: Date, 102 | createdAt: { 103 | type: Date, 104 | default: Date.now, 105 | }, 106 | }); 107 | 108 | module.exports = mongoose.model("Order", orderSchema); 109 | -------------------------------------------------------------------------------- /middlewares/models/productSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const productSchema = mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: [true, "Please Enter Product Name"], 7 | trim: true, 8 | }, 9 | description: { 10 | type: String, 11 | required: [true, "Please Enter Product Description"], 12 | }, 13 | author:{ 14 | 15 | type: String, 16 | type: String, 17 | required: [true, "Please Enter Product Author"], 18 | }, 19 | price: { 20 | type: Number, 21 | required: [true, "Please Enter Product Price"], 22 | maxLength: [8, "Price cannot exceed 8 characters"], 23 | }, 24 | ratings: { 25 | type: Number, 26 | default: 0, 27 | }, 28 | images: [ 29 | { 30 | public_id: { 31 | type: String, 32 | required: true, 33 | }, 34 | url: { 35 | type: String, 36 | required: true, 37 | }, 38 | }, 39 | ], 40 | category: { 41 | type: String, 42 | required: [true, "Please Enter Product Category"], 43 | }, 44 | Stock: { 45 | type: Number, 46 | required: [true, "Please Enter Product Stock"], 47 | maxLength: [4, "Stock cannot exceed 4 characters"], 48 | default: 1, 49 | }, 50 | numOfReviews: { 51 | type: Number, 52 | default: 0, 53 | }, 54 | reviews: [ 55 | { 56 | user: { 57 | type: mongoose.Schema.ObjectId, 58 | ref: "User", 59 | required: true, 60 | }, 61 | name: { 62 | type: String, 63 | required: true, 64 | }, 65 | rating: { 66 | type: Number, 67 | required: true, 68 | }, 69 | comment: { 70 | type: String, 71 | required: true, 72 | }, 73 | }, 74 | ], 75 | user: { 76 | type: mongoose.Schema.ObjectId, 77 | ref: "User", 78 | required: true, 79 | }, 80 | createdAt: { 81 | type: Date, 82 | default: Date.now, 83 | }, 84 | shareableLink:{ 85 | type: String, 86 | required:true, 87 | }, 88 | 89 | }); 90 | 91 | module.exports = mongoose.model("Product", productSchema); 92 | -------------------------------------------------------------------------------- /middlewares/models/wishlistSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const wishlistSchema = mongoose.Schema({ 4 | user: { 5 | type: mongoose.Schema.ObjectId, 6 | ref: "Customer", 7 | required: true, 8 | }, 9 | product: { 10 | type: mongoose.Schema.ObjectId, 11 | ref: "Product", 12 | required: true, 13 | }, 14 | }); 15 | 16 | module.exports = mongoose.model("Wishlist", wishlistSchema); 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@emotion/react": "^11.11.4", 15 | "@emotion/styled": "^11.11.5", 16 | "@heroicons/react": "^2.1.3", 17 | "@mui/material": "^5.16.0", 18 | "axios": "^1.7.2", 19 | "backend": "file:", 20 | "bcryptjs": "^2.4.3", 21 | "body-parser": "^1.20.3", 22 | "cookie-parser": "^1.4.6", 23 | "cors": "^2.8.5", 24 | "disposable-email-domains": "^1.0.62", 25 | "dotenv": "^16.4.5", 26 | "env": "^0.0.2", 27 | "express": "^4.21.0", 28 | "jsonwebtoken": "^9.0.2", 29 | "mongoose": "^7.6.11", 30 | "nodemailer": "^6.9.15", 31 | "nodemon": "^3.1.7", 32 | "passport": "^0.7.0", 33 | "passport-google-oauth20": "^2.0.0", 34 | "react-router-dom": "^6.26.2", 35 | "validator": "^13.12.0", 36 | "yup": "^1.4.0" 37 | }, 38 | "your-frontend": { 39 | "name": "your-frontend", 40 | "version": "0.1.0", 41 | "private": true, 42 | "proxy": "http://localhost:8080" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /routes/adminRoutes.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const { authorizeRoles } = require('../middlewares/auth'); 3 | const router=express.Router(); 4 | const {retriveFeedbacks,retriveFeedbacksById,updateFeeback}=require('../controllers/adminController'); 5 | 6 | 7 | 8 | 9 | 10 | router.get('/feedback',retriveFeedbacks); 11 | 12 | 13 | router.get('/feedback/:id',retriveFeedbacksById); 14 | 15 | router.post('/feedback/update/:id',updateFeeback); 16 | 17 | 18 | 19 | module.exports=router; 20 | -------------------------------------------------------------------------------- /routes/cartRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | // Mock cart data 5 | let cart = { 6 | items: [], 7 | total: 0, 8 | }; 9 | 10 | // Fetch cart data 11 | router.get("/", (req, res) => { 12 | res.json(cart); 13 | }); 14 | 15 | // Add item to cart 16 | router.post("/", (req, res) => { 17 | const item = req.body; 18 | cart.items.push(item); 19 | cart.total += item.price; 20 | res.json(cart); 21 | }); 22 | 23 | // Remove item from cart 24 | router.delete("/:itemId", (req, res) => { 25 | const itemId = req.params.itemId; 26 | const itemIndex = cart.items.findIndex((item) => item.id === itemId); 27 | if (itemIndex !== -1) { 28 | cart.total -= cart.items[itemIndex].price; 29 | cart.items.splice(itemIndex, 1); 30 | } 31 | res.json(cart); 32 | }); 33 | 34 | module.exports = router; 35 | -------------------------------------------------------------------------------- /routes/customerRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { 3 | registerCustomer, 4 | loginCustomer, 5 | getCustomerDetails, 6 | updatePassword, 7 | updateProfile, 8 | logoutCustomer, 9 | addFeedback, 10 | resetPassword 11 | 12 | 13 | } = require("../controllers/customerController.js"); 14 | const { 15 | addTocart, 16 | getCartItems, 17 | deleteCartItem 18 | }=require('../controllers/cartController'); 19 | const { isAuthenticatedUser, authorizeRoles } = require("../middlewares/auth.js"); 20 | 21 | 22 | const router = express.Router(); 23 | 24 | router.route("/register").post(registerCustomer); 25 | 26 | router.route("/login").post(loginCustomer); 27 | 28 | router.route("/logout").post(isAuthenticatedUser, logoutCustomer) 29 | 30 | router.route("/me").get(isAuthenticatedUser, getCustomerDetails); 31 | 32 | router.route("/password/update").put(isAuthenticatedUser, updatePassword); 33 | 34 | router.route("/me/update").put(isAuthenticatedUser, updateProfile); 35 | 36 | router.route("/resetpassword").post(resetPassword); 37 | 38 | 39 | //cart routes 40 | //instead of sending user as req parameter we can send user id 41 | router.route("/cart/add-product").post(addTocart); 42 | 43 | router.route("/cart/remove-product").delete(deleteCartItem); 44 | 45 | router.route("/cart").get(getCartItems); 46 | 47 | 48 | //cart routes 49 | //instead of sending user as req parameter we can send user id 50 | router.route("/cart/add-product").post(isAuthenticatedUser,addTocart); 51 | 52 | router.route("/cart/remove-product").delete(isAuthenticatedUser,deleteCartItem); 53 | 54 | router.route("/cart").get(isAuthenticatedUser,getCartItems); 55 | 56 | 57 | //giving feedback 58 | router.route("/add-feedback").post(isAuthenticatedUser,addFeedback); 59 | module.exports = router; 60 | -------------------------------------------------------------------------------- /routes/orderRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { 3 | newOrder, 4 | getSingleOrder, 5 | myOrders, 6 | updateOrder, 7 | deleteOrder 8 | } = require("../controllers/orderController"); 9 | const router = express.Router(); 10 | 11 | const { isAuthenticatedUser, authorizeRoles } = require("../middlewares/auth.js"); 12 | 13 | router.route("/order/new").post(isAuthenticatedUser, newOrder); 14 | 15 | router.route("/order/:id").get(isAuthenticatedUser, getSingleOrder); 16 | 17 | router.route("/orders/me").get(isAuthenticatedUser, myOrders); 18 | 19 | router 20 | .route("/admin/order/:id") 21 | .put(updateOrder) 22 | .delete(deleteOrder); 23 | 24 | module.exports = router; -------------------------------------------------------------------------------- /routes/productRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { 3 | createProduct, 4 | getAdminProducts, 5 | getProductDetails, 6 | getShareableLink,//it was missing causing error 7 | updateProduct, 8 | deleteProduct, 9 | searchProduct, 10 | filterProduct 11 | } = require("../controllers/productController.js"); 12 | 13 | const router = express.Router(); 14 | 15 | router 16 | .route("/admin/product/new") 17 | .post(createProduct); 18 | 19 | router 20 | .route("/admin/products") 21 | .get(getAdminProducts); 22 | 23 | router.route("/product/:id").get(getProductDetails); 24 | 25 | router.route("/producturl/:id").get(getShareableLink); 26 | 27 | router 28 | .route("/admin/product/:id") 29 | .put(updateProduct) 30 | .delete(deleteProduct); 31 | 32 | 33 | 34 | router.route("/product/search/:id").get(searchProduct); 35 | router.route("/product").get(filterProduct); 36 | 37 | module.exports = router; 38 | -------------------------------------------------------------------------------- /routes/wishlistRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const { isAuthenticatedUser } = require('../middlewares/auth'); 4 | const Wishlist = require('../models/wishlistSchema'); 5 | const ErrorHandler = require('../utils/errorHandler'); 6 | 7 | // route POST /api/wishlist/add-to-wishlist/:productId 8 | // description Add product to wishlist 9 | // access Private (requires authentication) 10 | router.post('/addtowishlist/:productId', isAuthenticatedUser, async (req, res, next) => { 11 | try { 12 | const { productId } = req.params; 13 | const userId = req.body.user.id; // Retrieve user ID from request body 14 | // console.log("inadd to wishlist") 15 | 16 | // Check if the product is already in the user's wishlist 17 | const existingWishlistItem = await Wishlist.findOne({ user: userId, product: productId }); 18 | 19 | if (existingWishlistItem) { 20 | return next(new ErrorHandler('Product is already in your wishlist', 400)); 21 | } 22 | 23 | // Create a new wishlist item 24 | const newWishlistItem = await Wishlist.create({ 25 | user: userId, 26 | product: productId, 27 | }); 28 | 29 | res.status(200).json({ 30 | success: true, 31 | message: 'Product added to wishlist successfully', 32 | wishlistItem: newWishlistItem, 33 | }); 34 | } catch (error) { 35 | next(error); 36 | } 37 | }); 38 | 39 | // route DELETE /api/wishlist/remove-from-wishlist/:productId 40 | // description Remove product from wishlist 41 | // access Private (requires authentication) 42 | router.delete('/removefromwishlist/:productId', isAuthenticatedUser, async (req, res, next) => { 43 | try { 44 | const { productId } = req.params; 45 | const userId = req.body.user.id; 46 | 47 | // Find and delete the wishlist item 48 | const deletedWishlistItem = await Wishlist.findOneAndDelete({ user: userId, product: productId }); 49 | 50 | if (!deletedWishlistItem) { 51 | return next(new ErrorHandler('Product not found in your wishlist', 404)); 52 | } 53 | 54 | res.status(200).json({ 55 | success: true, 56 | message: 'Product removed from wishlist successfully', 57 | wishlistItem: deletedWishlistItem, 58 | }); 59 | } catch (error) { 60 | next(error); 61 | } 62 | }); 63 | 64 | // route GET /api/wishlist 65 | // description Get user's wishlist 66 | // access Private (requires authentication) 67 | router.get('/getwishlistdata', isAuthenticatedUser, async (req, res, next) => { 68 | try { 69 | const userId = req.body.user.id; 70 | 71 | // Fetch user's wishlist items 72 | const wishlistItems = await Wishlist.find({ user: userId }).populate('product'); 73 | 74 | res.status(200).json({ 75 | success: true, 76 | wishlistItems, 77 | }); 78 | } catch (error) { 79 | next(error); 80 | } 81 | }); 82 | 83 | module.exports = router; 84 | -------------------------------------------------------------------------------- /utils/errorHandler.js: -------------------------------------------------------------------------------- 1 | const errorHandler = (status, title, detail) => { 2 | return { 3 | response: {}, 4 | errors: { 5 | status: status || 500, 6 | title: title || "Internal Server Error", 7 | detail: detail || "Error in processing request! Please try again later.", 8 | }, 9 | }; 10 | }; 11 | 12 | module.exports = { 13 | errorHandler 14 | } 15 | -------------------------------------------------------------------------------- /utils/jwtToken.js: -------------------------------------------------------------------------------- 1 | // Create Token and saving in cookie 2 | 3 | const sendToken = (user, statusCode, res) => { 4 | const token = user.getJWTToken(); 5 | 6 | // options for cookie 7 | const options = { 8 | expires: new Date(Date.now() + process.env.COOKIE_EXPIRE * 24 * 60 * 60 * 1000), 9 | httpOnly: true, 10 | }; 11 | 12 | 13 | res.status(statusCode).cookie("token", token).json({ 14 | success: true, 15 | user, 16 | token, 17 | }); 18 | }; 19 | 20 | module.exports = sendToken; 21 | -------------------------------------------------------------------------------- /utils/responseHandler.js: -------------------------------------------------------------------------------- 1 | const responseHandler = (res, options) => { 2 | return res.status(options?.status ? options.status : 200).send({ 3 | response: { 4 | data: options.data || {}, 5 | title: options.title || "Request Processed", 6 | message: options.message || "The request was successful", 7 | status: options.status || 200, 8 | }, 9 | errors: {}, 10 | }); 11 | }; 12 | 13 | module.exports = { responseHandler }; --------------------------------------------------------------------------------