├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── issue_template.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── PRValidator.yml │ ├── auto_label.yaml │ ├── check-merge-conflicts.yml │ ├── check_fork_status.yaml │ ├── issue_thank.yaml │ └── post_pr_thankyou.yaml ├── .gitignore ├── .htaccess ├── .vscode └── settings.json ├── 25a7f6e52f7d373e7db6d63c4bdb38d1.jpg ├── 404.html ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Carousel.js ├── Instructions.txt ├── LICENSE ├── OIP (10).jpeg ├── OIP (11).jpeg ├── OIP (12).jpeg ├── OIP (13).jpeg ├── OIP (5).jpeg ├── OIP (6).jpeg ├── OIP (7).jpeg ├── OIP (8).jpeg ├── OIP (9).jpeg ├── README.md ├── Scroll_and_progressbar.css ├── SignIn_SignUp.js ├── __pycache__ └── app.cpython-312.pyc ├── app.py ├── assets ├── 380x80 │ ├── img1.webp │ ├── img1_380x80.webp │ ├── img2.webp │ ├── img2_380x80.webp │ ├── img3.webp │ ├── img3_380x80.webp │ ├── img4.webp │ ├── img4_380x80.webp │ ├── img5.webp │ └── img5_380x80.webp ├── avatar.webp ├── face │ ├── face1.webp │ ├── face2.webp │ └── face3.webp ├── fashion.webp ├── google_icon.webp ├── health.webp ├── img1.webp ├── img10.webp ├── img11.webp ├── img12.webp ├── img13.webp ├── img14.webp ├── img2.webp ├── img21.webp ├── img3.webp ├── img4.webp ├── img5.webp ├── img6.webp ├── img7.webp ├── img8.webp ├── img9.webp ├── slider │ ├── img1.webp │ ├── img2.webp │ ├── img3.webp │ ├── img4.webp │ ├── img5.webp │ ├── img6.webp │ └── img7.webp └── start.webp ├── backend ├── .env.example ├── .gitignore ├── README.md ├── app.js ├── controllers │ ├── addBlogController.js │ ├── blogController.js │ ├── contactController.js │ ├── discussionController.js │ ├── faqController.js │ ├── feedController.js │ ├── getInTouchController.js │ ├── postsController.js │ ├── ratingController.js │ ├── subscribeController.js │ └── userController.js ├── generateFromAi.py ├── middlewares │ ├── adminMiddleware.js │ └── authMiddleware.js ├── models │ ├── addBlog.js │ ├── answers.js │ ├── blog.js │ ├── comment.js │ ├── contact.js │ ├── discussion.js │ ├── feedback.js │ ├── getInTouch.js │ ├── postModel.js │ ├── question.js │ ├── rating.js │ ├── subscribe.js │ └── user.js ├── package-lock.json ├── package.json ├── routes │ ├── addBlogRoutes.js │ ├── blogRoutes.js │ ├── contactRoute.js │ ├── discussionRoutes.js │ ├── faqRoutes.js │ ├── feedbackRoute.js │ ├── getInTouchRoutes.js │ ├── ratingRoutes.js │ ├── storiesRoutes.js │ ├── subscribeRoutes.js │ └── userRoutes.js ├── sendFeedbackToAdmin.js ├── utils │ ├── db.js │ └── sendMailToSubscribe.js └── validations │ ├── blogValidation.js │ └── userValidation.js ├── blog-comment.js ├── blog.css ├── blog.html ├── bookmarks.html ├── boy.webp ├── category.css ├── category.html ├── chatbot.gif ├── chatbot.html ├── comment.css ├── comment.js ├── comment1.html ├── comment10.html ├── comment11.html ├── comment12.html ├── comment13.html ├── comment14.html ├── comment2.html ├── comment3.html ├── comment4.html ├── comment5.html ├── comment6.html ├── comment7.html ├── comment8.html ├── comment9.html ├── contact.html ├── contact_us.css ├── contact_us.html ├── contact_us.js ├── darkMode.js ├── dist ├── contact_us.dev.js ├── contact_us.dev.js.map ├── contact_us.min.js └── contact_us.min.js.map ├── download (1).jpeg ├── download (2).jpeg ├── download (3).jpeg ├── download.jpeg ├── faq.js ├── faq.webp ├── fashion.html ├── food.html ├── forum.html ├── frontend ├── .gitignore ├── README.md ├── package.json ├── public │ └── blog │ │ ├── post-1.webp │ │ ├── post-2.webp │ │ └── post-3.webp ├── src │ ├── assets │ │ ├── about │ │ │ └── chart.png │ │ └── blog │ │ │ ├── 1.webp │ │ │ ├── 10.webp │ │ │ ├── 11.webp │ │ │ ├── 12.webp │ │ │ ├── 13.webp │ │ │ ├── 14.webp │ │ │ ├── 15.webp │ │ │ ├── 16.webp │ │ │ ├── 17.webp │ │ │ ├── 18.webp │ │ │ ├── 19.webp │ │ │ ├── 2.webp │ │ │ ├── 20.webp │ │ │ ├── 21.webp │ │ │ ├── 22.webp │ │ │ ├── 23.webp │ │ │ ├── 24.webp │ │ │ ├── 25.webp │ │ │ ├── 26.webp │ │ │ ├── 27.webp │ │ │ ├── 28.webp │ │ │ ├── 29.webp │ │ │ ├── 3.webp │ │ │ ├── 30.webp │ │ │ ├── 31.webp │ │ │ ├── 32.webp │ │ │ ├── 33.webp │ │ │ ├── 34.webp │ │ │ ├── 35.webp │ │ │ ├── 36.webp │ │ │ ├── 37.webp │ │ │ ├── 38.webp │ │ │ ├── 39.webp │ │ │ ├── 4.webp │ │ │ ├── 40.webp │ │ │ ├── 41.webp │ │ │ ├── 42.webp │ │ │ ├── 5.webp │ │ │ ├── 6.webp │ │ │ ├── 7.webp │ │ │ ├── 8.webp │ │ │ └── 9.webp │ ├── components │ │ ├── Footer.js │ │ └── Navbar.js │ ├── index.html │ ├── main.js │ ├── pages │ │ ├── About.js │ │ ├── AddBlog.js │ │ ├── BloggerProfile.js │ │ ├── Blogs.js │ │ ├── Categories.js │ │ ├── Contact.js │ │ ├── DiscussionForum.js │ │ ├── Feedback.js │ │ ├── GoogleTranslator.js │ │ ├── Home.js │ │ ├── Login.js │ │ ├── ReadMoreBlog.js │ │ ├── Stories.js │ │ └── TermsOfUse.js │ ├── styles │ │ ├── footer.css │ │ ├── input.css │ │ ├── navbar.css │ │ └── output.css │ └── utils │ │ └── router.js ├── tailwind.config.js └── vite.config.js ├── gaming_chronicles.html ├── give_feedback.html ├── give_feedback.js ├── google_signin.js ├── health.html ├── home.webp ├── icon-192x192.png ├── icon-512x512.png ├── images ├── Untitled design.webp ├── android-chrome-192x192.webp ├── android-chrome-512x512.webp ├── apple-touch-icon.webp ├── background.jpg ├── favi.webp ├── favicon-16x16.webp ├── favicon-32x32.webp ├── favicon.ico ├── favicon.webp ├── profile.webp └── website.webp ├── index.html ├── life.html ├── login.css ├── main.js ├── main2.js ├── manifest.json ├── package-lock.json ├── package.json ├── privacy.html ├── profile.html ├── profile.js ├── profiledropdown.js ├── profileedit.html ├── progress_bar.js ├── requirements.txt ├── resetpass.html ├── script.js ├── scripts.js ├── share.js ├── signup.css ├── site.webmanifest ├── start.html ├── start_writing.css ├── start_writing.html ├── start_writing.js ├── style.css ├── styles.css ├── sw.js ├── tech.html ├── testp.css ├── testp.js ├── travel.html └── words.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | 27 | 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Issue Checklist 2 | 3 | - [ ] I have searched for existing issues and made sure my issue is new. 4 | - [ ] I have made sure to add all necessary details. 5 | - [ ] I will not create multiple issues related to the same topic. 6 | 7 | ### Issue Description 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Pull Request Checklist 2 | 3 | - [ ] I have added screenshots and videos to show before and after the working of my code. 4 | - [ ] I have ensured that the screen size is set to 100% while making the video. 5 | - [ ] I have synced the latest fork with my local repository and resolved any conflicts. 6 | - [ ] I have mentioned the issue number which I created before making this PR .(format to mention issue number is : fixes #(issue number) ) 7 | - [ ] I understand that if any the above conditions are not met , my PR will not be MERGED . 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/PRValidator.yml: -------------------------------------------------------------------------------- 1 | name: PR Description Check 2 | 3 | on: 4 | pull_request: 5 | types: [opened] 6 | 7 | jobs: 8 | validate-pr-description: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Validate PR Description 12 | id: check_description 13 | run: | 14 | if [ -z "${{ github.event.pull_request.body }}" ]; then 15 | echo "::error::Description is missing!" 16 | exit 1 17 | fi 18 | 19 | - name: Check for issue number or 'Fixes #NEW' 20 | id: check_issue_number 21 | run: | 22 | description="${{ github.event.pull_request.body }}" 23 | if [[ ! "$description" =~ (Fixes #[0-9])]]; then 24 | echo "::error::PR description must contain an issue number or 'Fixes #NEW'" 25 | exit 1 26 | fi 27 | -------------------------------------------------------------------------------- /.github/workflows/auto_label.yaml: -------------------------------------------------------------------------------- 1 | name: Auto Label Issues and PRs 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | pull_request_target: # Correct indentation here 7 | types: [opened] 8 | 9 | jobs: 10 | add-labels: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Add labels to new issues 15 | if: github.event_name == 'issues' 16 | uses: actions-ecosystem/action-add-labels@v1 17 | with: 18 | github_token: ${{ secrets.PAT_TOKEN }} # Use PAT_TOKEN for issues 19 | labels: | 20 | gssoc-ext 21 | hacktoberfest-accepted 22 | 23 | - name: Add labels to new pull requests 24 | if: github.event_name == 'pull_request_target' 25 | uses: actions-ecosystem/action-add-labels@v1 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} # Use GITHUB_TOKEN for PRs 28 | labels: | 29 | gssoc-ext 30 | hacktoberfest-accepted 31 | -------------------------------------------------------------------------------- /.github/workflows/check-merge-conflicts.yml: -------------------------------------------------------------------------------- 1 | name: Merge Conflict Checker 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | 7 | permissions: 8 | pull-requests: write 9 | contents: read 10 | 11 | jobs: 12 | check_merge_conflicts: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v3 17 | 18 | - name: Check for merge conflicts 19 | run: | 20 | git fetch origin 21 | if git merge-base --is-ancestor HEAD origin/main; then 22 | echo "No merge conflicts." 23 | else 24 | echo "Merge conflicts detected." 25 | exit 1 26 | fi 27 | continue-on-error: true 28 | 29 | - name: Post comment if there are merge conflicts 30 | if: failure() 31 | uses: actions/github-script@v6 32 | with: 33 | script: | 34 | github.rest.issues.createComment({ 35 | issue_number: context.payload.pull_request.number, 36 | owner: context.repo.owner, 37 | repo: context.repo.repo, 38 | body: '⚠️ This branch has conflicts that must be resolved. Please resolve the merge conflicts before proceeding.' 39 | }) 40 | -------------------------------------------------------------------------------- /.github/workflows/check_fork_status.yaml: -------------------------------------------------------------------------------- 1 | name: Check Fork Status and Notify 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] # Trigger when a PR is opened or updated 6 | 7 | permissions: 8 | pull-requests: write # Allow posting comments on PRs 9 | 10 | jobs: 11 | check_fork: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout the repository 16 | uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 2 # Fetch enough history to compare branches 19 | 20 | - name: Fetch the latest base branch 21 | run: | 22 | git fetch origin main # Replace 'main' with your base branch if different 23 | 24 | - name: Check if the branch is up to date 25 | id: check_fork 26 | run: | 27 | # Compare the contributor's branch with the base branch (main) 28 | behind_count=$(git rev-list --right-only --count HEAD...origin/main) 29 | echo "::set-output name=behind::$behind_count" 30 | 31 | - name: Comment on the PR if fork is outdated 32 | if: steps.check_fork.outputs.behind != '0' # If the fork is behind 33 | uses: actions/github-script@v7 34 | with: 35 | script: | 36 | const prNumber = context.payload.pull_request.number; 37 | const owner = context.repo.owner; 38 | const repo = context.repo.repo; 39 | 40 | await github.rest.issues.createComment({ 41 | owner: owner, 42 | repo: repo, 43 | issue_number: prNumber, 44 | body: '⚠️ It looks like your branch is out of date with the base branch. Please sync your fork and update the PR.' 45 | }); 46 | -------------------------------------------------------------------------------- /.github/workflows/issue_thank.yaml: -------------------------------------------------------------------------------- 1 | name: Auto Assign and Thank Issues 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | auto-assign: 10 | runs-on: ubuntu-latest 11 | 12 | permissions: 13 | issues: write 14 | 15 | steps: 16 | - name: 'Thank and assign the issue to the creator' 17 | uses: actions/github-script@v7 # Updated to v7 18 | with: 19 | github-token: ${{ secrets.GITHUB_TOKEN }} # Use auto-generated GitHub Token 20 | script: | 21 | const issueNumber = context.issue.number; 22 | const issueCreator = context.payload.issue.user.login; 23 | 24 | // Thank the issue creator 25 | await github.rest.issues.createComment({ 26 | issue_number: issueNumber, 27 | owner: context.repo.owner, 28 | repo: context.repo.repo, 29 | body: `Thanks @${issueCreator} for raising this issue! We'll look into it.We hope you have made sure that a similar issue doesnt exist , if it does, kindly ask to be assigned on that issue ` 30 | }); 31 | 32 | // Assign the issue to the creator 33 | //await github.rest.issues.addAssignees({ 34 | // issue_number: issueNumber, 35 | // owner: context.repo.owner, 36 | // repo: context.repo.repo, 37 | // assignees: [issueCreator] 38 | // }); 39 | -------------------------------------------------------------------------------- /.github/workflows/post_pr_thankyou.yaml: -------------------------------------------------------------------------------- 1 | name: Post-PR Merge Thank You 2 | 3 | on: 4 | pull_request_target: 5 | types: [closed] # Trigger when a PR is closed 6 | 7 | permissions: 8 | issues: write 9 | pull-requests: write 10 | 11 | jobs: 12 | post_merge_message: 13 | if: github.event.pull_request.merged == true # Only run if the PR was merged 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Post thank you message 18 | uses: actions/github-script@v7 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} # Ensure token is used 21 | script: | 22 | const prNumber = context.payload.pull_request.number; 23 | const owner = context.repo.owner; 24 | const repo = context.repo.repo; 25 | 26 | // Post a thank you message upon PR merge 27 | await github.rest.issues.createComment({ 28 | owner: owner, 29 | repo: repo, 30 | issue_number: prNumber, 31 | body: `🎉🎉 Thank you for your contribution! Your PR #${prNumber} has been merged! 🎉🎉` 32 | }); 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | ErrorDocument 404 /404.html -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } -------------------------------------------------------------------------------- /25a7f6e52f7d373e7db6d63c4bdb38d1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/25a7f6e52f7d373e7db6d63c4bdb38d1.jpg -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 PAGE 7 | 8 | 9 | 10 | 41 | 42 | 43 | 44 | 45 | 46 |
47 |
48 |
49 |
50 |
51 |
52 |

404

53 | 54 | 55 |
56 | 57 |
58 |

59 | Look like you're lost 60 |

61 | 62 |

The page you are looking for not available!

63 | 64 | Go to Home 65 |
66 |
67 |
68 |
69 |
70 |
71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | sanjay@recodehive.com(GSSOC team). 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 |

✨ Contributors Guide ✨

2 |


We appreciate your interest in contributing.😊
This guide will help you get started with the project and make your first contribution.


3 | 4 | ## What you can Contribute? 5 | 6 | **🐞Bug Fixing :** 7 | Contributors can help by reviewing and confirming reported issues. This includes verifying bugs, providing additional details, and prioritizing tasks for the development team. 8 | 9 | **✨Enhancements :** 10 | Contributors can enhance the project by implementing new features or improvements suggested by users. This involves understanding requirements, designing solutions, and extending the functionality. 🚀
11 | 12 | ## How to Contribute? 13 | 14 | - Drop a Star ⭐ in this repo 15 | - Take a look at the existing [Issues](https://github.com/ANSHIKA-26/WordWise/issues). 16 | - Fork the Repo & create a branch for any issue that you are working on and commit your work. 17 | - At first raise an issue in which you want to work 18 | - Then after assigning only then work on that issue & make a PR 19 | - Create a [**Pull Request**](https://github.com/ANSHIKA-26/WordWise/pulls), which will be promptly reviewed and given 20 | suggestions for improvements by the community. 21 | - **REMINDER: Don't raise more than 2 `Issue` at a time** 22 | - **IMPORTANT: Don't make any type of `Pull Request` until & unless you get assigned to an `Issue`** 23 | - Add screenshots or screen captures to your `Pull Request` to help us understand the effects of the changes that are included in 24 | your commits. 25 | 26 | 27 | ## Submitting a Pull Request? 28 | **1.** Start by forking the [**WordWise**](https://github.com/ANSHIKA-26/WordWise) repository. Click on the symbol at the top right corner. 29 | 30 | **2.** Clone your forked repository: 31 | 32 | ```bash 33 | git clone https://github.com/Your-Username/WordWise.git 34 | ``` 35 | 36 | **3.** Add a reference(remote) to the original repository. 37 | 38 | ```bash 39 | git remote add upstream https://github.com/ANSHIKA-26/WordWise 40 | ``` 41 | 42 | **4.** Check the remotes for this repository. 43 | 44 | ```bash 45 | git remote -v 46 | ``` 47 | 48 | **5.** Always take a pull from the upstream repository to your master branch to keep it at par with the main project (updated repository). 49 | 50 | ```bash 51 | git pull upstream main 52 | ``` 53 | 54 | **6.** Create a Branch: Create a new branch for your changes: 55 | 56 | ```bash 57 | git checkout -b my-feature 58 | ``` 59 | 60 | **7.** Make Changes: Make your desired changes to the codebase. 61 | 62 | ```bash 63 | git add . 64 | git add 65 | ``` 66 | 67 | **8.** Commit Changes: Commit your changes with a descriptive commit message: 68 | 69 | ```bash 70 | git commit -m "Add new feature" 71 | ``` 72 | 73 | **9.** Push Changes: Push your changes to your forked repository: 74 | 75 | ```bash 76 | git push origin my-feature 77 | ``` 78 | 79 | **10.** Submit a Pull Request: Go to your forked repository on GitHub and submit a pull request. Be sure to provide a detailed description of your changes and why they are necessary. 80 | 81 | **11.** Congratulations! You've made your first contribution! 🙌🏼 82 | 83 | 84 | ## :zap: Pull Requests Review Criteria 🧲 85 | 86 | 1. Please fill the ***PR Template*** properly while making a Pull Request. 87 | 2. Never commit in the `main` branch. 88 | 3. Your work must be original, written by you not copied from other resources. 89 | 4. You must comment on your code where necessary. 90 | 91 | 92 | ## Documentation 📑 93 | 94 | - Document any significant changes or additions to the codebase. 95 | - Provide clear and concise explanations of the functionality, usage, and any relevant considerations. 96 | - Update the `README.md` file to reflect the changes made and provide instructions on how to use the project (if needed). 97 | 98 | ## :zap: Communication and Support 💬 99 | - Join the project's communication channels to interact with other contributors and seek assistance. 100 | - If you have any questions or need help, don't hesitate to ask in the project's communication channels or comment on the relevant issue. 101 | 102 | ## :zap: Code of Conduct 😇 103 | Please follow our project's code of conduct while contributing.
Treat all contributors and users with respect and create a positive and inclusive environment for everyone. 104 | 105 | ## :zap: License 📄 106 | The project is licensed under ***MIT***. Make sure to review and comply with the license terms.
We hope this guide helps you get started with contributing to our open-source project. Thank you for your contribution! 107 | 108 | #### Congratulations on successfully submitting your PR to our project! 🎉 109 | -------------------------------------------------------------------------------- /Carousel.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | const slides = document.querySelector('.slides'); 3 | const radioButtons = document.querySelectorAll('input[name="radio-btn"]'); 4 | const labels = document.querySelectorAll('.manual-btn'); 5 | const prevButton = document.getElementById('prevSlide'); 6 | const nextButton = document.getElementById('nextSlide'); 7 | let currentSlide = 0; 8 | 9 | function showSlide(index) { 10 | slides.style.transform = `translateX(-${index * 14.29}%)`; 11 | radioButtons[index].checked = true; 12 | updateActiveLabel(index); 13 | } 14 | 15 | function updateActiveLabel(index) { 16 | labels.forEach((label, i) => { 17 | if (i === index) { 18 | label.classList.add('active'); 19 | } else { 20 | label.classList.remove('active'); 21 | } 22 | }); 23 | } 24 | 25 | function nextSlide() { 26 | currentSlide = (currentSlide + 1) % radioButtons.length; 27 | showSlide(currentSlide); 28 | } 29 | function prevSlide() { 30 | currentSlide = (currentSlide - 1 + radioButtons.length) % radioButtons.length; 31 | showSlide(currentSlide); 32 | } 33 | 34 | // Add click event listeners to radio buttons 35 | radioButtons.forEach((radio, index) => { 36 | radio.addEventListener('click', () => { 37 | currentSlide = index; 38 | showSlide(currentSlide); 39 | }); 40 | }); 41 | 42 | // Add click event listeners to Prev and Next buttons 43 | prevButton.addEventListener('click', prevSlide); 44 | nextButton.addEventListener('click', nextSlide); 45 | 46 | // Show first slide initially 47 | showSlide(0); 48 | 49 | // Automatic slide change 50 | setInterval(nextSlide, 5000); 51 | }); 52 | -------------------------------------------------------------------------------- /Instructions.txt: -------------------------------------------------------------------------------- 1 | First Install all the requirements. 2 | Then run app.py 3 | Then run index.html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Anshika Arora 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. -------------------------------------------------------------------------------- /OIP (10).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (10).jpeg -------------------------------------------------------------------------------- /OIP (11).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (11).jpeg -------------------------------------------------------------------------------- /OIP (12).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (12).jpeg -------------------------------------------------------------------------------- /OIP (13).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (13).jpeg -------------------------------------------------------------------------------- /OIP (5).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (5).jpeg -------------------------------------------------------------------------------- /OIP (6).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (6).jpeg -------------------------------------------------------------------------------- /OIP (7).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (7).jpeg -------------------------------------------------------------------------------- /OIP (8).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (8).jpeg -------------------------------------------------------------------------------- /OIP (9).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/OIP (9).jpeg -------------------------------------------------------------------------------- /Scroll_and_progressbar.css: -------------------------------------------------------------------------------- 1 | /* Custom Scrollbar */ 2 | ::-webkit-scrollbar { 3 | width: 15px; /* Width of the scrollbar */ 4 | } 5 | 6 | ::-webkit-scrollbar-track { 7 | background: #f0f0f0; /* Background color of the track */ 8 | border-radius: 10px; /* Rounded corners for the track */ 9 | } 10 | 11 | ::-webkit-scrollbar-thumb { 12 | background: linear-gradient(45deg, #cd61ff, #ec6371); /* Gradient color for the scrollbar */ 13 | border-radius: 10px; /* Rounded corners for the scrollbar */ 14 | box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.3); /* Adds a shadow effect */ 15 | } 16 | 17 | ::-webkit-scrollbar-thumb:hover { 18 | background: linear-gradient(45deg, #ff409c, #e763ec); /* Hover effect to change color */ 19 | cursor: pointer; /* Changes cursor to pointer when hovered */ 20 | } 21 | 22 | ::-webkit-scrollbar-thumb:active { 23 | background: linear-gradient(45deg, #b222ff, #ff0062); /* Color when scrollbar is clicked */ 24 | } 25 | 26 | /* Optional: smooth scrolling */ 27 | html { 28 | scroll-behavior: smooth; 29 | } 30 | 31 | /* Hide scrollbar but keep functionality */ 32 | .main-body { 33 | scrollbar-width: none; /* For Firefox */ 34 | -ms-overflow-style: none; /* For IE and Edge */ 35 | } 36 | 37 | .main-body::-webkit-scrollbar { 38 | display: none; /* For Chrome, Safari, and Opera */ 39 | } 40 | 41 | /* Progress Bar Container */ 42 | .progress-container { 43 | width: 100%; 44 | height: 10px; /* Height of the progress bar */ 45 | background-color: #f8b5b5; /* Background color matching scrollbar track */ 46 | border-radius: 10px; /* Rounded corners */ 47 | box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.2); /* Optional shadow for container */ 48 | /* position: fixed; */ 49 | overflow: hidden; 50 | } 51 | 52 | /* Progress Bar */ 53 | .progress-bar { 54 | width: 0%; /* Initial width (can be dynamically adjusted via JS) */ 55 | height: 100%; 56 | background: linear-gradient(45deg, #cd61ff, #ec6371); /* Gradient matching scrollbar */ 57 | border-radius: 10px; /* Rounded corners */ 58 | transition: width 0.3s ease-in-out; /* Smooth transition for width change */ 59 | box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.3); /* Adds shadow effect */ 60 | } 61 | 62 | /* Hover effect for the progress bar */ 63 | .progress-bar:hover { 64 | background: linear-gradient(45deg, #ff409c, #e763ec); /* Hover gradient color */ 65 | } 66 | 67 | /* Active state (when clicked or progressing) */ 68 | .progress-bar:active { 69 | background: linear-gradient(45deg, #b222ff, #ff0062); /* Active gradient color */ 70 | } -------------------------------------------------------------------------------- /SignIn_SignUp.js: -------------------------------------------------------------------------------- 1 | 2 | function openLogin() { 3 | document.getElementById("loginForm").style.display = "block"; 4 | closeForm("signup"); 5 | } 6 | 7 | function openSignup() { 8 | document.getElementById("signupForm").style.display = "block"; 9 | closeForm("login"); 10 | } 11 | 12 | function closeForm(formType) { 13 | if (formType === "login") { 14 | document.getElementById("loginForm").style.display = "none"; 15 | } else if (formType === "signup") { 16 | document.getElementById("signupForm").style.display = "none"; 17 | } 18 | } 19 | 20 | function checkLogin() { 21 | const isLoggedIn = localStorage.getItem('authToken'); // Example: Check for token 22 | const profileLink = document.getElementById('profileLink'); 23 | 24 | // Show "My Profile" if the user is logged in 25 | if (isLoggedIn) { 26 | profileLink.style.display = 'block'; 27 | } 28 | } 29 | 30 | // Toggle menu display 31 | function toggleProfileMenu() { 32 | const profileMenu = document.getElementById('profileMenu'); 33 | profileMenu.style.display = profileMenu.style.display === 'block' ? 'none' : 'block'; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /__pycache__/app.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/__pycache__/app.cpython-312.pyc -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify 2 | from transformers import T5Tokenizer, T5ForConditionalGeneration 3 | from flask_cors import CORS 4 | app = Flask(__name__) 5 | CORS(app) 6 | 7 | model_name = "deep-learning-analytics/GrammarCorrector" 8 | tokenizer = T5Tokenizer.from_pretrained(model_name) 9 | model = T5ForConditionalGeneration.from_pretrained(model_name) 10 | 11 | @app.route('/grammar-correct', methods=['POST']) 12 | def grammar_correct(): 13 | data = request.json 14 | input_text = data.get('text', '') 15 | 16 | input_ids = tokenizer(f"grammar: {input_text}", return_tensors="pt").input_ids 17 | 18 | 19 | outputs = model.generate(input_ids, max_length=256, num_beams=5, early_stopping=True) 20 | corrected_text = tokenizer.decode(outputs[0], skip_special_tokens=True) 21 | 22 | 23 | return jsonify({'corrected_text': corrected_text}) 24 | 25 | if __name__ == '__main__': 26 | app.run(debug=True, port=5000) 27 | -------------------------------------------------------------------------------- /assets/380x80/img1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img1.webp -------------------------------------------------------------------------------- /assets/380x80/img1_380x80.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img1_380x80.webp -------------------------------------------------------------------------------- /assets/380x80/img2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img2.webp -------------------------------------------------------------------------------- /assets/380x80/img2_380x80.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img2_380x80.webp -------------------------------------------------------------------------------- /assets/380x80/img3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img3.webp -------------------------------------------------------------------------------- /assets/380x80/img3_380x80.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img3_380x80.webp -------------------------------------------------------------------------------- /assets/380x80/img4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img4.webp -------------------------------------------------------------------------------- /assets/380x80/img4_380x80.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img4_380x80.webp -------------------------------------------------------------------------------- /assets/380x80/img5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img5.webp -------------------------------------------------------------------------------- /assets/380x80/img5_380x80.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/380x80/img5_380x80.webp -------------------------------------------------------------------------------- /assets/avatar.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/avatar.webp -------------------------------------------------------------------------------- /assets/face/face1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/face/face1.webp -------------------------------------------------------------------------------- /assets/face/face2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/face/face2.webp -------------------------------------------------------------------------------- /assets/face/face3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/face/face3.webp -------------------------------------------------------------------------------- /assets/fashion.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/fashion.webp -------------------------------------------------------------------------------- /assets/google_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/google_icon.webp -------------------------------------------------------------------------------- /assets/health.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/health.webp -------------------------------------------------------------------------------- /assets/img1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img1.webp -------------------------------------------------------------------------------- /assets/img10.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img10.webp -------------------------------------------------------------------------------- /assets/img11.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img11.webp -------------------------------------------------------------------------------- /assets/img12.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img12.webp -------------------------------------------------------------------------------- /assets/img13.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img13.webp -------------------------------------------------------------------------------- /assets/img14.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img14.webp -------------------------------------------------------------------------------- /assets/img2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img2.webp -------------------------------------------------------------------------------- /assets/img21.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img21.webp -------------------------------------------------------------------------------- /assets/img3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img3.webp -------------------------------------------------------------------------------- /assets/img4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img4.webp -------------------------------------------------------------------------------- /assets/img5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img5.webp -------------------------------------------------------------------------------- /assets/img6.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img6.webp -------------------------------------------------------------------------------- /assets/img7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img7.webp -------------------------------------------------------------------------------- /assets/img8.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img8.webp -------------------------------------------------------------------------------- /assets/img9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/img9.webp -------------------------------------------------------------------------------- /assets/slider/img1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/slider/img1.webp -------------------------------------------------------------------------------- /assets/slider/img2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/slider/img2.webp -------------------------------------------------------------------------------- /assets/slider/img3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/slider/img3.webp -------------------------------------------------------------------------------- /assets/slider/img4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/slider/img4.webp -------------------------------------------------------------------------------- /assets/slider/img5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/slider/img5.webp -------------------------------------------------------------------------------- /assets/slider/img6.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/slider/img6.webp -------------------------------------------------------------------------------- /assets/slider/img7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/slider/img7.webp -------------------------------------------------------------------------------- /assets/start.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/assets/start.webp -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | # MONGO_URI=mongodb://localhost:27017/bloggingdb 2 | MONGO_URI=mongodb://localhost:27017/bloggingdb 3 | JWT_SECRET=scijyasfy7dsvegdffvbfbfgg435tgrsnbgfgn 4 | PORT=5000 5 | EMAIL_ID= 6 | PASS_KEY= 7 | ADMIN_EMAIL_ID= 8 | PALM_API_KEY= -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | uploads 4 | .env -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # Wordwise API Documentation 2 | 3 | ## Overview 4 | 5 | This API allows users to register, log in, create, update, and delete blog posts. It uses JSON Web Tokens (JWT) for authentication. Admin users have special privileges to manage other users and blogs. 6 | 7 | ### Base URL 8 | 9 | http://localhost:5000 10 | 11 | *__(this url is used for the development process not in the production)__* 12 | 13 | ## Authentication 14 | 15 | All routes that require authentication expect a JWT token in the `Authorization` header: 16 | 17 | Authorization: Bearer 18 | 19 | ## Endpoints 20 | 21 | ### User Routes 22 | 23 | #### 1. **Register a New User** 24 | 25 | - **URL:** `/api/users/register` 26 | - **Method:** `POST` 27 | - **Description:** Register a new user. 28 | - **Request Body:** 29 | - **Req**: 30 | 31 | ```json 32 | { 33 | "username": "john_doe", 34 | "email": "john@example.com", 35 | "password": "password123" 36 | } 37 | ``` 38 | - **Response**: 39 | ```json 40 | { 41 | "message": "User registered successfully!", 42 | "token": "" 43 | } 44 | ``` 45 | 46 | #### 2. **Login a User** 47 | - **URL**: /api/users/login 48 | - **Method**: `POST` 49 | - **Description**: Log in an existing user. 50 | - **Request Body**: 51 | ```json 52 | { 53 | "email": "john@example.com", 54 | "password": "password123" 55 | } 56 | ``` 57 | - **Response**: 58 | ```json 59 | { 60 | "message": "Login successful!", 61 | "token": "", 62 | "user": { 63 | "id": "user_id", 64 | "username": "john_doe", 65 | "email": "john@example.com" 66 | } 67 | } 68 | ``` 69 | 70 | #### **3. Get User Profile** 71 | - **URL**: /api/users/profile 72 | - **Method**: GET 73 | - **Description**: Fetch the logged-in user's profile. (Authentication required) 74 | - **Headers**: `Authorization`: `Bearer ` 75 | - **Response**: 76 | ```json 77 | { 78 | "id": "user_id", 79 | "username": "john_doe", 80 | "email": "john@example.com" 81 | } 82 | ``` 83 | 84 | #### **4. Get All Users (Admin Only)** 85 | - **URL**: /api/users 86 | - **Method**: GET 87 | - **Description**: Fetch a list of all users. (Admin authentication required) 88 | - **Headers**: `Authorization`: `Bearer ` 89 | - **Response**: 90 | 91 | ```json 92 | [ 93 | { 94 | "id": "user_id_1", 95 | "username": "john_doe", 96 | "email": "john@example.com" 97 | }, 98 | { 99 | "id": "user_id_2", 100 | "username": "jane_doe", 101 | "email": "jane@example.com" 102 | } 103 | ] 104 | ``` 105 | 106 | ### Blog Routes 107 | 108 | #### **1. Create a Blog Post** 109 | - **URL**: /api/blogs 110 | - **Method**: POST 111 | - **Description**: Create a new blog post. (Authentication required) 112 | - **Headers**: `Authorization`: `Bearer ` 113 | - **Request Body**: 114 | 115 | ```json 116 | { 117 | "title": "How to Learn JavaScript", 118 | "topic": "Programming", 119 | "image": "https://example.com/js-image.png", 120 | "content": "JavaScript is a versatile language...", 121 | "writer": "John Doe" 122 | } 123 | ``` 124 | - **Response**: 125 | ```json 126 | { 127 | "message": "Blog post created successfully!", 128 | "blog": { 129 | "id": "blog_id", 130 | "title": "How to Learn JavaScript", 131 | "topic": "Programming", 132 | "image": "https://example.com/js-image.png", 133 | "content": "JavaScript is a versatile language...", 134 | "writer": "John Doe" 135 | } 136 | } 137 | ``` 138 | 139 | #### **2. Update a Blog Post** 140 | 141 | - **URL**: /api/blogs/:id 142 | - **Method**: PUT 143 | - **Description**: Update an existing blog post. (Authentication required) 144 | - **Headers**: `Authorization`: `Bearer ` 145 | - **Request Body**: 146 | ```json 147 | { 148 | "title": "Updated JavaScript Guide", 149 | "topic": "Programming", 150 | "image": "https://example.com/updated-js-image.png", 151 | "content": "This guide has been updated to include ES6...", 152 | "writer": "John Doe" 153 | } 154 | ``` 155 | Response: 156 | ```json 157 | { 158 | "message": "Blog post updated successfully!", 159 | "blog": { 160 | "id": "blog_id", 161 | "title": "Updated JavaScript Guide", 162 | "topic": "Programming", 163 | "image": "https://example.com/updated-js-image.png", 164 | "content": "This guide has been updated to include ES6...", 165 | "writer": "John Doe" 166 | } 167 | } 168 | ``` 169 | 170 | ### **3. Delete a Blog Post (Admin Only)** 171 | **URL**: /api/blogs/:id 172 | **Method**: DELETE 173 | **Description**: Delete a blog post. (Admin authentication required) 174 | **Headers**: `Authorization`: `Bearer ` 175 | Response: 176 | ```json 177 | { 178 | "message": "Blog post deleted successfully!" 179 | } 180 | ``` 181 | Error Handling 182 | All error responses follow the same structure: 183 | 184 | ```json 185 | { 186 | "error": "Error message explaining the problem" 187 | } 188 | ``` 189 | ### Example Flow for Testing 190 | - **Register a User**: Send a `POST` request to `/api/users/register` with registration data. 191 | - **Login**: Send a `POST` request to `/api/users/login` with the login data and get a JWT token. 192 | - **Create a Blog**: Send a `POST` request to `/api/blogs` with the JWT token to create a blog. 193 | - **Update a Blog**: Send a `PUT` request to `/api/blogs/:id` with updated data. 194 | - **Delete a Blog (Admin Only)**: Send a `DELETE` request to `/api/blogs/:id` with the admin token. -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import dotenv from "dotenv"; 3 | import connectDB from "./utils/db.js"; 4 | import blogRoutes from "./routes/blogRoutes.js"; 5 | import userRoutes from "./routes/userRoutes.js"; 6 | import feedbackRoutes from "./routes/feedbackRoute.js"; 7 | import contactRoutes from "./routes/contactRoute.js"; 8 | import ratingRoutes from "./routes/ratingRoutes.js"; 9 | import getInTouch from "./routes/getInTouchRoutes.js"; 10 | import addBlog from "./routes/addBlogRoutes.js"; 11 | import subscribe from "./routes/subscribeRoutes.js"; 12 | import discussion from "./routes/discussionRoutes.js"; 13 | import faqRoutes from "./routes/faqRoutes.js"; 14 | import stories from "./routes/storiesRoutes.js"; 15 | 16 | 17 | import cors from "cors"; 18 | import path from "path"; // Import path module 19 | import { fileURLToPath } from "url"; // Import fileURLToPath 20 | 21 | dotenv.config(); 22 | const app = express(); 23 | connectDB(); 24 | 25 | app.use(express.json()); 26 | 27 | // to avoid cross-origin error 28 | app.use(cors()); 29 | 30 | // Define __dirname for ES module 31 | const __filename = fileURLToPath(import.meta.url); 32 | const __dirname = path.dirname(__filename); 33 | 34 | // Serve static files from the uploads directory 35 | app.use("/uploads", express.static(path.join(__dirname, "uploads"))); // Adjust path as necessary 36 | 37 | app.use("/api/users", userRoutes); 38 | app.use("/api/blogs", blogRoutes); 39 | app.use("/api/feedback", feedbackRoutes); 40 | app.use("/api/rating", ratingRoutes); 41 | app.use("/api/contact", contactRoutes); 42 | app.use("/api/getInTouch", getInTouch); 43 | app.use("/api/addBlog", addBlog); 44 | app.use("/api/newsletter", subscribe); 45 | app.use("/api/discussion", discussion); 46 | app.use("/api/faq", faqRoutes); 47 | app.use("/api/stories", stories); 48 | 49 | 50 | const PORT = process.env.PORT || 5000; 51 | app.listen(PORT, () => { 52 | console.log(`Server running on port ${PORT}`); 53 | }); 54 | -------------------------------------------------------------------------------- /backend/controllers/addBlogController.js: -------------------------------------------------------------------------------- 1 | import BlogPost from '../models/addBlog.js'; 2 | import multer from 'multer'; 3 | import path from 'path'; 4 | import fs from 'fs'; 5 | 6 | // Configure multer for file uploads 7 | const storage = multer.diskStorage({ 8 | destination: (req, file, cb) => { 9 | cb(null, 'uploads/'); // Ensure this directory exists 10 | }, 11 | filename: (req, file, cb) => { 12 | // Set the file name to be unique 13 | const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); 14 | cb(null, uniqueSuffix + path.extname(file.originalname)); // Preserve the original file extension 15 | } 16 | }); 17 | 18 | const upload = multer({ storage: storage }); 19 | 20 | // Function to save a new blog post 21 | export async function saveBlog(req, res) { 22 | try { 23 | const { title, category, summary, excerpt, tags, publish, likes, featuredImage } = req.body; 24 | 25 | // If an image is uploaded, use its path 26 | let imagePath = req.file ? req.file.path : null; 27 | 28 | // Handle base64 image if provided 29 | if (featuredImage && featuredImage.startsWith('data:image/')) { 30 | // Extract the base64 part of the image data 31 | const base64Image = featuredImage.split(';base64,').pop(); 32 | // Convert base64 string to Buffer 33 | const buffer = Buffer.from(base64Image, 'base64'); 34 | 35 | // Create a path to save the image 36 | const imagePathFromBase64 = `uploads/${Date.now()}.png`; // Use a unique name based on timestamp 37 | 38 | // Save the image buffer to a file 39 | await fs.promises.writeFile(imagePathFromBase64, buffer); 40 | imagePath = imagePathFromBase64; // Set imagePath to the saved file path 41 | } 42 | 43 | // Log request body and file info 44 | 45 | 46 | // Check if required fields are provided 47 | if (!title || !category || !summary || !excerpt) { 48 | return res.status(400).json({ message: "All required fields must be filled out." }); 49 | } 50 | 51 | // Create a new blog document 52 | const newBlog = new BlogPost({ 53 | title, 54 | category, 55 | summary, 56 | excerpt, 57 | tags, 58 | publish, 59 | likes, 60 | featuredImage: imagePath // Use the determined image path 61 | }); 62 | 63 | console.log(newBlog); 64 | 65 | // Save the blog post to the database 66 | await newBlog.save(); 67 | 68 | // Respond with success message 69 | res.status(201).json({ 70 | message: "Blog post created successfully!", 71 | data: newBlog 72 | }); 73 | } catch (error) { 74 | console.error("Error saving blog post:", error); 75 | res.status(500).json({ message: "Failed to create blog post.", error }); 76 | } 77 | } 78 | 79 | 80 | // Function to retrieve all blog posts 81 | export async function getAllBlog(req, res) { 82 | try { 83 | const blogs = await BlogPost.find(); // Retrieve all blog posts 84 | res.status(200).json(blogs); // Respond with the list of blog posts 85 | } catch (error) { 86 | console.error("Error retrieving blog posts:", error); 87 | res.status(500).json({ message: "Failed to retrieve blog posts.", error }); 88 | } 89 | } 90 | 91 | // Function to retrieve a specific blog post by ID 92 | export async function getBlog(req, res) { 93 | try { 94 | const { id } = req.params; 95 | 96 | // Retrieve the specific blog post by ID 97 | const blog = await BlogPost.findById(id); 98 | if (!blog) { 99 | return res.status(404).json({ message: "Blog post not found." }); 100 | } 101 | return res.status(200).json(blog); 102 | } catch (error) { 103 | console.error("Error retrieving blog post:", error); 104 | res.status(500).json({ message: "Failed to retrieve blog post.", error }); 105 | } 106 | } 107 | 108 | 109 | 110 | export async function updateLikes(req, res) { 111 | const { postId, liked } = req.body; // Destructure liked from the body 112 | console.log(postId + " " + liked); 113 | 114 | try { 115 | // Find the post by ID 116 | const post = await BlogPost.findById(postId); 117 | console.log(post); 118 | 119 | if (!post) { 120 | return res.status(404).json({ message: "Post not found" }); 121 | } 122 | 123 | // Update the likes count based on the "liked" value 124 | post.likes = liked ? post.likes + 1 : Math.max(post.likes - 1, 0); // Update post.likes, not BlogPost.likes 125 | 126 | // Save the updated post 127 | await post.save(); 128 | 129 | // Respond with the new likes count 130 | return res.status(200).json({ likesCount: post.likes }); // Use post.likes 131 | } catch (error) { 132 | console.error("Error updating likes:", error); 133 | return res.status(500).json({ message: "Failed to update likes" }); 134 | } 135 | } 136 | 137 | 138 | 139 | export { upload }; -------------------------------------------------------------------------------- /backend/controllers/blogController.js: -------------------------------------------------------------------------------- 1 | import Blog from "../models/blog.js"; 2 | import Comment from "../models/comment.js"; 3 | 4 | export const createBlog = async (req, res) => { 5 | try { 6 | const { title, topic, image, content, author } = req.body; 7 | const newBlog = new Blog({ title, topic, image, content, author }); 8 | await newBlog.save(); 9 | res.status(201).json(newBlog); 10 | } catch (error) { 11 | res.status(500).json({ message: error.message }); 12 | } 13 | }; 14 | 15 | export const updateBlog = async (req, res) => { 16 | try { 17 | const { id } = req.params; 18 | const blog = await Blog.findByIdAndUpdate(id, req.body, { new: true }); 19 | if (!blog) return res.status(404).json({ message: "Blog not found" }); 20 | res.status(200).json(blog); 21 | } catch (error) { 22 | res.status(500).json({ message: error.message }); 23 | } 24 | }; 25 | 26 | export const deleteBlog = async (req, res) => { 27 | try { 28 | const { id } = req.params; 29 | const blog = await Blog.findByIdAndDelete(id); 30 | if (!blog) return res.status(404).json({ message: "Blog not found" }); 31 | res.status(200).json({ message: "Blog deleted" }); 32 | } catch (error) { 33 | res.status(500).json({ message: error.message }); 34 | } 35 | }; 36 | 37 | export const getAllBlogs = async (req, res) => { 38 | try { 39 | const blogs = await Blog.find({}); 40 | res.status(200).json(blogs); 41 | } catch (error) { 42 | res.status(500).json({ message: error.message }); 43 | } 44 | }; 45 | 46 | export const getSingleBlog = async (req, res) => { 47 | try { 48 | const { id } = req.params; 49 | const blog = await Blog.findById(id); 50 | if (!blog) return res.status(404).json({ message: "Blog not found" }); 51 | res.status(200).json(blog); 52 | } catch (error) { 53 | res.status(500).json({ message: error.message }); 54 | } 55 | }; 56 | 57 | export const saveComment = async (req, resp) => { 58 | try { 59 | const { name, comment } = req.body; 60 | 61 | if (!name || !comment) { 62 | return resp.status(400).send({ 63 | message: "all fields are required", 64 | success: false, 65 | }); 66 | } 67 | 68 | const newcomment = await new Comment({ 69 | name: name, 70 | comment: comment, 71 | }); 72 | 73 | newcomment.save(); 74 | 75 | if (newcomment) { 76 | return resp.status(200).send({ 77 | success: true, 78 | message: "new comment added", 79 | newcomment, 80 | }); 81 | } 82 | } catch (error) { 83 | console.log(error); 84 | return resp.status(500).send({ 85 | success: false, 86 | message: "internal server error", 87 | error, 88 | }); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /backend/controllers/contactController.js: -------------------------------------------------------------------------------- 1 | import Contact from "../models/contact.js"; 2 | import nodemailer from "nodemailer"; 3 | 4 | export async function saveContact(req, resp) { 5 | try { 6 | const { name, email, subject, message } = req.body; 7 | 8 | // Check if all fields are provided 9 | if (!name || !email || !subject || !message) { 10 | return resp.status(400).json({ message: "All fields are required." }); 11 | } 12 | 13 | // Create new contact document 14 | const newContact = new Contact({ name, email, subject, message }); 15 | 16 | // Save contact form data to the database 17 | await newContact.save(); 18 | 19 | // Respond with success message 20 | resp 21 | .status(201) 22 | .json({ message: "Contact form submitted successfully!", newContact }); 23 | } catch (error) { 24 | console.error("Error saving contact form:", error); 25 | resp.status(500).json({ message: "Failed to submit contact form.", error }); 26 | } 27 | } 28 | 29 | export async function getContact(req, resp) { 30 | resp.send("hello contact"); 31 | } 32 | 33 | export async function newsletter(req, resp) { 34 | try { 35 | const { email } = req.body; 36 | 37 | if (!email) { 38 | return resp.status(400).json({ message: "Email is required" }); 39 | } 40 | 41 | const transporter = nodemailer.createTransport({ 42 | service: "gmail", 43 | auth: { 44 | user: "taskmaster991@gmail.com", 45 | pass: "kmepakzcabvztekd", 46 | }, 47 | }); 48 | 49 | const mailOptions = { 50 | from: process.env.EMAIL_USER, 51 | to: email, 52 | subject: "Thank you for Subscribing to WORDWISE", 53 | html: ` 54 |
55 | 56 |
57 |

WordWise

58 |

Welcome to Our Newsletter

59 |
60 | 61 | 62 |
63 |

Thank You for Subscribing!

64 |

Dear Subscriber,

65 |

We are thrilled to have you with us. Stay tuned for our latest updates, offers, and insights to keep you informed and inspired!

66 | 67 | 68 | 74 |
75 | 76 | 77 |
78 |

Best Regards,
WORDWISE Team

79 |

© ${new Date().getFullYear()} WORDWISE. All rights reserved.

80 |
81 |
82 | `, 83 | }; 84 | 85 | // Send the email 86 | await transporter.sendMail(mailOptions); 87 | 88 | resp.status(200).json({ message: "Newsletter email sent successfully!" }); 89 | } catch (error) { 90 | console.error("Error sending newsletter:", error); 91 | resp.status(500).json({ message: "Error sending newsletter" }); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /backend/controllers/discussionController.js: -------------------------------------------------------------------------------- 1 | import Question from '../models/discussion.js'; 2 | 3 | export const getQuestions = async (req, res) => { 4 | try { 5 | const questions = await Question.find(); 6 | res.json(questions); 7 | } catch (error) { 8 | res.status(500).json({ error: 'Failed to retrieve questions' }); 9 | } 10 | }; 11 | 12 | // Save a new question to MongoDB 13 | export const saveQuestion = async (req, res) => { 14 | try { 15 | const { content } = req.body; 16 | 17 | const newQuestion = new Question({ 18 | content, 19 | }); 20 | 21 | await newQuestion.save(); 22 | 23 | res.status(201).json(newQuestion); 24 | } catch (error) { 25 | res.status(500).json({ error: 'Failed to save question' }); 26 | } 27 | }; 28 | 29 | export const addAnswerToQuestion = async (req, res) => { 30 | try { 31 | const questionId = req.params.id; 32 | const { answer } = req.body; 33 | 34 | 35 | const updatedQuestion = await Question.findByIdAndUpdate( 36 | questionId, 37 | { answered: true, answer }, 38 | { new: true } 39 | ); 40 | 41 | if (updatedQuestion) { 42 | res.json(updatedQuestion); 43 | } else { 44 | res.status(404).json({ error: 'Question not found' }); 45 | } 46 | } catch (error) { 47 | res.status(500).json({ error: 'Failed to update question' }); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /backend/controllers/faqController.js: -------------------------------------------------------------------------------- 1 | import QuestionModel from "../models/question.js"; 2 | import Answer from "../models/answers.js"; 3 | export const createQuestion = async (req, res) => { 4 | try { 5 | const { content } = req.body; 6 | 7 | // Check if the question already exists 8 | const existingQuestion = await QuestionModel.findOne({ content }); 9 | if (existingQuestion) { 10 | return res.status(400).json({ message: "Question already exists" }); 11 | } 12 | 13 | // Create the new question 14 | const newQuestion = new QuestionModel({ content }); 15 | await newQuestion.save(); 16 | 17 | res.status(201).json(newQuestion); 18 | } catch (error) { 19 | res.status(500).json({ message: error.message }); 20 | } 21 | }; 22 | 23 | // Route to fetch questions based on 'answered' or 'unanswered' query 24 | export const getQuestions = async (req, res) => { 25 | try { 26 | const question = req.query.question; 27 | 28 | let query; 29 | if (question === "answered") { 30 | // Fetch questions with at least one answer, and include the answers 31 | query = QuestionModel.find({ answered: true }).populate("answers"); 32 | 33 | } else if (question === "unanswered") { 34 | // Fetch questions with no answers 35 | query = QuestionModel.find({ answered: false }).populate("answers"); 36 | } else { 37 | // If no specific filter is provided, fetch all questions 38 | query = QuestionModel.find(); 39 | } 40 | 41 | const questions = await query.exec(); 42 | if(!question){ 43 | res.status(200).json({message:"No response"}) 44 | } 45 | res.status(200).json(questions); 46 | } catch (error) { 47 | res.status(500).json({ message: error.message }); 48 | } 49 | }; 50 | 51 | // Example route to create a new question with an optional answer 52 | 53 | export const answerQuestion = async (req, res) => { 54 | try { 55 | const { questionId } = req.body; // The ID of the question to answer 56 | const { content } = req.body; 57 | 58 | // Check if the question exists 59 | const existingQuestion = await QuestionModel.findById(questionId); 60 | if (!existingQuestion) { 61 | return res.status(404).json({ message: "Question not found" }); 62 | } 63 | 64 | // Create a new answer linked to the question 65 | const newAnswer = new Answer({ 66 | content, 67 | question: questionId, 68 | }); 69 | await newAnswer.save(); 70 | 71 | // Add the answer ID to the question's answers array 72 | existingQuestion.answers.push(newAnswer._id); 73 | existingQuestion.answered=true; 74 | await existingQuestion.save(); 75 | 76 | res.status(201).json({ message: "Answer added successfully", answer: newAnswer }); 77 | } catch (error) { 78 | res.status(500).json({ message: error.message }); 79 | } 80 | }; -------------------------------------------------------------------------------- /backend/controllers/feedController.js: -------------------------------------------------------------------------------- 1 | import Feedback from "../models/feedback.js"; 2 | import { sendMailToAdmin } from "../sendFeedbackToAdmin.js"; 3 | 4 | // Controller to handle feedback submission 5 | export async function submitFeedback(req, res) { // Changed resp to res 6 | console.log('feedback form getting submit') 7 | try { 8 | // Extract feedback data from the request body 9 | const { 10 | overallExperience, 11 | featuresUsed, 12 | mostHelpfulFeature, 13 | improvement, 14 | newFeatures, 15 | recommendation, 16 | additionalComments 17 | } = req.body; 18 | 19 | // Validate required fields 20 | if (!overallExperience || !featuresUsed || !mostHelpfulFeature || !recommendation) { 21 | return res.status(400).json({ message: 'Please provide all required fields.' }); 22 | } 23 | 24 | // Create a new Feedback instance with the extracted data 25 | const feedback = new Feedback({ 26 | overallExperience, 27 | featuresUsed, 28 | mostHelpfulFeature, 29 | improvement, 30 | newFeatures, 31 | recommendation, 32 | additionalComments 33 | }); 34 | 35 | // Save the feedback data to MongoDB 36 | await feedback.save(); 37 | sendMailToAdmin(feedback) 38 | // Respond with a success message 39 | res.status(201).json({ message: 'Feedback submitted successfully', feedback }); 40 | } catch (error) { 41 | // Handle errors and send a failure response 42 | console.error('Error saving feedback:', error); 43 | res.status(500).json({ message: 'Failed to submit feedback', error: error.message }); 44 | } 45 | } 46 | 47 | 48 | export async function getFeed(req, res) { 49 | res.send("feedback form") 50 | } -------------------------------------------------------------------------------- /backend/controllers/getInTouchController.js: -------------------------------------------------------------------------------- 1 | // Import the GetInTouch model 2 | import GetInTouch from '../models/getInTouch.js'; 3 | 4 | // Controller function to handle form submission 5 | export const submitGetInTouch = async (req, res) => { 6 | try { 7 | // Create a new GetInTouch document using request data 8 | const { name, email, message } = req.body; 9 | 10 | // Validate request data 11 | if (!name || !email || !message) { 12 | return res.status(400).json({ error: 'All fields are required.' }); 13 | } 14 | 15 | const newMessage = new GetInTouch({ 16 | name, 17 | email, 18 | message, 19 | }); 20 | 21 | // Save the new document to the database 22 | await newMessage.save(); 23 | 24 | // Send a success response 25 | res.status(201).json({ message: 'Thank you! Your message has been received.' }); 26 | } catch (error) { 27 | // Handle errors 28 | console.error('Error submitting message:', error); 29 | res.status(500).json({ error: 'There was an error submitting your message. Please try again.' }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /backend/controllers/postsController.js: -------------------------------------------------------------------------------- 1 | // postsController.js 2 | import Post from "../models/postModel.js"; 3 | 4 | // Fetch all posts 5 | export const getPosts = async (req, res) => { 6 | try { 7 | const posts = await Post.find(); 8 | res.status(200).json(posts); 9 | } catch (error) { 10 | res.status(500).json({ message: "Error fetching posts", error }); 11 | } 12 | }; 13 | 14 | // Save a new post 15 | export const savePost = async (req, res) => { 16 | const { title, content, category, date } = req.body; 17 | 18 | try { 19 | const newPost = new Post({ title, content, category, date }); 20 | const savedPost = await newPost.save(); 21 | res.status(201).json(savedPost); 22 | } catch (error) { 23 | res.status(500).json({ message: "Error saving post", error }); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /backend/controllers/ratingController.js: -------------------------------------------------------------------------------- 1 | // controllers/feedbackController.js 2 | 3 | import Rating from '../models/rating.js'; 4 | import nodemailer from 'nodemailer'; 5 | 6 | // Configure nodemailer transporter 7 | const transporter = nodemailer.createTransport({ 8 | service: 'gmail', // Example with Gmail, customize as needed 9 | auth: { 10 | user: 'your-email@gmail.com', 11 | pass: 'your-email-password', 12 | }, 13 | }); 14 | 15 | const saveRating = async (req, res) => { 16 | const { name, email, message, rating } = req.body; 17 | 18 | try { 19 | // Save feedback to the database 20 | const feedback = new Rating({ name, email, message, rating }); 21 | await feedback.save(); 22 | 23 | // Send confirmation email to the user 24 | const mailOptions = { 25 | from: 'your-email@gmail.com', 26 | to: email, 27 | subject: 'Thank you for your feedback!', 28 | text: `Dear ${name},\n\nThank you for your valuable feedback!\n\nYour Feedback:\n"${message}"\n\nRating: ${rating} stars\n\nBest regards,\nYour Company`, 29 | }; 30 | 31 | transporter.sendMail(mailOptions, (error, info) => { 32 | if (error) { 33 | console.error('Error sending email:', error); 34 | return res.status(500).json({ message: 'Feedback saved but failed to send email' }); 35 | } else { 36 | console.log('Email sent:', info.response); 37 | return res.status(200).json({ message: 'Feedback saved and email sent successfully!' }); 38 | } 39 | }); 40 | } catch (error) { 41 | console.error('Error saving feedback:', error); 42 | res.status(500).json({ message: 'Failed to save feedback' }); 43 | } 44 | }; 45 | 46 | export default saveRating -------------------------------------------------------------------------------- /backend/controllers/subscribeController.js: -------------------------------------------------------------------------------- 1 | import Suscribe from "../models/subscribe.js"; 2 | import { sendMailToSubscriber } from "../utils/sendMailToSubscribe.js"; 3 | export async function saveSubsribe(req, res) { 4 | try { 5 | const { name, email } = req.body; 6 | 7 | if (!name || !email) { 8 | return res.status(400).json({ message: "All fields are required." }); 9 | } 10 | 11 | // Create new contact document 12 | const newSuscribe = new Suscribe({ name, email }); 13 | sendMailToSubscriber(newSuscribe) 14 | await newSuscribe.save(); 15 | res 16 | .status(201) 17 | .json({ message: "Contact form submitted successfully!", newSuscribe }); 18 | } catch (error) { 19 | console.error("Error saving contact form:", error); 20 | res.status(500).json({ message: "Failed to submit contact form.", error }); 21 | } 22 | } 23 | 24 | export async function getSubscribe(req, res) { 25 | res.send('hello subscriber') 26 | } 27 | -------------------------------------------------------------------------------- /backend/generateFromAi.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify 2 | from flask_cors import CORS 3 | import google.generativeai as palm 4 | import os 5 | from dotenv import load_dotenv 6 | 7 | app = Flask(__name__) 8 | CORS(app) # Enable CORS for all routes 9 | 10 | # Load API key from .env file 11 | load_dotenv() 12 | palm_api_key = os.getenv('PALM_API_KEY') # Get your API key from environment variable 13 | if not palm_api_key: 14 | raise ValueError("API key is missing. Please set your API key in the environment variables.") 15 | palm.configure(api_key=palm_api_key) 16 | 17 | @app.route('/generate_trending_titles', methods=['GET']) 18 | def generate_trending_titles(): 19 | try: 20 | prompt = """ 21 | You are a content generator specialized in creating catchy and engaging blog titles. 22 | Please generate 20 trending blog titles that would appeal to a broad audience in the current time. 23 | The titles should be innovative, popular, and attention-grabbing. 24 | """ 25 | model = palm.GenerativeModel('gemini-pro') 26 | response = model.generate_content(prompt) 27 | blog_titles = response.text.strip().split("\n") 28 | return jsonify({"trending_titles": blog_titles[:10]}), 200 29 | except Exception as e: 30 | print("Error:", str(e)) 31 | return jsonify({"error": "An error occurred while generating the blog titles."}), 500 32 | 33 | # New endpoint to generate content based on the title 34 | @app.route('/generate-content', methods=['POST']) 35 | def generate_content(): 36 | try: 37 | # Get the title and summary from the request 38 | data = request.get_json() 39 | title = data.get('title', '') 40 | summary = data.get('summary', '') 41 | 42 | # Check if both title and summary are provided 43 | if not title and not summary: 44 | return jsonify({"error": "Title and summary are required"}), 400 45 | 46 | # Define a prompt using the title and summary to generate content 47 | prompt = ( 48 | f"Write a detailed and informative blog post on the topic: '{title}'. " 49 | f"Use the following summary as a reference: '{summary}'. " 50 | "Make it engaging and informative, and elaborate based on the provided summary in 200 words." 51 | "Generate the output in the following format: " 52 | "" 53 | "<5 category in single line each category should be comma seperated>" 54 | "<3 tags in single line each tags should be comma seperated>" 55 | ) 56 | 57 | 58 | 59 | # Call the generative model with the title and summary prompt 60 | model = palm.GenerativeModel('gemini-pro') 61 | response = model.generate_content(prompt) 62 | 63 | # Extract the generated content 64 | content = response.text.strip() if response and response.text else "No content generated." 65 | 66 | # Return the content as a JSON response 67 | return jsonify({"content": content}), 200 68 | 69 | except Exception as e: 70 | print("Error:", str(e)) 71 | return jsonify({"error": "An error occurred while generating the content."}), 500 72 | 73 | 74 | except Exception as e: 75 | print("Error:", str(e)) 76 | return jsonify({"error": "An error occurred while generating the content."}), 500 77 | 78 | @app.route('/analyze', methods=['GET']) 79 | def health_check(): 80 | return jsonify({"message": "Server running!"}), 200 81 | 82 | if __name__ == "__main__": 83 | app.run(debug=True, port=8000) 84 | -------------------------------------------------------------------------------- /backend/middlewares/adminMiddleware.js: -------------------------------------------------------------------------------- 1 | const adminMiddleware = (req, res, next) => { 2 | if (req.user && req.user.isAdmin) { 3 | next(); 4 | } else { 5 | res.status(403).json({ message: "Admin access only" }); 6 | } 7 | }; 8 | 9 | export default adminMiddleware; 10 | -------------------------------------------------------------------------------- /backend/middlewares/authMiddleware.js: -------------------------------------------------------------------------------- 1 | import jwt from "jsonwebtoken"; 2 | import dotenv from "dotenv"; 3 | dotenv.config(); 4 | 5 | const authMiddleware = (req, res, next) => { 6 | const token = req.header("Authorization")?.replace("Bearer ", ""); 7 | if (!token) { 8 | return res.status(401).json({ message: "No token, authorization denied" }); 9 | } 10 | 11 | try { 12 | const decoded = jwt.verify(token, process.env.JWT_SECRET); 13 | req.user = decoded; 14 | next(); 15 | } catch (err) { 16 | res.status(401).json({ message: "Token is not valid" }); 17 | } 18 | }; 19 | 20 | export default authMiddleware; 21 | -------------------------------------------------------------------------------- /backend/models/addBlog.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const blogPostSchema = new mongoose.Schema({ 4 | title: { 5 | type: String, 6 | required: true, 7 | trim: true 8 | }, 9 | category: { 10 | type: String, 11 | required: true, 12 | }, 13 | summary: { 14 | type: String, 15 | required: true, 16 | trim: true 17 | }, 18 | excerpt: { 19 | type: String, 20 | required: true, 21 | trim: true 22 | }, 23 | tags: { 24 | type: [String], 25 | default: [] 26 | }, 27 | publish: { 28 | type: Boolean, 29 | default: false 30 | }, 31 | featuredImage: { 32 | type: String, 33 | default: null 34 | }, 35 | likes: { 36 | type: Number, 37 | default: 0 38 | }, 39 | createdAt: { 40 | type: Date, 41 | default: Date.now 42 | }, 43 | updatedAt: { 44 | type: Date, 45 | default: Date.now 46 | } 47 | }); 48 | 49 | // Add a pre-save hook to update the `updatedAt` field 50 | blogPostSchema.pre('save', function (next) { 51 | this.updatedAt = Date.now(); 52 | next(); 53 | }); 54 | 55 | const BlogPost = mongoose.model('BlogPost', blogPostSchema); 56 | 57 | export default BlogPost; 58 | -------------------------------------------------------------------------------- /backend/models/answers.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import QuestionModel from './question.js'; 3 | 4 | const answerSchema = new mongoose.Schema({ 5 | content:{ 6 | type:String 7 | }, 8 | 9 | }); 10 | 11 | const answer = mongoose.model("Answer", answerSchema); 12 | export default answer; -------------------------------------------------------------------------------- /backend/models/blog.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const blogSchema = new mongoose.Schema({ 4 | title: { type: String, required: true }, 5 | topic: { type: String, required: true }, 6 | image: { type: String, required: true }, 7 | content: { type: String, required: true }, 8 | author: { type: String, required: true }, 9 | createdAt: { type: Date, default: Date.now }, 10 | }); 11 | 12 | const Blog = mongoose.model("Blog", blogSchema); 13 | export default Blog; 14 | -------------------------------------------------------------------------------- /backend/models/comment.js: -------------------------------------------------------------------------------- 1 | import { mongoose } from "mongoose"; 2 | 3 | const commentSchema = new mongoose.Schema( 4 | { 5 | name: { 6 | type: String, 7 | required: true, 8 | trim: true, 9 | }, 10 | comment: { 11 | type: String, 12 | required: true, 13 | trim: true, 14 | }, 15 | }, 16 | { timestamps: true } 17 | ); 18 | 19 | const Comment = mongoose.model("Comment", commentSchema); 20 | 21 | export default Comment; 22 | -------------------------------------------------------------------------------- /backend/models/contact.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const contactSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true, 7 | trim: true 8 | }, 9 | email: { 10 | type: String, 11 | required: true, 12 | trim: true, 13 | match: [/^\S+@\S+\.\S+$/, "Please enter a valid email address"] 14 | }, 15 | subject: { 16 | type: String, 17 | required: true, 18 | trim: true 19 | }, 20 | message: { 21 | type: String, 22 | required: true, 23 | trim: true 24 | }, 25 | createdAt: { 26 | type: Date, 27 | default: Date.now 28 | } 29 | }); 30 | 31 | const Contact = mongoose.model("Contact", contactSchema); 32 | 33 | export default Contact; 34 | -------------------------------------------------------------------------------- /backend/models/discussion.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | // Define the schema for a question 4 | const discussionSchema = new mongoose.Schema({ 5 | content: { 6 | type: String, 7 | required: true, 8 | }, 9 | answered: { 10 | type: Boolean, 11 | default: false, 12 | }, 13 | answer: { 14 | type: String, 15 | default: '', 16 | }, 17 | }, { timestamps: true }); 18 | 19 | const Question = mongoose.model('Question', discussionSchema); 20 | 21 | export default Question; 22 | -------------------------------------------------------------------------------- /backend/models/feedback.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const feedbackSchema = new mongoose.Schema({ 4 | overallExperience: { 5 | type: Number, 6 | required: true, 7 | min: 1, 8 | max: 5 9 | }, 10 | featuresUsed: { 11 | type: [String], 12 | enum: ['vocabulary', 'grammar', 'pronunciation', 'reading', 'listening'], 13 | required: true 14 | }, 15 | mostHelpfulFeature: { 16 | type: String, 17 | enum: ['vocabulary', 'grammar', 'pronunciation', 'reading', 'listening'], 18 | required: true 19 | }, 20 | improvement: { 21 | type: String, 22 | trim: true 23 | }, 24 | newFeatures: { 25 | type: String, 26 | trim: true 27 | }, 28 | recommendation: { 29 | type: Number, 30 | required: true, 31 | min: 1, 32 | max: 5 33 | }, 34 | additionalComments: { 35 | type: String, 36 | trim: true 37 | }, 38 | submittedAt: { 39 | type: Date, 40 | default: Date.now 41 | } 42 | }); 43 | 44 | const Feedback = mongoose.model("Feedback", feedbackSchema); 45 | 46 | export default Feedback; 47 | -------------------------------------------------------------------------------- /backend/models/getInTouch.js: -------------------------------------------------------------------------------- 1 | // Import Mongoose 2 | import mongoose from 'mongoose'; 3 | 4 | // Define the schema for the form data 5 | const getInTouchSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | required: true, 9 | trim: true, 10 | }, 11 | email: { 12 | type: String, 13 | required: true, 14 | trim: true, 15 | match: [/.+\@.+\..+/, 'Please enter a valid email address'], 16 | }, 17 | message: { 18 | type: String, 19 | required: true, 20 | trim: true, 21 | }, 22 | submittedAt: { 23 | type: Date, 24 | default: Date.now, 25 | }, 26 | }); 27 | 28 | // Create the model from the schema 29 | const GetInTouch = mongoose.model('GetInTouch', getInTouchSchema); 30 | 31 | // Export the model as the default export 32 | export default GetInTouch; 33 | -------------------------------------------------------------------------------- /backend/models/postModel.js: -------------------------------------------------------------------------------- 1 | // postModel.js 2 | import mongoose from "mongoose"; 3 | 4 | const postSchema = new mongoose.Schema({ 5 | title: String, 6 | content: String, 7 | category: String, 8 | date: { type: Date, default: Date.now }, 9 | }); 10 | 11 | const Post = mongoose.model("Post", postSchema); 12 | 13 | export default Post; 14 | -------------------------------------------------------------------------------- /backend/models/question.js: -------------------------------------------------------------------------------- 1 | import mongoose, { Mongoose } from "mongoose"; 2 | import answer from "./answers.js" 3 | // import answer from "./answers.js"; 4 | const questionSchema = new mongoose.Schema({ 5 | content:{ 6 | type:String 7 | }, 8 | answers:[ 9 | { 10 | type:mongoose.Schema.Types.ObjectId, 11 | ref:answer 12 | } 13 | ], 14 | answered:{ 15 | type:Boolean, 16 | default: false 17 | }, 18 | 19 | }); 20 | export default mongoose.models.Question || mongoose.model('Question', questionSchema); 21 | 22 | -------------------------------------------------------------------------------- /backend/models/rating.js: -------------------------------------------------------------------------------- 1 | // models/Feedback.js 2 | 3 | import mongoose from 'mongoose'; 4 | 5 | const ratingSchema = new mongoose.Schema({ 6 | name: { type: String, required: true }, 7 | email: { type: String, required: true }, 8 | message: { type: String, required: true }, 9 | rating: { type: Number, required: true, min: 1, max: 5 }, 10 | createdAt: { type: Date, default: Date.now } 11 | }); 12 | 13 | const Rating = mongoose.model('Rating', ratingSchema); 14 | 15 | export default Rating 16 | -------------------------------------------------------------------------------- /backend/models/subscribe.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const subscribeSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true, 7 | trim: true 8 | }, 9 | email: { 10 | type: String, 11 | required: true, 12 | trim: true, 13 | match: [/^\S+@\S+\.\S+$/, "Please enter a valid email address"] 14 | }, 15 | subscribedAt: { 16 | type: Date, 17 | default: Date.now 18 | } 19 | }); 20 | 21 | const Subscribe = mongoose.model("Subscribe", subscribeSchema); 22 | 23 | export default Subscribe; 24 | -------------------------------------------------------------------------------- /backend/models/user.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import bcrypt from "bcryptjs"; 3 | 4 | const userSchema = new mongoose.Schema({ 5 | username: { type: String, required: true, unique: true }, 6 | email: { type: String, required: true, unique: true }, 7 | password: { type: String, required: true }, 8 | isAdmin: { type: Boolean, default: false }, 9 | }); 10 | 11 | // Hash password before saving 12 | userSchema.pre("save", async function (next) { 13 | if (!this.isModified("password")) { 14 | return next(); 15 | } 16 | this.password = await bcrypt.hash(this.password, 10); 17 | next(); 18 | }); 19 | 20 | // Compare entered password with hashed password 21 | userSchema.methods.matchPassword = async function (enteredPassword) { 22 | return await bcrypt.compare(enteredPassword, this.password); 23 | }; 24 | 25 | const User = mongoose.model("User", userSchema); 26 | export default User; 27 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "type": "module", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "nodemon app.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "description": "", 14 | "dependencies": { 15 | "backend": "file:", 16 | "bcryptjs": "^2.4.3", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.4.5", 19 | "express": "^4.21.1", 20 | "express-validator": "^7.2.0", 21 | "jsonwebtoken": "^9.0.2", 22 | "mongoose": "^8.7.1", 23 | "multer": "^1.4.5-lts.1", 24 | "nodemailer": "^6.9.16" 25 | }, 26 | "devDependencies": { 27 | "nodemon": "^3.1.7" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backend/routes/addBlogRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { getAllBlog, getBlog, saveBlog, upload, updateLikes } from "../controllers/addBlogController.js"; 3 | const router = express.Router(); 4 | 5 | router.post("/saveBlog", upload.single('featuredImage'), saveBlog); 6 | router.get("/getAllBlog", getAllBlog); 7 | router.patch("/updateLikes", updateLikes); 8 | router.get("/getBlog/:id", getBlog); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /backend/routes/blogRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | createBlog, 4 | updateBlog, 5 | deleteBlog, 6 | getAllBlogs, 7 | getSingleBlog, 8 | saveComment, 9 | } from "../controllers/blogController.js"; 10 | import authMiddleware from "../middlewares/authMiddleware.js"; 11 | import adminMiddleware from "../middlewares/adminMiddleware.js"; 12 | import { blogValidation } from "../validations/blogValidation.js"; 13 | 14 | const router = express.Router(); 15 | 16 | // Open routes 17 | router.get("/", getAllBlogs); 18 | router.get("/:id", getSingleBlog); 19 | 20 | // Authenticated routes 21 | router.post("/", authMiddleware, blogValidation, createBlog); 22 | router.put("/:id", authMiddleware, blogValidation, updateBlog); 23 | 24 | // Admin-only routes 25 | router.delete("/:id", authMiddleware, adminMiddleware, deleteBlog); 26 | 27 | // save user comments 28 | router.post("/savecomment", saveComment); 29 | 30 | export default router; 31 | -------------------------------------------------------------------------------- /backend/routes/contactRoute.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | const router = express.Router(); 3 | import { 4 | getContact, 5 | saveContact, 6 | newsletter, 7 | } from "../controllers/contactController.js"; 8 | 9 | router.post("/saveContact", saveContact); 10 | router.get("/saveContact", getContact); 11 | router.post("/newsletter", newsletter); 12 | export default router; 13 | -------------------------------------------------------------------------------- /backend/routes/discussionRoutes.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | const router = express.Router(); 3 | 4 | import { getQuestions, saveQuestion, addAnswerToQuestion } from '../controllers/discussionController.js'; 5 | 6 | router.get('/getQuestion', getQuestions); 7 | 8 | router.post('/postQuestion', saveQuestion); 9 | 10 | router.put('/:id/answer', addAnswerToQuestion); 11 | 12 | export default router; 13 | -------------------------------------------------------------------------------- /backend/routes/faqRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | const router = express.Router(); 3 | // import { getContact, saveContact } from "../controllers/contactController.js"; 4 | import {createQuestion, getQuestions,answerQuestion} from "../controllers/faqController.js" 5 | 6 | router.post("/createquestion", createQuestion); 7 | router.get("/getquestions",getQuestions); 8 | router.post("/answerquestion",answerQuestion); 9 | 10 | export default router; -------------------------------------------------------------------------------- /backend/routes/feedbackRoute.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { getFeed, submitFeedback } from "../controllers/feedController.js"; 3 | const router = express.Router(); 4 | 5 | 6 | router.post("/saveFeedback", submitFeedback); 7 | router.get("/saveFeedback", getFeed); 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /backend/routes/getInTouchRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { submitGetInTouch } from "../controllers/getInTouchController.js"; 3 | const router = express.Router(); 4 | 5 | 6 | router.post("/saveGetInTouch", submitGetInTouch); 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /backend/routes/ratingRoutes.js: -------------------------------------------------------------------------------- 1 | // routes/feedbackRoutes.js 2 | 3 | import express from 'express'; 4 | import saveRating from '../controllers/ratingController.js'; 5 | 6 | const router = express.Router(); 7 | 8 | // Route to save feedback 9 | router.post('/', saveRating); 10 | 11 | export default router 12 | -------------------------------------------------------------------------------- /backend/routes/storiesRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { getPosts, savePost } from "../controllers/postsController.js"; 3 | 4 | const router = express.Router(); 5 | 6 | router.get("/getposts", getPosts); 7 | router.post("/saveposts", savePost); 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /backend/routes/subscribeRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | const router = express.Router(); 3 | import { getSubscribe, saveSubsribe } from "../controllers/subscribeController.js"; 4 | 5 | router.post("/subscribe", saveSubsribe); 6 | router.get("/getSubscribe", getSubscribe); 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /backend/routes/userRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | registerUser, 4 | loginUser, 5 | getUserProfile, 6 | getAllUsers, 7 | ForgotPassWordEmail, 8 | ResetPassword, 9 | } from "../controllers/userController.js"; 10 | import authMiddleware from "../middlewares/authMiddleware.js"; 11 | import adminMiddleware from "../middlewares/adminMiddleware.js"; 12 | import { userValidation } from "../validations/userValidation.js"; 13 | const router = express.Router(); 14 | 15 | // Public routes 16 | router.post("/register", userValidation, registerUser); 17 | router.post("/login", loginUser); 18 | 19 | // Authenticated route to get user profile 20 | router.get("/profile", authMiddleware, getUserProfile); 21 | 22 | // Admin route to get all users 23 | router.get("/", authMiddleware, adminMiddleware, getAllUsers); 24 | 25 | router.post("/forgotpassword", ForgotPassWordEmail); 26 | 27 | router.post("/resetpassword", ResetPassword); 28 | 29 | export default router; 30 | -------------------------------------------------------------------------------- /backend/sendFeedbackToAdmin.js: -------------------------------------------------------------------------------- 1 | import nodemailer from 'nodemailer'; 2 | import dotenv from 'dotenv'; 3 | 4 | // Load environment variables from .env file 5 | dotenv.config(); 6 | 7 | const sendMailToAdmin = (userdata) => { 8 | const transporter = nodemailer.createTransport({ 9 | service: "gmail", 10 | host: "smtp.gmail.com", 11 | port: 587, 12 | secure: false, 13 | auth: { 14 | user: process.env.EMAIL_ID, // by this email id you will get mail 15 | pass: process.env.PASS_KEY, // passkey 16 | }, 17 | }); 18 | 19 | async function main() { 20 | await transporter.sendMail({ 21 | from: { 22 | name: `Wordwise - ${new Date().toLocaleString()}`, 23 | address: process.env.EMAIL_ID, 24 | }, // sender address 25 | to: process.env.ADMIN_EMAIL_ID, // list of receivers 26 | subject: "New User Feedback from Wordwise ✔", // Subject line 27 | text: "Wordwise User Feedback", // plain text body 28 | html: `
29 |
30 | Wordwise User Feedback 31 |
32 | 33 | 34 | 35 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
36 | Field 37 | 39 | Value 40 |
Submitted At${new Date(userdata.submittedAt).toLocaleString()}
Overall Experience${userdata.overallExperience}
Recommendation${userdata.recommendation}
Additional Comments${userdata.additionalComments}
Improvement${userdata.improvement}
Most Helpful Feature${userdata.mostHelpfulFeature}
New Features${userdata.newFeatures}
Features Used${userdata.featuresUsed.join(', ')}
78 |
`, // html body 79 | }); 80 | } 81 | 82 | main().catch(console.error); 83 | } 84 | 85 | export { sendMailToAdmin }; 86 | -------------------------------------------------------------------------------- /backend/utils/db.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import dotenv from "dotenv"; 3 | dotenv.config(); 4 | 5 | const connectDB = async () => { 6 | try { 7 | await mongoose.connect(process.env.MONGO_URI); 8 | console.log("MongoDB Connected"); 9 | } catch (error) { 10 | console.error("Database connection error:", error); 11 | process.exit(1); 12 | } 13 | }; 14 | 15 | export default connectDB; 16 | -------------------------------------------------------------------------------- /backend/utils/sendMailToSubscribe.js: -------------------------------------------------------------------------------- 1 | import nodemailer from 'nodemailer'; 2 | import dotenv from 'dotenv'; 3 | dotenv.config(); 4 | 5 | const sendMailToSubscriber = (userdata) => { 6 | const transporter = nodemailer.createTransport({ 7 | service: "gmail", 8 | host: "smtp.gmail.com", 9 | port: 587, 10 | secure: false, 11 | auth: { 12 | user: process.env.EMAIL_ID, 13 | pass: process.env.PASS_KEY, 14 | }, 15 | }); 16 | 17 | async function main() { 18 | await transporter.sendMail({ 19 | from: { 20 | name: "WordWise", 21 | address: process.env.EMAIL_ID, 22 | }, 23 | to: userdata.email, 24 | subject: "Welcome to WordWise! 📖 Your Vocabulary Journey Begins Here", 25 | text: "Thank you for subscribing to WordWise!", 26 | html: ` 27 |
28 |
29 |

Welcome to WordWise, ${userdata.name}!

30 |

31 | We’re thrilled to have you join the WordWise community—a place where words come alive, and every blog post is crafted to expand your vocabulary and deepen your understanding of language and topics you care about. 32 |

33 |

34 | At WordWise, we believe in the power of words to enlighten and inspire. Our responsive, user-friendly platform is designed with you in mind, ensuring that each visit feels as seamless as it is engaging. Whether you’re here to explore our latest blogs, delve into specific topics, or simply enjoy a well-crafted read, WordWise has something for everyone. 35 |

36 |

37 | As part of our community, you’ll be among the first to receive fresh content that’s both insightful and enriching. From curated articles that explore a variety of subjects to interactive features that enhance your reading experience, WordWise is more than just a blog—it’s a journey into the world of words. 38 |

39 |

40 | We encourage you to dive into our sections, such as Home, Leading Blogs, About, and Contact Us. Each one is thoughtfully designed to guide you through your reading adventure. And if you ever wish to share feedback or connect with us, our Contact Us page is always open. 41 |

42 |

43 | Thank you for subscribing to WordWise! We’re excited to share our latest blogs and updates with you. Here’s to many engaging reads and enriching experiences ahead! 44 |

45 |

46 | With warm regards,
47 | The WordWise Team 48 |

49 |
50 |
51 | `, 52 | }); 53 | } 54 | 55 | main().catch(console.error); 56 | }; 57 | 58 | export { sendMailToSubscriber }; 59 | -------------------------------------------------------------------------------- /backend/validations/blogValidation.js: -------------------------------------------------------------------------------- 1 | import { body, validationResult } from 'express-validator'; 2 | 3 | export const blogValidation = [ 4 | body('title').not().isEmpty().withMessage('Title is required'), 5 | body('topic').not().isEmpty().withMessage('Topic is required'), 6 | body('image').not().isEmpty().withMessage('Image is required'), 7 | body('content').not().isEmpty().withMessage('Content is required'), 8 | body('author').not().isEmpty().withMessage('Author is required'), 9 | (req, res, next) => { 10 | const errors = validationResult(req); 11 | if (!errors.isEmpty()) { 12 | return res.status(400).json({ errors: errors.array() }); 13 | } 14 | next(); 15 | }, 16 | ]; 17 | -------------------------------------------------------------------------------- /backend/validations/userValidation.js: -------------------------------------------------------------------------------- 1 | import { body, validationResult } from "express-validator"; 2 | 3 | export const userValidation = [ 4 | body("username").not().isEmpty().withMessage("Username is required"), 5 | body("email").isEmail().withMessage("Enter a valid email"), 6 | body("password") 7 | .isLength({ min: 6 }) 8 | .withMessage("Password must be at least 6 characters"), 9 | (req, res, next) => { 10 | const errors = validationResult(req); 11 | if (!errors.isEmpty()) { 12 | return res.status(400).json({ errors: errors.array() }); 13 | } 14 | next(); 15 | }, 16 | ]; 17 | -------------------------------------------------------------------------------- /boy.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/boy.webp -------------------------------------------------------------------------------- /chatbot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/chatbot.gif -------------------------------------------------------------------------------- /comment.css: -------------------------------------------------------------------------------- 1 | *{ 2 | box-sizing:border-box ; 3 | } 4 | /* body{ 5 | display: block; 6 | margin: 0; 7 | padding: 0; 8 | font-family: "Montserrat", sans-serif; 9 | font-size: 16px; 10 | background-color:rgb(230 224 216); 11 | margin-top: 20px; 12 | } */ 13 | .flex{ 14 | display:flex ; 15 | justify-content: space-between; 16 | width: 100%; 17 | } 18 | img{ 19 | width: 100%; 20 | height: 100%; 21 | } 22 | .comment-box, .comment-list{ 23 | background: #fff; 24 | border-radius: 4px; 25 | box-shadow: 0 8px 8px #0002; 26 | position: relative; 27 | animation: float 4s ease-in-out infinite; 28 | 29 | } 30 | .comment-box, .comment-list:hover{ 31 | box-sizing: inherit; 32 | } 33 | .comment-session{ 34 | width: 650px; 35 | height: auto; 36 | margin: 0 auto; 37 | } 38 | .comment-list{ 39 | width: 100%; 40 | margin-bottom: 12px; 41 | } 42 | 43 | .comment-list .user{ 44 | display:flex ; 45 | padding: 8px; 46 | overflow: hidden; 47 | } 48 | .comment-list .user img{ 49 | height: 38px; 50 | width: 38px; 51 | border-radius: 50%; 52 | margin-right: 10px; 53 | } 54 | .comment-list .name{ 55 | text-transform: uppercase; 56 | font-weight: 600; 57 | } 58 | .comment-list .day{ 59 | font-size: 12px; 60 | } 61 | .reply{ 62 | display: flex; 63 | align-items: center; 64 | gap: 10px; 65 | justify-content: center; 66 | padding:10px ; 67 | gap:10px; 68 | } 69 | 70 | .icons { 71 | display: flex; 72 | align-items: center; 73 | justify-content: center; 74 | height: 24px; 75 | color:#777; 76 | gap: 5px; 77 | 78 | } 79 | /* .icon:hover{ 80 | color: #009cff; 81 | cursor: pointer; 82 | } */ 83 | body.dark-mode { --background-color: #121212; --text-color: #ffffff; --comment-bg-color: #1e1e1e; --border-color: #333; --box-shadow-color: rgba(255, 255, 255, 0.1); --icon-color: #bbbbbb; --icon-hover-color: #ffffff; --link-color: #82b1ff; --button-bg-color: #1e88e5; --button-hover-bg-color: #1565c0; --text-area-bg-color: #2e2e2e; --text-area-border-color: #555; } 84 | .icons.like:hover, .icons.like.active { 85 | color: #1e90ff; 86 | cursor: pointer; 87 | } 88 | .icons.dislike:hover, .icons.dislike.active { 89 | color: #ff6347; 90 | cursor: pointer; 91 | } 92 | .post-comment .comment{ 93 | padding: 0 0 15px 58px; 94 | } 95 | .comment-box{ 96 | padding: 10px; 97 | overflow: hidden; 98 | 99 | } 100 | .comment-box .user{ 101 | display: flex; 102 | width: min-content; 103 | } 104 | .comment-box .image img{ 105 | width: 24px; 106 | height: 24px; 107 | margin-right: 10px; 108 | border-radius: 50%; 109 | } 110 | .comment-box textarea{ 111 | background: #eee; 112 | width:-webkit-fill-available; 113 | height: 152px; 114 | margin: 10px 0; 115 | padding: 10px; 116 | border: none; 117 | border-radius: 4px; 118 | box-shadow: 0 0 0 0.5px #0003; 119 | } 120 | .comment-box textarea:focus{ 121 | outline-color: #009cff; 122 | 123 | } 124 | .comment-box .comment-submit{ 125 | float:right ; 126 | padding: 6px 14px; 127 | border: none; 128 | background: #009cff; 129 | color:#fff; 130 | cursor: pointer; 131 | border-radius: 2px; 132 | 133 | } 134 | .comment-box .comment-submit:hover{ 135 | background-color: #027ecc; 136 | } 137 | .re-comment:hover { 138 | text-decoration: underline; 139 | } 140 | @keyframes float { 141 | 0%, 100% { 142 | transform: translateY(0); 143 | } 50% { 144 | transform: translateY(-10px); 145 | } } 146 | -------------------------------------------------------------------------------- /comment.js: -------------------------------------------------------------------------------- 1 | let likeCount = 420; 2 | 3 | function toggleLike(element) { 4 | if (element.classList.contains('active')) { 5 | element.classList.remove('active'); 6 | likeCount--; 7 | } else { 8 | element.classList.add('active'); 9 | likeCount++; 10 | } 11 | element.querySelector('.count').innerText = likeCount; 12 | } 13 | 14 | element.classList.toggle('active'); 15 | 16 | 17 | document.querySelector('.like').addEventListener('click', function() { 18 | toggleLike(this); 19 | }); 20 | 21 | document.querySelector('.dislike').addEventListener('click', function() { 22 | toggleDislike(this); 23 | }); 24 | -------------------------------------------------------------------------------- /contact_us.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const sendEmailButton = document.getElementById("sendEmailButton"); 3 | 4 | sendEmailButton.addEventListener("click", SendEmail); 5 | }); 6 | 7 | const trustedDomains = [ 8 | 'gmail.com', 9 | 'outlook.com', 10 | 'yahoo.com', 11 | 'protonmail.com', 12 | 'icloud.com', 13 | 'tutanota.com', 14 | 'hotmail.com', 15 | 'live.com', 16 | 'mail.com', 17 | 'zoho.com', 18 | 'gmx.com', 19 | 'aol.com', 20 | 'fastmail.com', 21 | 'yandex.com', 22 | '*.edu', 23 | '*.ac.uk', 24 | '*.edu.in', 25 | '*.edu.au', 26 | 'examplecompany.com', 27 | 'mailfence.com', 28 | 'posteo.de', 29 | 'runbox.com' 30 | ]; 31 | 32 | // Email validation function to check format and domain 33 | function ValidateTrustEmail(email) { 34 | const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // Basic email format validation 35 | const domain = email.split('@')[1]; 36 | 37 | return ( 38 | emailPattern.test(email) && 39 | trustedDomains.some((trusted) => 40 | trusted.includes('*') ? domain.endsWith(trusted.slice(1)) : domain === trusted 41 | ) 42 | ); 43 | } 44 | 45 | async function SendEmail(event) { 46 | event.preventDefault(); 47 | 48 | const Name = document.getElementById("Name").value.trim(); 49 | const email = document.getElementById("email2").value.trim(); 50 | const phone = document.getElementById("phone").value.trim(); 51 | const message = document.getElementById("message").value.trim(); 52 | 53 | // Input validation 54 | if (!Name || !email || !phone || !message) { 55 | alert("All fields are required."); 56 | return; 57 | } 58 | 59 | if (!ValidateTrustEmail(email)) { 60 | alert("Please enter a valid email address from a trusted provider."); 61 | return; 62 | } 63 | 64 | if (message.length < 10) { 65 | alert("Message must be at least 10 characters long."); 66 | return; 67 | } 68 | // If validation passes 69 | alert('Form submitted successfully!'); 70 | 71 | const data = { Name, email, phone, message }; 72 | 73 | try { 74 | const response = await fetch("http://127.0.0.1:3000/send-email", { 75 | method: "POST", 76 | headers: { "Content-Type": "application/json" }, 77 | body: JSON.stringify(data), 78 | }); 79 | 80 | const result = await response.json(); 81 | 82 | if (response.ok) { 83 | showPopup(); 84 | document.getElementById("contactForm").reset(); 85 | } else { 86 | handleErrorResponse(result); 87 | } 88 | } catch (error) { 89 | console.error("Error sending email:", error); 90 | } 91 | } 92 | 93 | function validateEmail(email) { 94 | const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 95 | return re.test(String(email).toLowerCase()); 96 | } 97 | 98 | function showPopup() { 99 | const popup = document.getElementById("popupMessage"); 100 | const overlay = document.getElementById("overlay"); 101 | 102 | overlay.style.display = "block"; 103 | popup.style.display = "block"; 104 | 105 | // Close the popup 106 | document.getElementById("closePopup").addEventListener("click", hidePopup); 107 | 108 | // Close when clicking outside 109 | overlay.addEventListener("click", hidePopup); 110 | } 111 | 112 | function hidePopup() { 113 | const popup = document.getElementById("popupMessage"); 114 | const overlay = document.getElementById("overlay"); 115 | 116 | popup.style.display = "none"; 117 | overlay.style.display = "none"; 118 | } 119 | 120 | function handleErrorResponse(result) { 121 | alert(`Error: ${result.message || 'An unexpected error occurred.'}`); 122 | } 123 | function validateForm() { 124 | const phonePattern = /^\d{10}$/; //for 10-digit phone numbers 125 | // Validate phone number 126 | if (!phonePattern.test(phone)) { 127 | alert('Please enter a valid phone number (10 digits).'); 128 | return; // Stop further execution 129 | } 130 | } 131 | 132 | function restrictInputToNumbers(event) { 133 | const key = event.key; 134 | if (!/^\d$/.test(key) && key !== "Backspace" && key !== "Delete") { 135 | event.preventDefault(); // Prevent input if it's not a digit 136 | } 137 | } 138 | 139 | document.addEventListener('DOMContentLoaded', () => { 140 | const phoneInput = document.getElementById('phone'); 141 | phoneInput.addEventListener('keydown', restrictInputToNumbers); 142 | }); -------------------------------------------------------------------------------- /darkMode.js: -------------------------------------------------------------------------------- 1 | const checkbox = document.getElementById('checkbox'); 2 | const modeLabel = document.getElementById('mode-label'); 3 | 4 | // Function to apply the theme and update the label 5 | const applyTheme = (theme) => { 6 | console.log(`Applying theme: ${theme}`); // Debugging line 7 | document.documentElement.setAttribute('data-theme', theme); 8 | localStorage.setItem('theme', theme); 9 | checkbox.checked = (theme === 'dark'); 10 | 11 | // Update the label text based on the current theme 12 | modeLabel.textContent = theme === 'dark' ? '' : ''; 13 | }; 14 | 15 | // Check for saved theme in localStorage or use system preference 16 | const savedTheme = localStorage.getItem('theme'); 17 | const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; 18 | 19 | if (savedTheme) { 20 | console.log(`Found saved theme: ${savedTheme}`); // Debugging line 21 | applyTheme(savedTheme); 22 | } else { 23 | console.log(`Using system preference: ${systemPrefersDark ? 'dark' : 'light'}`); // Debugging line 24 | applyTheme(systemPrefersDark ? 'dark' : 'light'); 25 | } 26 | 27 | // Add event listener for toggle switch 28 | checkbox.addEventListener('change', () => { 29 | const newTheme = checkbox.checked ? 'dark' : 'light'; 30 | console.log(`Checkbox changed: ${newTheme}`); // Debugging line 31 | applyTheme(newTheme); 32 | }); 33 | 34 | // Listen for system theme changes 35 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => { 36 | const newTheme = event.matches ? 'dark' : 'light'; 37 | console.log(`System theme changed: ${newTheme}`); // Debugging line 38 | applyTheme(newTheme); 39 | }); 40 | -------------------------------------------------------------------------------- /dist/contact_us.dev.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | document.addEventListener("DOMContentLoaded", function () { 4 | var sendEmailButton = document.getElementById("sendEmailButton"); // Assuming your button has this ID 5 | 6 | sendEmailButton.addEventListener("click", SendEmail); 7 | }); 8 | 9 | function SendEmail(event) { 10 | var firstName, lastName, email, phone, message, data, response, result; 11 | return regeneratorRuntime.async(function SendEmail$(_context) { 12 | while (1) { 13 | switch (_context.prev = _context.next) { 14 | case 0: 15 | event.preventDefault(); 16 | firstName = document.getElementById("firstName").value.trim(); 17 | lastName = document.getElementById("lastName").value.trim(); 18 | email = document.getElementById("email").value.trim(); 19 | phone = document.getElementById("phone").value.trim(); 20 | message = document.getElementById("message").value.trim(); // Input validation 21 | 22 | if (!(!firstName || !lastName || !email || !phone || !message)) { 23 | _context.next = 9; 24 | break; 25 | } 26 | 27 | alert("All fields are required."); 28 | return _context.abrupt("return"); 29 | 30 | case 9: 31 | if (validateEmail(email)) { 32 | _context.next = 12; 33 | break; 34 | } 35 | 36 | alert("Please enter a valid email address."); 37 | return _context.abrupt("return"); 38 | 39 | case 12: 40 | console.log(Name, email, phone, message); 41 | 42 | if (!(message.length < 10)) { 43 | _context.next = 16; 44 | break; 45 | } 46 | 47 | alert("Message must be at least 10 characters long."); 48 | return _context.abrupt("return"); 49 | 50 | case 16: 51 | //console.log("Email value:", email , Name, phone, message); 52 | data = { 53 | Name: Name, 54 | email: email, 55 | phone: phone, 56 | message: message 57 | }; 58 | _context.prev = 17; 59 | _context.next = 20; 60 | return regeneratorRuntime.awrap(fetch("http://127.0.0.1:3000/send-email", { 61 | method: "POST", 62 | headers: { 63 | "Content-Type": "application/json" 64 | }, 65 | body: JSON.stringify(data) 66 | })); 67 | 68 | case 20: 69 | response = _context.sent; 70 | _context.next = 23; 71 | return regeneratorRuntime.awrap(response.json()); 72 | 73 | case 23: 74 | result = _context.sent; 75 | 76 | if (response.ok) { 77 | showPopup(); 78 | document.getElementById("contactForm").reset(); 79 | } else { 80 | handleErrorResponse(result); 81 | } 82 | 83 | _context.next = 31; 84 | break; 85 | 86 | case 27: 87 | _context.prev = 27; 88 | _context.t0 = _context["catch"](17); 89 | console.error("Error sending email:", _context.t0); 90 | alert("Error sending email. Please try again later."); 91 | 92 | case 31: 93 | case "end": 94 | return _context.stop(); 95 | } 96 | } 97 | }, null, null, [[17, 27]]); 98 | } 99 | 100 | function validateEmail(email) { 101 | var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 102 | return re.test(String(email).toLowerCase()); 103 | } 104 | 105 | function showPopup() { 106 | var popup = document.getElementById("popupMessage"); 107 | var overlay = document.getElementById("overlay"); 108 | overlay.style.display = "block"; 109 | popup.style.display = "block"; // Close the popup 110 | 111 | document.getElementById("closePopup").addEventListener("click", hidePopup); // Close when clicking outside 112 | 113 | overlay.addEventListener("click", hidePopup); 114 | } 115 | 116 | function hidePopup() { 117 | var popup = document.getElementById("popupMessage"); 118 | var overlay = document.getElementById("overlay"); 119 | popup.style.display = "none"; 120 | overlay.style.display = "none"; 121 | } 122 | 123 | function handleErrorResponse(result) { 124 | alert("Error: ".concat(result.message || 'An unexpected error occurred.')); 125 | } 126 | //# sourceMappingURL=contact_us.dev.js.map 127 | -------------------------------------------------------------------------------- /dist/contact_us.dev.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["contact_us.js"],"names":["document","addEventListener","sendEmailButton","getElementById","SendEmail","event","preventDefault","firstName","value","trim","lastName","email","phone","message","alert","validateEmail","console","log","Name","length","data","fetch","method","headers","body","JSON","stringify","response","json","result","ok","showPopup","reset","handleErrorResponse","error","re","test","String","toLowerCase","popup","overlay","style","display","hidePopup"],"mappings":";;AAAAA,QAAQ,CAACC,gBAAT,CAA0B,kBAA1B,EAA8C,YAAM;AAClD,MAAMC,eAAe,GAAGF,QAAQ,CAACG,cAAT,CAAwB,iBAAxB,CAAxB,CADkD,CACkB;;AAEpED,EAAAA,eAAe,CAACD,gBAAhB,CAAiC,OAAjC,EAA0CG,SAA1C;AACD,CAJD;;AAMA,SAAeA,SAAf,CAAyBC,KAAzB;AAAA;AAAA;AAAA;AAAA;AAAA;AACEA,UAAAA,KAAK,CAACC,cAAN;AAEMC,UAAAA,SAHR,GAGoBP,QAAQ,CAACG,cAAT,CAAwB,WAAxB,EAAqCK,KAArC,CAA2CC,IAA3C,EAHpB;AAIQC,UAAAA,QAJR,GAImBV,QAAQ,CAACG,cAAT,CAAwB,UAAxB,EAAoCK,KAApC,CAA0CC,IAA1C,EAJnB;AAKQE,UAAAA,KALR,GAKgBX,QAAQ,CAACG,cAAT,CAAwB,OAAxB,EAAiCK,KAAjC,CAAuCC,IAAvC,EALhB;AAMQG,UAAAA,KANR,GAMgBZ,QAAQ,CAACG,cAAT,CAAwB,OAAxB,EAAiCK,KAAjC,CAAuCC,IAAvC,EANhB;AAOQI,UAAAA,OAPR,GAOkBb,QAAQ,CAACG,cAAT,CAAwB,SAAxB,EAAmCK,KAAnC,CAAyCC,IAAzC,EAPlB,EASE;;AATF,gBAUM,CAACF,SAAD,IAAc,CAACG,QAAf,IAA2B,CAACC,KAA5B,IAAqC,CAACC,KAAtC,IAA+C,CAACC,OAVtD;AAAA;AAAA;AAAA;;AAWIC,UAAAA,KAAK,CAAC,0BAAD,CAAL;AAXJ;;AAAA;AAAA,cAeOC,aAAa,CAACJ,KAAD,CAfpB;AAAA;AAAA;AAAA;;AAgBIG,UAAAA,KAAK,CAAC,qCAAD,CAAL;AAhBJ;;AAAA;AAoBEE,UAAAA,OAAO,CAACC,GAAR,CAAYC,IAAZ,EAAiBP,KAAjB,EAAuBC,KAAvB,EAA6BC,OAA7B;;AApBF,gBAsBMA,OAAO,CAACM,MAAR,GAAiB,EAtBvB;AAAA;AAAA;AAAA;;AAuBIL,UAAAA,KAAK,CAAC,8CAAD,CAAL;AAvBJ;;AAAA;AA0BE;AACMM,UAAAA,IA3BR,GA2Be;AACXF,YAAAA,IAAI,EAAEA,IADK;AAEXP,YAAAA,KAAK,EAAEA,KAFI;AAGXC,YAAAA,KAAK,EAAEA,KAHI;AAIXC,YAAAA,OAAO,EAAEA;AAJE,WA3Bf;AAAA;AAAA;AAAA,0CAmC2BQ,KAAK,CAAC,kCAAD,EAAqC;AAC/DC,YAAAA,MAAM,EAAE,MADuD;AAE/DC,YAAAA,OAAO,EAAE;AAAE,8BAAgB;AAAlB,aAFsD;AAG/DC,YAAAA,IAAI,EAAEC,IAAI,CAACC,SAAL,CAAeN,IAAf;AAHyD,WAArC,CAnChC;;AAAA;AAmCUO,UAAAA,QAnCV;AAAA;AAAA,0CAyCyBA,QAAQ,CAACC,IAAT,EAzCzB;;AAAA;AAyCUC,UAAAA,MAzCV;;AA2CI,cAAIF,QAAQ,CAACG,EAAb,EAAiB;AACfC,YAAAA,SAAS;AACT/B,YAAAA,QAAQ,CAACG,cAAT,CAAwB,aAAxB,EAAuC6B,KAAvC;AACD,WAHD,MAGO;AACLC,YAAAA,mBAAmB,CAACJ,MAAD,CAAnB;AACD;;AAhDL;AAAA;;AAAA;AAAA;AAAA;AAmDIb,UAAAA,OAAO,CAACkB,KAAR,CAAc,sBAAd;AACApB,UAAAA,KAAK,CAAC,8CAAD,CAAL;;AApDJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAwDA,SAASC,aAAT,CAAuBJ,KAAvB,EAA8B;AAC5B,MAAMwB,EAAE,GAAG,4BAAX;AACA,SAAOA,EAAE,CAACC,IAAH,CAAQC,MAAM,CAAC1B,KAAD,CAAN,CAAc2B,WAAd,EAAR,CAAP;AACD;;AAED,SAASP,SAAT,GAAqB;AACnB,MAAMQ,KAAK,GAAGvC,QAAQ,CAACG,cAAT,CAAwB,cAAxB,CAAd;AACA,MAAMqC,OAAO,GAAGxC,QAAQ,CAACG,cAAT,CAAwB,SAAxB,CAAhB;AAEAqC,EAAAA,OAAO,CAACC,KAAR,CAAcC,OAAd,GAAwB,OAAxB;AACAH,EAAAA,KAAK,CAACE,KAAN,CAAYC,OAAZ,GAAsB,OAAtB,CALmB,CAOnB;;AACA1C,EAAAA,QAAQ,CAACG,cAAT,CAAwB,YAAxB,EAAsCF,gBAAtC,CAAuD,OAAvD,EAAgE0C,SAAhE,EARmB,CAUnB;;AACAH,EAAAA,OAAO,CAACvC,gBAAR,CAAyB,OAAzB,EAAkC0C,SAAlC;AACD;;AAED,SAASA,SAAT,GAAqB;AACnB,MAAMJ,KAAK,GAAGvC,QAAQ,CAACG,cAAT,CAAwB,cAAxB,CAAd;AACA,MAAMqC,OAAO,GAAGxC,QAAQ,CAACG,cAAT,CAAwB,SAAxB,CAAhB;AAEAoC,EAAAA,KAAK,CAACE,KAAN,CAAYC,OAAZ,GAAsB,MAAtB;AACAF,EAAAA,OAAO,CAACC,KAAR,CAAcC,OAAd,GAAwB,MAAxB;AACD;;AAED,SAAST,mBAAT,CAA6BJ,MAA7B,EAAqC;AACnCf,EAAAA,KAAK,kBAAWe,MAAM,CAAChB,OAAP,IAAkB,+BAA7B,EAAL;AACD","sourcesContent":["document.addEventListener(\"DOMContentLoaded\", () => {\r\n const sendEmailButton = document.getElementById(\"sendEmailButton\"); // Assuming your button has this ID\r\n\r\n sendEmailButton.addEventListener(\"click\", SendEmail);\r\n});\r\n\r\nasync function SendEmail(event) {\r\n event.preventDefault();\r\n\r\n const firstName = document.getElementById(\"firstName\").value.trim();\r\n const lastName = document.getElementById(\"lastName\").value.trim();\r\n const email = document.getElementById(\"email\").value.trim();\r\n const phone = document.getElementById(\"phone\").value.trim();\r\n const message = document.getElementById(\"message\").value.trim();\r\n\r\n // Input validation\r\n if (!firstName || !lastName || !email || !phone || !message) {\r\n alert(\"All fields are required.\");\r\n return;\r\n }\r\n\r\n if (!validateEmail(email)) {\r\n alert(\"Please enter a valid email address.\");\r\n return;\r\n }\r\n\r\n console.log(Name,email,phone,message);\r\n\r\n if (message.length < 10) {\r\n alert(\"Message must be at least 10 characters long.\");\r\n return;\r\n }\r\n //console.log(\"Email value:\", email , Name, phone, message); \r\n const data = {\r\n Name: Name,\r\n email: email,\r\n phone: phone,\r\n message: message,\r\n };\r\n\r\n try {\r\n const response = await fetch(\"http://127.0.0.1:3000/send-email\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(data),\r\n });\r\n\r\n const result = await response.json();\r\n \r\n if (response.ok) {\r\n showPopup();\r\n document.getElementById(\"contactForm\").reset();\r\n } else {\r\n handleErrorResponse(result);\r\n }\r\n \r\n } catch (error) {\r\n console.error(\"Error sending email:\", error);\r\n alert(\"Error sending email. Please try again later.\");\r\n }\r\n}\r\n\r\nfunction validateEmail(email) {\r\n const re = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\r\n return re.test(String(email).toLowerCase());\r\n}\r\n\r\nfunction showPopup() {\r\n const popup = document.getElementById(\"popupMessage\");\r\n const overlay = document.getElementById(\"overlay\");\r\n \r\n overlay.style.display = \"block\";\r\n popup.style.display = \"block\";\r\n\r\n // Close the popup\r\n document.getElementById(\"closePopup\").addEventListener(\"click\", hidePopup);\r\n \r\n // Close when clicking outside\r\n overlay.addEventListener(\"click\", hidePopup);\r\n}\r\n\r\nfunction hidePopup() {\r\n const popup = document.getElementById(\"popupMessage\");\r\n const overlay = document.getElementById(\"overlay\");\r\n \r\n popup.style.display = \"none\";\r\n overlay.style.display = \"none\";\r\n}\r\n\r\nfunction handleErrorResponse(result) {\r\n alert(`Error: ${result.message || 'An unexpected error occurred.'}`);\r\n}"],"file":"contact_us.dev.js"} -------------------------------------------------------------------------------- /dist/contact_us.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function SendEmail(t){var n,r,a,o,l,s,d,i;return regeneratorRuntime.async(function(e){for(;;)switch(e.prev=e.next){case 0:if(t.preventDefault(),n=document.getElementById("firstName").value.trim(),r=document.getElementById("lastName").value.trim(),a=document.getElementById("email").value.trim(),o=document.getElementById("phone").value.trim(),l=document.getElementById("message").value.trim(),n&&r&&a&&o&&l){e.next=9;break}return alert("All fields are required."),e.abrupt("return");case 9:if(validateEmail(a)){e.next=12;break}return alert("Please enter a valid email address."),e.abrupt("return");case 12:if(console.log(Name,a,o,l),l.length<10)return alert("Message must be at least 10 characters long."),e.abrupt("return");e.next=16;break;case 16:return s={Name:Name,email:a,phone:o,message:l},e.prev=17,e.next=20,regeneratorRuntime.awrap(fetch("http://127.0.0.1:3000/send-email",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}));case 20:return d=e.sent,e.next=23,regeneratorRuntime.awrap(d.json());case 23:i=e.sent,d.ok?(showPopup(),document.getElementById("contactForm").reset()):handleErrorResponse(i),e.next=31;break;case 27:e.prev=27,e.t0=e.catch(17),console.error("Error sending email:",e.t0),alert("Error sending email. Please try again later.");case 31:case"end":return e.stop()}},null,null,[[17,27]])}function validateEmail(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(e).toLowerCase())}function showPopup(){var e=document.getElementById("popupMessage"),t=document.getElementById("overlay");t.style.display="block",e.style.display="block",document.getElementById("closePopup").addEventListener("click",hidePopup),t.addEventListener("click",hidePopup)}function hidePopup(){var e=document.getElementById("popupMessage"),t=document.getElementById("overlay");e.style.display="none",t.style.display="none"}function handleErrorResponse(e){alert("Error: ".concat(e.message||"An unexpected error occurred."))}document.addEventListener("DOMContentLoaded",function(){document.getElementById("sendEmailButton").addEventListener("click",SendEmail)}); 2 | //# sourceMappingURL=contact_us.min.js.map 3 | -------------------------------------------------------------------------------- /dist/contact_us.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["contact_us.js"],"names":["SendEmail","event","firstName","lastName","email","phone","message","data","response","result","regeneratorRuntime","async","_context","prev","next","preventDefault","document","getElementById","value","trim","abrupt","validateEmail","alert","length","Name","awrap","method","headers","Content-Type","body","JSON","stringify","sent","json","ok","showPopup","reset","t0","console","error","stop","String","overlay","style","display","popup","addEventListener","hidePopup","concat","sendEmailButton"],"mappings":"aAIC,SAJDA,UAAAC,GAIC,IAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAA,OAAAC,mBAAAC,MAAA,SAAAC,GAAA,OAAA,OAAAA,EAAAC,KAAAD,EAAAE,MAAA,KAAA,EAAA,GAGCb,EAAMc,iBADRb,EAAAc,SAAAC,eAAA,aAAAC,MAAAC,OAAAhB,EAAAa,SAAAC,eAAA,YAAAC,MAAAC,OAAAf,EAAAY,SAAAC,eAAA,SAAAC,MAAAC,OAAAd,EAAAW,SAAAC,eAAA,SAAAC,MAAAC,OAAAb,EAAAU,SAAAC,eAAA,WAAAC,MAAAC,OAIQhB,GAAWa,GAASC,GAATZ,GAAwBC,EAN1C,CAAAM,EAAAE,KAAA,EAAA,MAAA,OAOOV,MAAAA,4BAPPQ,EAAAQ,OAAA,UAAA,KAAA,EAAA,GAEDC,cAUOnB,GAZN,CAAAU,EAAAE,KAAA,GAAA,MAAA,OAEDQ,MAAA,uCAFCV,EAAAQ,OAAA,UAAA,KAAA,GAAA,GAaGE,QAAAA,IAAAA,KAAMlB,EAAAC,EAAAC,GAWJA,EAAQiB,OAAS,GAxBtB,OAEDD,MAAA,gDAFCV,EAAAQ,OAAA,UAAAR,EAAAE,KAAA,GAAA,MAAA,KAAA,GAAA,OAEDP,EAAA,CA4BIiB,KAAMA,KAZNF,MAAAA,EAhBJjB,MAAAA,EA+BIC,QAASA,GAjCZM,EAAAC,KAAA,GAAAD,EAAAE,KAAA,GAAAJ,mBAAAe,MAwBaF,MAAS,mCAtBvB,CAAAG,OAAA,OAAAC,QAAA,CAAAC,eAAA,oBAAAC,KAAAC,KAAAC,UAAAxB,MAFC,KAAA,GAAA,OAEDC,EAFCI,EAAAoB,KAAApB,EAAAE,KAAA,GAAAJ,mBAAAe,MAEDjB,EAAAyB,QAFC,KAAA,GAEDxB,EAFCG,EAAAoB,KAEDxB,EAAA0B,IA0BEC,YACM5B,SA3BRU,eA2Be,eAAAmB,SAEXhC,oBAFWK,GA7BdG,EAAAE,KAAA,GAAA,MAAA,KAAA,GAAAF,EAAAC,KAAA,GAAAD,EAAAyB,GAAAzB,EAAA,MAAA,IAED0B,QAAAC,MAAA,uBAAA3B,EAAAyB,IAAAf,MAAA,gDAFC,KAAA,GAAA,IAAA,MAAA,OAAAV,EAAA4B,SAAA,KAAA,KAAA,CAAA,CAAA,GAAA,MAuCgB,SAAAnB,cAAAjB,GACXyB,MAH+D,6BAG/DA,KAAIY,OAAOV,GAAAA,eAtCjB,SAAAI,YAmCU3B,IAAAA,EAAAA,SAnCVS,eAAA,gBAAAyB,EAAA1B,SAAAC,eAAA,WAiEEyB,EAAQC,MAAMC,QAAU,QAjE1BC,EAAAF,MAAAC,QAAA,QA2CI5B,SAAAC,eAAA,cAAiB6B,iBAAA,QAAAC,WAGhBL,EAAAI,iBAAM,QAAAC,WA6BX,SAASA,YA3ET,IAAAF,EAAA7B,SAAAC,eAAA,gBAAAyB,EAAA1B,SAAAC,eAAA,WAAA4B,EAAAF,MAAAC,QAAA,OAAAF,EAAAC,MAAAC,QAAA,OAoDItB,SAAAA,oBAAMb,GAgCRa,MAAK,UAAA0B,OAAWvC,EAAOH,SAAW,kCA1FpCU,SAAS8B,iBAAiB,mBAAoB,WACpB9B,SAASC,eAAe,mBAA1CgC,iBAA2BhC,QAAAA","file":"contact_us.min.js","sourcesContent":["document.addEventListener(\"DOMContentLoaded\", () => {\r\n const sendEmailButton = document.getElementById(\"sendEmailButton\"); // Assuming your button has this ID\r\n\r\n sendEmailButton.addEventListener(\"click\", SendEmail);\r\n});\r\n\r\nasync function SendEmail(event) {\r\n event.preventDefault();\r\n\r\n const firstName = document.getElementById(\"firstName\").value.trim();\r\n const lastName = document.getElementById(\"lastName\").value.trim();\r\n const email = document.getElementById(\"email\").value.trim();\r\n const phone = document.getElementById(\"phone\").value.trim();\r\n const message = document.getElementById(\"message\").value.trim();\r\n\r\n // Input validation\r\n if (!firstName || !lastName || !email || !phone || !message) {\r\n alert(\"All fields are required.\");\r\n return;\r\n }\r\n\r\n if (!validateEmail(email)) {\r\n alert(\"Please enter a valid email address.\");\r\n return;\r\n }\r\n\r\n console.log(Name,email,phone,message);\r\n\r\n if (message.length < 10) {\r\n alert(\"Message must be at least 10 characters long.\");\r\n return;\r\n }\r\n //console.log(\"Email value:\", email , Name, phone, message); \r\n const data = {\r\n Name: Name,\r\n email: email,\r\n phone: phone,\r\n message: message,\r\n };\r\n\r\n try {\r\n const response = await fetch(\"http://127.0.0.1:3000/send-email\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(data),\r\n });\r\n\r\n const result = await response.json();\r\n \r\n if (response.ok) {\r\n showPopup();\r\n document.getElementById(\"contactForm\").reset();\r\n } else {\r\n handleErrorResponse(result);\r\n }\r\n \r\n } catch (error) {\r\n console.error(\"Error sending email:\", error);\r\n alert(\"Error sending email. Please try again later.\");\r\n }\r\n}\r\n\r\nfunction validateEmail(email) {\r\n const re = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\r\n return re.test(String(email).toLowerCase());\r\n}\r\n\r\nfunction showPopup() {\r\n const popup = document.getElementById(\"popupMessage\");\r\n const overlay = document.getElementById(\"overlay\");\r\n \r\n overlay.style.display = \"block\";\r\n popup.style.display = \"block\";\r\n\r\n // Close the popup\r\n document.getElementById(\"closePopup\").addEventListener(\"click\", hidePopup);\r\n \r\n // Close when clicking outside\r\n overlay.addEventListener(\"click\", hidePopup);\r\n}\r\n\r\nfunction hidePopup() {\r\n const popup = document.getElementById(\"popupMessage\");\r\n const overlay = document.getElementById(\"overlay\");\r\n \r\n popup.style.display = \"none\";\r\n overlay.style.display = \"none\";\r\n}\r\n\r\nfunction handleErrorResponse(result) {\r\n alert(`Error: ${result.message || 'An unexpected error occurred.'}`);\r\n}"]} -------------------------------------------------------------------------------- /download (1).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/download (1).jpeg -------------------------------------------------------------------------------- /download (2).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/download (2).jpeg -------------------------------------------------------------------------------- /download (3).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/download (3).jpeg -------------------------------------------------------------------------------- /download.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/download.jpeg -------------------------------------------------------------------------------- /faq.js: -------------------------------------------------------------------------------- 1 | const accordions = document.querySelectorAll(".accordion"); 2 | 3 | accordions.forEach((accordion, index) => { 4 | const header = accordion.querySelector(".accordion__header"); 5 | const content = accordion.querySelector(".accordion__content"); 6 | const icon = accordion.querySelector(".accordion__icon i"); 7 | 8 | header.addEventListener("click", () => { 9 | const isOpen = content.style.height === `${content.scrollHeight}px`; 10 | 11 | accordions.forEach((a, i) => { 12 | const c = a.querySelector(".accordion__content"); 13 | const ic = a.querySelector(".accordion__icon i"); 14 | 15 | if (i === index) { 16 | c.style.height = isOpen ? "0px" : `${c.scrollHeight}px`; 17 | ic.classList.toggle("ri-add-line", isOpen); 18 | ic.classList.toggle("ri-subtract-fill", !isOpen); 19 | // add a new class active to the active accordion 20 | // removeActiveClasses(); 21 | a.classList.toggle("active"); 22 | // end of block 23 | } else { 24 | c.style.height = "0px"; 25 | ic.classList.add("ri-add-line"); 26 | ic.classList.remove("ri-subtract-fill"); 27 | // remove active classes for other accordions 28 | a.classList.remove("active"); 29 | } 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /faq.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/faq.webp -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | package-lock.json 4 | dist -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # WordWise Frontend 2 | 3 | This is the frontend for the WordWise application, a language learning platform. 4 | 5 | ## Prerequisites 6 | 7 | Before you begin, ensure you have the following installed on your system: 8 | - Node.js (preferably the latest LTS version) 9 | - npm (usually comes with Node.js) 10 | 11 | ## Setup Guide 12 | 13 | 1. Navigate to the frontend directory: 14 | ``` 15 | cd frontend 16 | ``` 17 | 18 | 2. Install dependencies: 19 | ``` 20 | npm install 21 | ``` 22 | 23 | 3. Start the development server: 24 | ``` 25 | npm run dev 26 | ``` 27 | This command will start the Vite development server and watch for changes in your Tailwind CSS input file. 28 | 29 | 4. Open your browser and navigate to `http://localhost:5173` (or the URL provided in the console) to view the application. 30 | 31 | ## Available Scripts 32 | 33 | In the project directory, you can run: 34 | 35 | - `npm run dev`: Runs the app in development mode with hot-reloading and Tailwind CSS watching. 36 | - `npm run build`: Builds the app for production to the `dist` folder. 37 | - `npm run preview`: Locally preview the production build. 38 | 39 | ## Project Structure 40 | 41 | - `src/`: Contains the source code for the application 42 | - `components/`: Reusable components 43 | - `pages/`: Individual page components 44 | - `styles/`: CSS files, including Tailwind CSS input file 45 | - `public/`: Static assets that will be served directly 46 | 47 | ## Technologies Used 48 | 49 | - [Vite](https://vitejs.dev/): Next Generation Frontend Tooling 50 | - [Tailwind CSS](https://tailwindcss.com/): A utility-first CSS framework 51 | 52 | ## Contributing 53 | 54 | Please read the main project's CONTRIBUTING.md file for details on our code of conduct and the process for submitting pull requests. 55 | 56 | ## License 57 | 58 | This project is licensed under the MIT License - see the main project's LICENSE.md file for details. 59 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wordwise-frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "concurrently \"npx tailwindcss -i ./src/styles/input.css -o ./src/styles/output.css --watch\" \"vite\"", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "concurrently": "^8.2.0", 13 | "tailwindcss": "^3.4.14", 14 | "vite": "^4.3.9" 15 | }, 16 | "dependencies": { 17 | "firebase": "^11.0.1", 18 | "toastr": "^2.1.4", 19 | "wordwise-frontend": "file:" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/public/blog/post-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/public/blog/post-1.webp -------------------------------------------------------------------------------- /frontend/public/blog/post-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/public/blog/post-2.webp -------------------------------------------------------------------------------- /frontend/public/blog/post-3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/public/blog/post-3.webp -------------------------------------------------------------------------------- /frontend/src/assets/about/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/about/chart.png -------------------------------------------------------------------------------- /frontend/src/assets/blog/1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/1.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/10.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/10.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/11.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/11.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/12.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/12.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/13.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/13.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/14.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/14.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/15.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/15.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/16.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/16.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/17.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/17.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/18.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/18.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/19.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/19.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/2.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/20.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/20.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/21.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/21.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/22.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/22.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/23.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/23.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/24.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/24.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/25.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/25.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/26.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/26.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/27.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/27.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/28.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/28.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/29.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/29.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/3.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/30.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/30.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/31.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/31.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/32.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/32.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/33.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/33.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/34.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/34.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/35.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/35.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/36.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/36.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/37.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/37.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/38.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/38.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/39.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/39.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/4.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/40.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/40.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/41.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/41.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/42.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/42.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/5.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/6.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/6.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/7.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/8.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/8.webp -------------------------------------------------------------------------------- /frontend/src/assets/blog/9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/frontend/src/assets/blog/9.webp -------------------------------------------------------------------------------- /frontend/src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import '../styles/navbar.css' 2 | export function renderNavbar() { 3 | const navbar = document.getElementById('navbar'); 4 | navbar.innerHTML = ` 5 | 38 | `; 39 | 40 | const hamburgerBtn = document.getElementById('hamburger'); 41 | const navbarMenu = document.getElementById('navbar-menu'); 42 | 43 | // Toggle menu visibility on smaller screens 44 | hamburgerBtn.addEventListener('click', () => { 45 | navbarMenu.classList.toggle('open'); // Add or remove 'open' class to trigger slide-in 46 | }); 47 | } 48 | 49 | // Apply saved theme on load 50 | if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { 51 | document.documentElement.classList.add('dark'); 52 | } else { 53 | document.documentElement.classList.remove('dark'); 54 | } 55 | -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WordWise 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 25 | 27 | 28 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import { renderNavbar } from './components/Navbar.js'; 2 | import { renderFooter } from './components/Footer.js'; 3 | import { router } from './utils/router.js'; 4 | import 'toastr/build/toastr.min.css'; 5 | 6 | document.addEventListener('DOMContentLoaded', () => { 7 | renderNavbar(); 8 | renderFooter(); 9 | router(); 10 | }); 11 | 12 | toastr.options = { 13 | "closeButton": true, 14 | "debug": false, 15 | "newestOnTop": true, 16 | "progressBar": true, 17 | "positionClass": "toast-top-right", 18 | "preventDuplicates": false, 19 | "onclick": null, 20 | "showDuration": "300", 21 | "hideDuration": "1000", 22 | "timeOut": "5000", 23 | "extendedTimeOut": "1000", 24 | "showEasing": "swing", 25 | "hideEasing": "linear", 26 | "showMethod": "fadeIn", 27 | "hideMethod": "fadeOut" 28 | } 29 | 30 | window.addEventListener('popstate', router); 31 | -------------------------------------------------------------------------------- /frontend/src/pages/Categories.js: -------------------------------------------------------------------------------- 1 | export function renderCategories(container) { 2 | container.innerHTML = ` 3 |
4 |
5 |

Blog Categories

6 |

Explore our articles by topic

7 |
8 | 9 |
10 | ${renderCategorySection("Vocabulary", [ 11 | { 12 | title: "10 Essential Phrasal Verbs for Daily Conversations", 13 | description: "Master these common phrasal verbs to enhance your everyday English communication.", 14 | date: "2023-05-15", 15 | link: "/blog/post-1" 16 | }, 17 | { 18 | title: "Building Your Vocabulary: 5 Effective Strategies", 19 | description: "Discover proven methods to expand your word bank and retain new vocabulary.", 20 | date: "2023-05-20", 21 | link: "/blog/post-2" 22 | } 23 | ])} 24 | 25 | ${renderCategorySection("Grammar", [ 26 | { 27 | title: "Mastering the Present Perfect: When and How to Use It", 28 | description: "Unravel the mysteries of the present perfect tense and learn to use it confidently in your writing and speaking.", 29 | date: "2023-05-25", 30 | link: "/blog/post-3" 31 | }, 32 | { 33 | title: "Common Grammar Mistakes and How to Avoid Them", 34 | description: "Identify and correct frequent grammatical errors to improve your language accuracy.", 35 | date: "2023-05-30", 36 | link: "/blog/post-4" 37 | } 38 | ])} 39 | 40 | ${renderCategorySection("Pronunciation", [ 41 | { 42 | title: "The Secret to Perfect English Pronunciation: Stress and Intonation", 43 | description: "Learn how word stress and sentence intonation can dramatically improve your spoken English.", 44 | date: "2023-06-05", 45 | link: "/blog/post-5" 46 | }, 47 | { 48 | title: "Mastering the 'Th' Sound: Tips and Exercises", 49 | description: "Conquer one of the trickiest sounds in English with our step-by-step guide and practice exercises.", 50 | date: "2023-06-10", 51 | link: "/blog/post-6" 52 | } 53 | ])} 54 | 55 | ${renderCategorySection("Culture", [ 56 | { 57 | title: "Understanding British vs. American English: Key Differences", 58 | description: "Explore the nuances between British and American English in vocabulary, spelling, and pronunciation.", 59 | date: "2023-06-15", 60 | link: "/blog/post-7" 61 | }, 62 | { 63 | title: "Idioms from Around the World: Expanding Your Cultural Vocabulary", 64 | description: "Discover fascinating idioms from different cultures and their English equivalents.", 65 | date: "2023-06-20", 66 | link: "/blog/post-8" 67 | } 68 | ])} 69 | 70 | ${renderCategorySection("Learning Tips", [ 71 | { 72 | title: "5 Proven Techniques to Boost Your Language Learning", 73 | description: "Discover effective strategies to accelerate your language acquisition and retention.", 74 | date: "2023-06-25", 75 | link: "/blog/post-9" 76 | }, 77 | { 78 | title: "The Power of Immersion: Creating a Language-Rich Environment", 79 | description: "Learn how to surround yourself with your target language for faster and more natural learning.", 80 | date: "2023-06-30", 81 | link: "/blog/post-10" 82 | } 83 | ])} 84 |
85 |
86 | `; 87 | } 88 | 89 | function renderCategorySection(category, posts) { 90 | return ` 91 |
92 |

${category}

93 |
    94 | ${posts.map(post => renderPost(post)).join('')} 95 |
96 |
97 | `; 98 | } 99 | 100 | function renderPost(post) { 101 | return ` 102 |
  • 103 | 104 |

    ${post.title}

    105 |

    ${post.description}

    106 |
    107 | 108 | 109 | 110 | 111 |
    112 |
    113 |
  • 114 | `; 115 | } 116 | 117 | function formatDate(dateString) { 118 | const options = { year: 'numeric', month: 'long', day: 'numeric' }; 119 | return new Date(dateString).toLocaleDateString('en-US', options); 120 | } 121 | -------------------------------------------------------------------------------- /frontend/src/pages/GoogleTranslator.js: -------------------------------------------------------------------------------- 1 | export function renderTranslator(container) { 2 | const loadGoogleTranslateScript = () => { 3 | if (!document.getElementById('google_translate_script')) { 4 | const script = document.createElement('script'); 5 | script.type = 'text/javascript'; 6 | script.src = 'https://translate.google.com/translate_a/element.js?cb=googleTranslateInit'; 7 | script.id = 'google_translate_script'; 8 | script.onerror = () => console.error('Error loading Google Translate script'); 9 | document.body.appendChild(script); 10 | } 11 | }; 12 | 13 | const cleanUpGadgetText = () => { 14 | const gadgetElement = document.querySelector('.goog-te-gadget'); 15 | if (gadgetElement) { 16 | const textNodes = gadgetElement.childNodes; 17 | textNodes.forEach(node => { 18 | if (node.nodeType === Node.TEXT_NODE) { 19 | node.textContent = ''; // Clear text content 20 | } 21 | }); 22 | } 23 | }; 24 | 25 | window.googleTranslateInit = () => { 26 | if (!window.google?.translate?.TranslateElement) { 27 | setTimeout(window.googleTranslateInit, 100); 28 | } else { 29 | new window.google.translate.TranslateElement( 30 | { 31 | pageLanguage: 'en', 32 | includedLanguages: 'en,hi,pa,sa,mr,ur,bn,es,ja,ko,zh-CN,es,nl,fr,de,it,ta,te,gu', 33 | layout: window.google.translate.TranslateElement.InlineLayout.HORIZONTAL, 34 | defaultLanguage: 'en', 35 | autoDisplay: false, 36 | }, 37 | container 38 | ); 39 | } 40 | cleanUpGadgetText(); 41 | }; 42 | 43 | loadGoogleTranslateScript(); 44 | 45 | if (window.google && window.google.translate) { 46 | window.googleTranslateInit(); 47 | } 48 | 49 | // Add custom styles 50 | const style = document.createElement('style'); 51 | style.innerHTML = ` 52 | .goog-te-combo { 53 | background-color: #272d39; 54 | border-radius: 0.4rem; 55 | padding: 0.5rem; 56 | font-size: 0.8rem !important; 57 | transition: all 0.3s ease-in-out; 58 | outline: none; 59 | font-weight: 400; 60 | cursor: pointer; 61 | text-align: center; 62 | color: #fff; 63 | } 64 | .goog-te-combo:hover { 65 | background-color: #272d31; 66 | border-color: #0056b3; 67 | color: #eee; 68 | transform: scale(1.02); 69 | } 70 | .goog-logo-link { 71 | display: none !important; 72 | } 73 | .goog-te-gadget { 74 | color: transparent !important; 75 | } 76 | .goog-te-gadget > span > a { 77 | display: none !important; 78 | } 79 | .goog-te-banner-frame { 80 | display: none !important; 81 | } 82 | .goog-te-menu-frame { 83 | max-height: 400px !important; 84 | overflow-y: auto !important; 85 | background-color: #ffffff; 86 | border: 2px solid #007bff; 87 | border-radius: 0.75rem; 88 | box-shadow: 0 4px 8px rgba(0, 123, 255, 0.1); 89 | } 90 | .skiptranslate > iframe { 91 | height: 0 !important; 92 | border-style: none; 93 | box-shadow: none; 94 | } 95 | `; 96 | document.head.appendChild(style); 97 | } 98 | -------------------------------------------------------------------------------- /frontend/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | export function renderHome(container) { 2 | container.innerHTML = ` 3 |
    4 |

    5 | Share Your Story with the World 6 |

    7 |

    8 | WordWise offers a platform for passionate writers to express their ideas and connect with readers worldwide. 9 | Start your blogging journey today and join our community of creative minds. 10 |

    11 |
    12 | 18 | 24 |
    25 |
    26 |
    27 |
    28 |

    29 | Trending Blog Titles 30 |

    31 | 34 |
    35 | 38 |
    39 | `; 40 | 41 | // Fetch and render the titles 42 | fetchTrendingTitles(); 43 | } 44 | 45 | async function fetchTrendingTitles() { 46 | try { 47 | const response = await fetch('http://127.0.0.1:8000/generate_trending_titles'); 48 | const data = await response.json(); 49 | 50 | // Assuming 'data.trending_titles' is an array of titles (without numbering) 51 | const titles = data.trending_titles.map(item => item 52 | .replace(/^\d+\.\s*/, '') 53 | .replace(/^\*\*(.*)\*\*$/, '$1') 54 | ); // Clean numbering 55 | 56 | const trendingTitlesContainer = document.getElementById('trending-titles'); 57 | const nextButton = document.getElementById('nextTitleButton'); 58 | 59 | // Clear any existing titles 60 | trendingTitlesContainer.innerHTML = ''; 61 | 62 | let currentIndex = 0; 63 | 64 | // Function to render the current title 65 | function renderNextTitle() { 66 | if (currentIndex < titles.length) { 67 | const title = titles[currentIndex]; 68 | const titleElement = document.createElement('div'); 69 | titleElement.className = 'text-lg text-gray-800 dark:text-gray-200'; 70 | titleElement.textContent = title; 71 | 72 | titleElement.setAttribute('data-aos', 'fade-right'); 73 | titleElement.setAttribute('data-aos-duration', '1200'); 74 | 75 | // Append the title element 76 | trendingTitlesContainer.appendChild(titleElement); 77 | currentIndex++; 78 | } 79 | if (currentIndex === titles.length) { 80 | nextButton.classList.add('hidden'); // Hide the button if no more titles 81 | } 82 | } 83 | 84 | // Initially render the first title 85 | renderNextTitle(); 86 | 87 | // Show the button after the first title is rendered 88 | nextButton.classList.remove('hidden'); 89 | 90 | // Set up the button to render the next title 91 | nextButton.onclick = renderNextTitle; 92 | } catch (error) { 93 | console.error('Error fetching trending titles:', error); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /frontend/src/pages/Login.js: -------------------------------------------------------------------------------- 1 | // Function to render the login UI 2 | import { initializeApp } from "firebase/app"; 3 | import { getAuth, signInWithPopup, GoogleAuthProvider, onAuthStateChanged, signOut as firebaseSignOut } from "firebase/auth"; 4 | 5 | // Initialize Firebase 6 | const firebaseConfig = { 7 | apiKey: "AIzaSyDwowmzH0skVhieH3KPgIP8_vQBzhJmIi4", 8 | authDomain: "wordwise-d1607.firebaseapp.com", 9 | projectId: "wordwise-d1607", 10 | storageBucket: "wordwise-d1607.appspot.com", 11 | messagingSenderId: "426579758621", 12 | appId: "1:426579758621:web:5bc883cd5eea3a416940f4", 13 | measurementId: "G-QL9ZF6G3HH" 14 | }; 15 | 16 | const app = initializeApp(firebaseConfig); 17 | const auth = getAuth(app); 18 | const googleAuthProvider = new GoogleAuthProvider(); 19 | 20 | export function renderLogin(container) { 21 | container.innerHTML = ` 22 | 24 | 25 |
    26 | 30 | 31 | 35 | 36 | 39 |
    40 | `; 41 | 42 | // Attach event listeners for sign-in and sign-out buttons 43 | document.getElementById('google-login-btn').addEventListener('click', signInWithGoogle); 44 | document.getElementById('logout-btn').addEventListener('click', signOut); 45 | } 46 | 47 | // ... [Firebase setup code here] ... 48 | 49 | // Function to handle Google sign-in 50 | function signInWithGoogle() { 51 | signInWithPopup(auth, googleAuthProvider) 52 | .then((result) => { 53 | // Check if "Remember Me" is checked 54 | const rememberMe = document.getElementById('remember-me').checked; 55 | if (rememberMe) { 56 | localStorage.setItem('rememberedEmail', result.user.email); 57 | } else { 58 | localStorage.removeItem('rememberedEmail'); 59 | } 60 | initializeUser(result.user); 61 | }) 62 | .catch((error) => { 63 | console.error('Error during sign-in:', error); 64 | }); 65 | } 66 | 67 | // Function to log out the user 68 | export default function signOut() { 69 | firebaseSignOut(auth) 70 | .then(() => { 71 | initializeUser(null); 72 | document.getElementById('google-login-btn').classList.remove('hidden'); 73 | localStorage.removeItem('rememberedEmail'); // Clear remembered email on logout 74 | }) 75 | .catch((error) => { 76 | console.error('Error during sign-out:', error); 77 | }); 78 | } 79 | 80 | // Function to check authentication state 81 | function initializeUser(user) { 82 | const authStatus = document.getElementById('auth-status'); 83 | const googleBtn = document.getElementById('google-login-btn'); 84 | const signoutBtn = document.getElementById('logout-btn') 85 | if (user) { 86 | authStatus.innerHTML = `View profile: ${user.displayName}`; 87 | googleBtn.classList.add('hidden'); 88 | signoutBtn.classList.remove('hidden'); 89 | authStatus.classList.remove('hidden') 90 | 91 | authStatus.style.cursor = 'pointer'; 92 | authStatus.addEventListener('click', () => { 93 | const name = user.displayName ? encodeURIComponent(user.displayName) : 'unknown'; 94 | const email = user.email ? encodeURIComponent(user.email) : 'unknown'; 95 | const profilePic = user.photoURL ? encodeURIComponent(user.photoURL) : ''; 96 | 97 | window.location.href = `/profile?name=${name}&email=${email}&profilePic=${profilePic}`; 98 | }); 99 | } else { 100 | authStatus.innerHTML = ''; 101 | signoutBtn.classList.add('hidden'); 102 | authStatus.removeEventListener('click', () => { }); 103 | } 104 | } 105 | 106 | // Listen for auth state changes 107 | onAuthStateChanged(auth, (user) => { 108 | initializeUser(user); 109 | }); 110 | -------------------------------------------------------------------------------- /frontend/src/pages/ReadMoreBlog.js: -------------------------------------------------------------------------------- 1 | const blogPosts = [ 2 | { 3 | id: 1, 4 | title: "Mastering Vocabulary: 10 Words to Boost Your Language Skills", 5 | excerpt: "Expanding your vocabulary is essential for improving your language proficiency. By incorporating new words into your daily conversations, you not only enrich your communication but also increase your confidence in expressing complex ideas. This article presents ten powerful words that can elevate your language skills and enhance your ability to articulate thoughts clearly and persuasively. Understanding the nuances of each word will allow you to use them appropriately in different contexts, making your speech and writing more impactful. With consistent practice, you will find yourself communicating with greater precision and clarity in all your interactions.", 6 | date: "2023-05-15", 7 | tags: "Vocabulary, Learning Tips", 8 | imagePath: "../assets/blog/4.webp" 9 | }, 10 | { 11 | id: 2, 12 | title: "Essential Grammar Rules for Clear Communication", 13 | excerpt: "Grammar forms the backbone of effective communication. Mastering essential grammar rules not only enhances your language skills but also enables you to convey your message with clarity and precision. In this article, we will explore key grammatical principles that every writer and speaker should know, including proper sentence structure, subject-verb agreement, and punctuation usage. By understanding these fundamentals, you can avoid common mistakes that lead to misunderstandings. Clear communication is vital in both personal and professional settings, and solid grammar skills will enhance your credibility and help you connect more effectively with your audience.", 14 | date: "2023-05-20", 15 | tags: "Grammar, Writing", 16 | imagePath: "../assets/blog/2.webp" 17 | }, 18 | { 19 | id: 3, 20 | title: "Mastering Pronunciation: Tips and Tricks", 21 | excerpt: "Pronunciation plays a crucial role in effective communication. Mastering the sounds of a language can significantly improve your speaking skills and boost your confidence in conversations. In this article, we will share practical tips and tricks to help you enhance your pronunciation. From practicing individual sounds to intonation and rhythm, these techniques will enable you to sound more natural when speaking. Additionally, we’ll discuss the importance of listening to native speakers and mimicking their speech patterns. By dedicating time to practice and incorporating these strategies, you can ensure that your message is understood and appreciated by your audience.", 22 | date: "2023-05-25", 23 | tags: "Pronunciation, Speaking", 24 | imagePath: "../assets/blog/3.webp" 25 | } 26 | ]; 27 | 28 | const formatDate = (dateStr) => { 29 | const options = { year: 'numeric', month: 'long', day: 'numeric' }; 30 | return new Date(dateStr).toLocaleDateString(undefined, options); 31 | }; 32 | 33 | 34 | // Function to extract the `id` parameter from the URL 35 | function getBlogIdFromUrl() { 36 | // const pathParts = window.location.pathname.split('/'); 37 | return Math.floor(Math.random() * blogPosts.length) + 1; // Get the last part, which is the blog ID 38 | } 39 | 40 | // Find and render the blog post based on the ID from the URL 41 | export function renderFullBlogPost(container) { 42 | const blogId = getBlogIdFromUrl(); 43 | console.log(blogId) 44 | const blog = blogPosts.find(post => post.id === blogId); 45 | 46 | if (blog) { 47 | container.innerHTML = ` 48 |
    49 | ${blog.title} 50 |
    51 |

    ${blog.title}

    52 |
    53 | 54 | 55 | 56 | 57 | ${blog.tags} 58 |
    59 |
    60 | ${blog.excerpt} 61 |
    62 | Back to Blog 63 |
    64 |
    65 | `; 66 | } else { 67 | document.getElementById('app').innerHTML = '

    Blog post not found.

    '; 68 | } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /frontend/src/styles/footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | background-color: #f8f9fa; 3 | text-align: center; 4 | } 5 | 6 | body.dark-mode footer { 7 | background-color: #222; 8 | } 9 | 10 | @media (max-width: 1200px) { 11 | .footer-container { 12 | padding: 20px; 13 | } 14 | .col-3-main { 15 | flex-direction: row; 16 | flex-wrap: wrap; 17 | } 18 | .col-3 { 19 | width: 33.33%; 20 | } 21 | } 22 | 23 | @media (max-width: 992px) { 24 | .col-3 { 25 | width: 50%; /* Stack 2 columns side by side on medium screens */ 26 | } 27 | } 28 | 29 | @media (max-width: 768px) { 30 | .col-3 { 31 | width: 100%; /* Stack all columns vertically on small screens */ 32 | margin-bottom: 20px; 33 | } 34 | 35 | .feature-post div { 36 | flex-direction: column; 37 | } 38 | 39 | .tags { 40 | text-align: center; 41 | } 42 | } 43 | 44 | @media (max-width: 576px) { 45 | .feature-post div { 46 | align-items: center; 47 | } 48 | 49 | .tags.social { 50 | justify-content: center; 51 | } 52 | 53 | .wordwise-footer-content { 54 | text-align: center; 55 | } 56 | } 57 | 58 | /* Optional: You can also set max-width for images */ 59 | img.object-fit { 60 | max-width: 100%; /* Makes images responsive */ 61 | height: auto; 62 | } 63 | -------------------------------------------------------------------------------- /frontend/src/styles/input.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @import 'navbar.css'; 6 | @import 'footer.css'; 7 | 8 | body { 9 | font-family: Arial, sans-serif; 10 | margin: 0; 11 | padding: 0; 12 | transition: background-color 0.3s, color 0.3s; 13 | } 14 | 15 | body.dark-mode { 16 | background-color: #333; 17 | color: #fff; 18 | } 19 | 20 | #app { 21 | display: flex; 22 | flex-direction: column; 23 | min-height: 100vh; 24 | } 25 | 26 | #content { 27 | flex: 1; 28 | padding: 20px; 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/styles/navbar.css: -------------------------------------------------------------------------------- 1 | nav ul { 2 | list-style-type: none; 3 | padding: 0; 4 | display: flex; 5 | justify-content: space-around; 6 | } 7 | 8 | nav a { 9 | text-decoration: none; 10 | color: #333; 11 | } 12 | 13 | body.dark-mode nav { 14 | background-color: #222; 15 | } 16 | 17 | body.dark-mode nav a { 18 | color: #fff; 19 | } 20 | 21 | .form-popup { 22 | display: none; 23 | position: fixed; 24 | z-index: 9; 25 | left: 50%; 26 | top: 50%; 27 | transform: translate(-50%, -50%); 28 | width: 100%; 29 | max-width: 500px; 30 | padding: 20px; 31 | background-color: white; 32 | box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.6); 33 | border-radius: 10px; 34 | box-sizing: border-box; 35 | } 36 | 37 | .form-container h2 { 38 | margin-bottom: 20px; 39 | color: #333; 40 | } 41 | 42 | .form-group { 43 | margin-bottom: 15px; 44 | } 45 | 46 | .form-control { 47 | border-radius: 5px; 48 | padding: 12px; 49 | font-size: 16px; 50 | line-height: 1.5; 51 | box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.1); 52 | width: 100%; 53 | } 54 | 55 | .forgot { 56 | text-align: right; 57 | margin-left: 50px; 58 | font-size: 10px; 59 | margin-top: -40px; 60 | 61 | } 62 | 63 | .btn { 64 | margin-top: 10px; 65 | border-radius: 5px; 66 | } 67 | 68 | .eye-icon { 69 | position: absolute; 70 | right: 15px; 71 | top: 38px; 72 | cursor: pointer; 73 | color: #007bff; 74 | } 75 | 76 | .close-icon { 77 | font-size: 24px; 78 | cursor: pointer; 79 | float: right; 80 | } 81 | 82 | .position-relative { 83 | position: relative; 84 | } 85 | 86 | /* Login button jumping issue fix */ 87 | .btn-primary { 88 | transition: all 0.3s ease; 89 | /* Smooth transition */ 90 | } 91 | 92 | .btn-primary:hover { 93 | background-color: #0056b3; 94 | } 95 | 96 | .google-btn { 97 | display: flex; 98 | align-items: center; 99 | justify-content: center; 100 | } 101 | 102 | .google-logo { 103 | width: 20px; 104 | height: 20px; 105 | margin-right: 10px; 106 | } 107 | 108 | .btn-primary:hover { 109 | background-color: #0056b3; 110 | } 111 | 112 | .open { 113 | background: #0f1522f0; 114 | position: absolute; 115 | left: 42%; 116 | display: flex !important; 117 | align-items: start; 118 | top: 0%; 119 | height: 100vh; 120 | border-radius: 15px; 121 | padding: 30px; 122 | padding-top: 30px; 123 | padding-top: 75px; 124 | z-index: 9; 125 | } -------------------------------------------------------------------------------- /frontend/src/utils/router.js: -------------------------------------------------------------------------------- 1 | import { renderHome } from '../pages/Home.js'; 2 | import { renderBlogs } from '../pages/Blogs.js'; 3 | import { renderAddBlog } from '../pages/AddBlog.js'; 4 | import { renderCategories } from '../pages/Categories.js'; 5 | import { renderAbout } from '../pages/About.js'; 6 | import { renderContact } from '../pages/Contact.js'; 7 | import { renderFeedback } from '../pages/Feedback.js'; 8 | import { renderProfilePage } from '../pages/BloggerProfile.js'; 9 | import { renderFullBlogPost } from '../pages/ReadMoreBlog.js'; 10 | import { renderTermsOfUse } from '../pages/TermsOfUse.js'; 11 | import { renderDiscussionForum } from '../pages/DiscussionForum.js'; 12 | import { renderStories } from '../pages/Stories.js'; 13 | 14 | const routes = { 15 | '/': renderHome, 16 | '/blogs': renderBlogs, 17 | '/add-blog': renderAddBlog, 18 | '/categories': renderCategories, 19 | '/about': renderAbout, 20 | '/contact': renderContact, 21 | '/feedback': renderFeedback, 22 | '/profile': renderProfilePage, 23 | '/discussion': renderDiscussionForum, 24 | '/readmore': renderFullBlogPost, 25 | '/termsOfUse': renderTermsOfUse, 26 | '/stories': renderStories, 27 | }; 28 | 29 | export function router() { 30 | const path = window.location.pathname; 31 | const renderFunction = routes[path] || renderHome; 32 | const content = document.getElementById('content'); 33 | content.innerHTML = ''; 34 | renderFunction(content); 35 | } 36 | 37 | document.addEventListener('click', e => { 38 | if (e.target.matches('[data-link]')) { 39 | e.preventDefault(); 40 | history.pushState(null, null, e.target.href); 41 | router(); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./src/**/*.{html,js}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | darkMode: 'class', 9 | }; 10 | -------------------------------------------------------------------------------- /frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | 3 | export default defineConfig({ 4 | root: 'src', 5 | build: { 6 | outDir: '../dist', 7 | emptyOutDir: true, 8 | }, 9 | server: { 10 | open: true, 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /give_feedback.js: -------------------------------------------------------------------------------- 1 | document.getElementById('feedbackForm').addEventListener('submit', function(event) { 2 | event.preventDefault(); // Prevent form submission 3 | 4 | // Validation 5 | const name = document.getElementById('name').value.trim(); 6 | const email = document.getElementById('email').value.trim(); 7 | const feedback = document.getElementById('feedback').value.trim(); 8 | const rating = document.getElementById('rating').value; 9 | 10 | if (name === '' || email === '' || feedback === '' || rating === '') { 11 | alert('Please fill out all fields.'); 12 | return; 13 | } 14 | 15 | // If validation passes, submit the form (e.g., send data to server) 16 | alert('Thank you for your feedback!'); 17 | document.getElementById('feedbackForm').reset(); // Reset the form 18 | }); 19 | -------------------------------------------------------------------------------- /google_signin.js: -------------------------------------------------------------------------------- 1 | function handleGoogleLogin() { 2 | // The pop up for google login will only appear if the clientID is provided 3 | const clientId = 'YOUR_CLIENT_ID'; // Replace with your actual Client ID 4 | 5 | // Initialize the Google Identity Services library 6 | window.google.accounts.id.initialize({ 7 | client_id: clientId, 8 | callback: handleCredentialResponse 9 | }); 10 | 11 | // Prompt the user to sign in 12 | window.google.accounts.id.prompt(); 13 | } 14 | 15 | function handleCredentialResponse(response) { 16 | const token = response.credential; // This is the access token 17 | 18 | // Send the token to your backend for verification 19 | // Backend logic has to be written 20 | fetch('http://localhost:3000/oauth/google-login', { 21 | method: 'POST', 22 | headers: { 23 | 'Content-Type': 'application/json' 24 | }, 25 | body: JSON.stringify({ token }) 26 | }) 27 | .then(res => res.json()) 28 | .then(data => { 29 | if (data.token) { 30 | // Save the token in cookies or local storage 31 | document.cookie = `token=${data.token}; path=/; max-age=${60 * 60 * 24}`; // 1 day expiration 32 | 33 | // Redirect to home or other page 34 | window.location.href = '/home'; // Adjust as needed 35 | } else { 36 | alert('Login failed. Please try again.'); 37 | } 38 | }) 39 | .catch(error => { 40 | console.error('Error during Google login:', error); 41 | alert('Google login failed. Please try again.'); 42 | }); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /home.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/home.webp -------------------------------------------------------------------------------- /icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/icon-192x192.png -------------------------------------------------------------------------------- /icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/icon-512x512.png -------------------------------------------------------------------------------- /images/Untitled design.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/Untitled design.webp -------------------------------------------------------------------------------- /images/android-chrome-192x192.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/android-chrome-192x192.webp -------------------------------------------------------------------------------- /images/android-chrome-512x512.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/android-chrome-512x512.webp -------------------------------------------------------------------------------- /images/apple-touch-icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/apple-touch-icon.webp -------------------------------------------------------------------------------- /images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/background.jpg -------------------------------------------------------------------------------- /images/favi.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/favi.webp -------------------------------------------------------------------------------- /images/favicon-16x16.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/favicon-16x16.webp -------------------------------------------------------------------------------- /images/favicon-32x32.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/favicon-32x32.webp -------------------------------------------------------------------------------- /images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/favicon.ico -------------------------------------------------------------------------------- /images/favicon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/favicon.webp -------------------------------------------------------------------------------- /images/profile.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/profile.webp -------------------------------------------------------------------------------- /images/website.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ANSHIKA-26/WordWise/35306bcecabfe172acab453a8de12bf1ffde08dc/images/website.webp -------------------------------------------------------------------------------- /login.css: -------------------------------------------------------------------------------- 1 | main{ 2 | width: 100vw; 3 | height: 84vh; 4 | background: url(images/login_bg.jpg); 5 | background-size: cover; 6 | background-position: center; 7 | background-repeat: no-repeat; 8 | background-attachment: fixed; 9 | } 10 | .container-login{ 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | min-height: 100%; 15 | } 16 | .box-login{ 17 | background-color: rgba(238, 216, 187, 0.849); 18 | color: white; 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: center; 22 | position: relative; 23 | padding: 0px 20px 30px 20px; 24 | height: 550px; 25 | width: 450px; 26 | border: 3px solid rgba(245, 135, 62, 0.882); 27 | border-radius: 23px; 28 | -webkit-backdrop-filter: blur(15px); 29 | backdrop-filter: blur(-15px); 30 | overflow: hidden; 31 | } 32 | .top-header{ 33 | text-align: center; 34 | margin: 30px 0; 35 | margin-top: 0; 36 | } 37 | .top-header h2{ 38 | color: brown; 39 | font-size: 29px; 40 | font-weight: 700; 41 | margin-bottom: 8px; 42 | margin-left: 18px; 43 | } 44 | .input-group{ 45 | width: 100%; 46 | } 47 | .input-field{ 48 | margin: 12px 0; 49 | position: relative; 50 | } 51 | .input-box{ 52 | /* width: 100%; */ 53 | margin-top: 8px; 54 | margin-left: 13px; 55 | height: 40px; 56 | width: 340px; 57 | font-size: 15px; 58 | color: black; 59 | border: 1px solid rgb(178, 95, 18); 60 | border-radius:10px; 61 | padding: 2px 15px 0 20px; 62 | background: rgba(244, 208, 154, 0.926); 63 | backdrop-filter: blur(2px); 64 | outline: none; 65 | } 66 | .input-field label{ 67 | color: brown; 68 | font-size: 18px; 69 | transition: .3s ease-in-out; 70 | 71 | } 72 | .remember{ 73 | display: flex; 74 | font-size: 16px; 75 | margin: 12px 0 20px 0; 76 | color: brown; 77 | } 78 | .check{ 79 | margin-right: 8px; 80 | width: 14px; 81 | } 82 | .Input-submit{ 83 | width: 100%; 84 | height: 50px; 85 | font-size: 15px; 86 | font-weight: 500; 87 | border: none; 88 | border-radius: 10px; 89 | background: rgb(184, 98, 5); 90 | color: white; 91 | box-shadow: 0px 4px 20px rgba(169, 115, 61, 0.726); 92 | cursor: pointer; 93 | transition: .4s; 94 | } 95 | .Input-submit:hover{ 96 | background: wheat; 97 | color: brown; 98 | border: 2px solid brown; 99 | box-shadow: 0px 4px 20px rgba(117, 88, 54, 0.32); 100 | } 101 | .forgot{ 102 | text-align:end; 103 | font-size: 13px; 104 | padding: 23px; 105 | } 106 | .forgot a{ 107 | text-decoration: none; 108 | color: rgb(254, 252, 252); 109 | color: brown; 110 | } 111 | .new{ 112 | text-align:center; 113 | font-size: 16px; 114 | padding: 13px; 115 | font-weight: 500; 116 | color: brown; 117 | } 118 | .new a{ 119 | text-decoration: none; 120 | color: brown; 121 | font-weight: bolder; 122 | } -------------------------------------------------------------------------------- /main2.js: -------------------------------------------------------------------------------- 1 | let toggle = document.querySelector("#header .toggle-button"); 2 | let collapse = document.querySelectorAll("#header .collapse"); 3 | 4 | toggle.addEventListener('click' , function(){ 5 | collapse.forEach(col => col.classList.toggle("collapse-toggle")); 6 | }) 7 | //swiper library 8 | // main.js 9 | document.addEventListener('DOMContentLoaded', function () { 10 | var swiper = new Swiper('.swiper-container', { 11 | direction: 'horizontal', 12 | loop: true, 13 | slidesPerView: 1, 14 | autoplay: { 15 | delay: 3000 16 | }, 17 | pagination: { 18 | el: '.swiper-pagination', 19 | clickable: true, 20 | }, 21 | navigation: { 22 | nextEl: '.swiper-button-next', 23 | prevEl: '.swiper-button-prev', 24 | }, 25 | }); 26 | }); 27 | window.onscroll = function(){ myFunction()}; 28 | 29 | // get the current value 30 | let navbar = document.getElementById("header"); 31 | 32 | // get the navbar position 33 | let sticky = navbar.offsetTop; 34 | 35 | // sticky function 36 | function myFunction(){ 37 | if(window.pageYOffset >= sticky){ 38 | navbar.classList.add("sticky"); 39 | }else{ 40 | navbar.classList.remove("sticky"); 41 | } 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WordWise", 3 | "short_name": "WordWise", 4 | "start_url": "/", 5 | "display": "standalone", 6 | "background_color": "#ffffff", 7 | "theme_color": "#000000", 8 | "icons": [ 9 | { 10 | "src": "/icon-192x192.png", 11 | "sizes": "192x192", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "/icon-512x512.png", 16 | "sizes": "512x512", 17 | "type": "image/png" 18 | } 19 | ] 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wordwise", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "wordwise", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wordwise": "file:" 13 | } 14 | }, 15 | "node_modules/wordwise": { 16 | "resolved": "", 17 | "link": true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wordwise", 3 | "version": "1.0.0", 4 | "description": "![GSSoC-Ext](/Untitled%20design.png)\r # 📖 WordWise Blogging Website 📝", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "wordwise": "file:" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /profile.js: -------------------------------------------------------------------------------- 1 | // profile.js 2 | document.addEventListener("DOMContentLoaded", async () => { 3 | try { 4 | // Fetch user profile data from backend 5 | const response = await fetch("http://localhost:5000/api/users/profile", { 6 | method: "GET", 7 | headers: { 8 | "Content-Type": "application/json", 9 | "Authorization": `Bearer ${localStorage.getItem("authToken")}`, // Use your auth token here 10 | }, 11 | }); 12 | 13 | if (!response.ok) { 14 | throw new Error("Failed to fetch profile data."); 15 | } 16 | 17 | // Parse the JSON data 18 | const profileData = await response.json(); 19 | 20 | // Populate profile data in HTML 21 | document.querySelector(".profile-name").textContent = profileData.username; 22 | document.querySelector(".profile-email").textContent = `Email: ${profileData.email}`; 23 | } catch (error) { 24 | console.error("Error fetching profile data:", error); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /profiledropdown.js: -------------------------------------------------------------------------------- 1 | function toggleDropdown() { 2 | var dropdownMenu = document.getElementById('dropdownMenu'); 3 | dropdownMenu.classList.toggle('show'); // Toggles the 'show' class 4 | } 5 | 6 | // Close the dropdown if clicked outside 7 | window.onclick = function(event) { 8 | if (!event.target.closest('.profile-icon')) { // Updated to closest to avoid immediate closing 9 | var dropdowns = document.getElementsByClassName('dropdown-menu'); 10 | for (var i = 0; i < dropdowns.length; i++) { 11 | var openDropdown = dropdowns[i]; 12 | if (openDropdown.classList.contains('show')) { 13 | openDropdown.classList.remove('show'); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /progress_bar.js: -------------------------------------------------------------------------------- 1 | // Function to update the progress bar width as the user scrolls 2 | function updateProgressBar() { 3 | const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; 4 | const scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight; 5 | const scrollPercentage = (scrollTop / scrollHeight) * 100; 6 | 7 | document.getElementById('progress-bar').style.width = scrollPercentage + '%'; 8 | } 9 | 10 | // Event listener for scrolling to update the progress bar 11 | window.addEventListener('scroll', updateProgressBar); 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-cors 3 | transformers 4 | torch -------------------------------------------------------------------------------- /scripts.js: -------------------------------------------------------------------------------- 1 | // Register the service worker 2 | if ('serviceWorker' in navigator) { 3 | window.addEventListener('load', () => { 4 | navigator.serviceWorker.register('/sw.js') // Pointing to the sw.js file 5 | .then(registration => { 6 | console.log('ServiceWorker registration successful:', registration); 7 | }) 8 | .catch(error => { 9 | console.error('ServiceWorker registration failed:', error); 10 | }); 11 | }); 12 | 13 | 14 | } -------------------------------------------------------------------------------- /share.js: -------------------------------------------------------------------------------- 1 | // Function to open the sharing modal 2 | function openShareModal(url, title) { 3 | const modal = document.getElementById('share-modal'); 4 | modal.style.display = 'block'; // Show the modal 5 | 6 | // Set the sharing links in the modal 7 | const facebookShareLink = document.getElementById('share-facebook'); 8 | const twitterShareLink = document.getElementById('share-twitter'); 9 | const linkedinShareLink = document.getElementById('share-linkedin'); 10 | const pinterestShareLink = document.getElementById('share-pinterest'); 11 | 12 | facebookShareLink.href = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`; 13 | twitterShareLink.href = `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`; 14 | linkedinShareLink.href = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`; 15 | pinterestShareLink.href = `https://pinterest.com/pin/create/button/?url=${encodeURIComponent(url)}&description=${encodeURIComponent(title)}`; 16 | } 17 | 18 | // Function to close the modal 19 | function closeShareModal() { 20 | const modal = document.getElementById('share-modal'); 21 | modal.style.display = 'none'; // Hide the modal 22 | } 23 | 24 | // Event listener for close button 25 | document.addEventListener('DOMContentLoaded', () => { 26 | document.getElementById('close-modal').addEventListener('click', closeShareModal); 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /signup.css: -------------------------------------------------------------------------------- 1 | main{ 2 | width: 100vw; 3 | height: 84vh; 4 | background: url(images/login_bg.jpg); 5 | background-size: cover; 6 | background-position: center; 7 | background-repeat: no-repeat; 8 | background-attachment: fixed; 9 | } 10 | .container-signup{ 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | max-height: 900px; 15 | overflow-y: auto; 16 | } 17 | header div h2 a.home-link{ 18 | text-align: center; 19 | } 20 | 21 | .box-login{ 22 | background-color: rgba(238, 216, 187, 0.849); 23 | color: white; 24 | display: flex; 25 | flex-direction: column; 26 | justify-content: center; 27 | position: relative; 28 | padding: 0px 20px 15px 20px; 29 | height: 550px; 30 | width: 450px; 31 | border: 3px solid rgba(245, 135, 62, 0.882); 32 | border-radius: 23px; 33 | -webkit-backdrop-filter: blur(15px); 34 | backdrop-filter: blur(-15px); 35 | overflow: hidden; 36 | } 37 | .top-header{ 38 | text-align: center; 39 | margin: 15px 0; 40 | /* padding-top: 15px; */ 41 | } 42 | .top-header h2{ 43 | color: brown; 44 | font-size: 29px; 45 | font-weight: 700; 46 | margin-bottom: 8px; 47 | margin-left: 18px; 48 | } 49 | .input-group{ 50 | width: 100%; 51 | } 52 | .input-field{ 53 | margin: 12px 0; 54 | position: relative; 55 | } 56 | .input-box{ 57 | /* width: 100%; */ 58 | margin-top: 8px; 59 | margin-left: 13px; 60 | height: 40px; 61 | width: 340px; 62 | font-size: 15px; 63 | color: black; 64 | border: 1px solid rgb(178, 95, 18); 65 | border-radius:10px; 66 | padding: 2px 15px 0 20px; 67 | background: rgba(244, 208, 154, 0.926); 68 | backdrop-filter: blur(2px); 69 | outline: none; 70 | } 71 | .input-field label{ 72 | color: brown; 73 | font-size: 18px; 74 | transition: .3s ease-in-out; 75 | 76 | } 77 | .remember{ 78 | display: flex; 79 | font-size: 16px; 80 | margin: 12px 0 20px 0; 81 | color: brown; 82 | } 83 | .check{ 84 | margin-right: 8px; 85 | width: 14px; 86 | } 87 | .Input-submit{ 88 | width: 100%; 89 | height: 50px; 90 | margin: 5px 0; 91 | font-size: 15px; 92 | font-weight: 500; 93 | border: none; 94 | border-radius: 10px; 95 | background: rgb(184, 98, 5); 96 | color: white; 97 | box-shadow: 0px 4px 20px rgba(169, 115, 61, 0.726); 98 | cursor: pointer; 99 | transition: .4s; 100 | } 101 | .Input-submit:hover{ 102 | background: wheat; 103 | color: brown; 104 | border: 2px solid brown; 105 | box-shadow: 0px 4px 20px rgba(117, 88, 54, 0.32); 106 | } 107 | .forgot{ 108 | text-align:end; 109 | font-size: 13px; 110 | padding: 23px; 111 | } 112 | .forgot a{ 113 | text-decoration: none; 114 | color: rgb(254, 252, 252); 115 | color: brown; 116 | } 117 | .new{ 118 | text-align:center; 119 | font-size: 16px; 120 | padding-top: 26px; 121 | padding-bottom: 0; 122 | font-weight: 500; 123 | color: brown; 124 | } 125 | .new a{ 126 | text-decoration: none; 127 | color: brown; 128 | font-weight: bolder; 129 | } -------------------------------------------------------------------------------- /site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /start_writing.js: -------------------------------------------------------------------------------- 1 | async function PostBlog(event) { 2 | event.preventDefault(); 3 | 4 | 5 | const form = document.getElementById("blogForm"); 6 | const formData = new FormData(form); 7 | 8 | try { 9 | 10 | const response = await fetch("http://localhost:3000/post_blog", { 11 | method: "POST", 12 | body: formData, 13 | }); 14 | 15 | const result = await response.json(); 16 | 17 | if (result.success) { 18 | 19 | let existingPopup = document.getElementById("popupMessage"); 20 | if (existingPopup) { 21 | existingPopup.remove(); 22 | } 23 | 24 | 25 | const popup = document.createElement("div"); 26 | popup.id = "popupMessage"; 27 | popup.innerText = "Blog submitted successfully!"; 28 | 29 | 30 | document.body.appendChild(popup); 31 | 32 | 33 | popup.style.display = "block"; 34 | 35 | setTimeout(() => { 36 | popup.style.display = "none"; 37 | document.getElementById("blogForm").reset(); 38 | }, 3000); 39 | } else { 40 | alert("Error: " + result.message); 41 | } 42 | } catch (error) { 43 | console.error("Error submitting blog:", error); 44 | alert("An error occurred while submitting the blog."); 45 | } 46 | } 47 | 48 | const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; 49 | if (SpeechRecognition) { 50 | const recognition = new SpeechRecognition(); 51 | recognition.continuous = true; 52 | recognition.interimResults = false; 53 | recognition.lang = 'en-US'; 54 | 55 | const blogContent = document.getElementById('blogContent'); 56 | const startVoiceInputButton = document.getElementById('start-voice-input'); 57 | 58 | let isListening = false; 59 | 60 | startVoiceInputButton.addEventListener('click', () => { 61 | if (isListening) { 62 | recognition.stop(); 63 | startVoiceInputButton.innerText = '🎤'; 64 | isListening = false; 65 | } else { 66 | recognition.start(); 67 | startVoiceInputButton.innerText = '🛑'; 68 | isListening = true; 69 | } 70 | }); 71 | 72 | recognition.onresult = (event) => { 73 | const transcript = event.results[event.resultIndex][0].transcript; 74 | blogContent.value += ' ' + transcript; 75 | }; 76 | 77 | recognition.onerror = (event) => { 78 | console.error("Speech recognition error:", event.error); 79 | alert("Error with voice input: " + event.error); 80 | }; 81 | } else { 82 | alert("Speech recognition is not supported by your browser."); 83 | } 84 | 85 | 86 | 87 | async function getWritingSuggestions() { 88 | const content = document.getElementById("blogContent").value; 89 | 90 | 91 | if (!content) { 92 | alert("Please enter some content to get suggestions."); 93 | return; 94 | } 95 | 96 | try { 97 | 98 | const response = await fetch('http://127.0.0.1:5000/grammar-correct', { 99 | method: 'POST', 100 | headers: { 101 | 'Content-Type': 'application/json', 102 | }, 103 | body: JSON.stringify({ text: content }) 104 | }); 105 | 106 | 107 | if (response.ok) { 108 | const result = await response.json(); 109 | const suggestions = result.corrected_text || "No suggestions available"; 110 | 111 | 112 | document.getElementById("suggestionsText").innerText = suggestions; 113 | document.getElementById("suggestionsContainer").classList.add('visible'); 114 | } else { 115 | console.error("Error fetching suggestions:", response.status); 116 | alert("Failed to fetch suggestions. Please try again later."); 117 | } 118 | } catch (error) { 119 | console.error("Error:", error); 120 | alert("An error occurred while fetching suggestions."); 121 | } 122 | } 123 | 124 | // Get the Back to Top button element 125 | const backToTopButton = document.getElementById('backToTop'); 126 | 127 | // Add scroll event to show/hide the button 128 | window.addEventListener('scroll', () => { 129 | if (window.scrollY > 300) { 130 | backToTopButton.classList.add('show'); 131 | } else { 132 | backToTopButton.classList.remove('show'); 133 | } 134 | }); 135 | 136 | // Add click event to scroll to the top smoothly 137 | backToTopButton.addEventListener('click', () => { 138 | window.scrollTo({ 139 | top: 0, 140 | behavior: 'smooth' 141 | }); 142 | }); 143 | 144 | 145 | document.getElementById("getSuggestions").addEventListener("click", getWritingSuggestions); 146 | 147 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* .form-popup { 2 | display: none; Initially hidden */ 3 | /* position: fixed; 4 | top: 50%; 5 | left: 50%; 6 | transform: translate(-50%, -50%); 7 | border: 1px solid #ccc; 8 | padding: 20px; 9 | background-color: white; 10 | box-shadow: 0 2px 10px rgba(0,0,0,0.1); 11 | z-index: 1000; 12 | } 13 | .form-container { 14 | width: 300px; 15 | } 16 | .btn { 17 | display: block; 18 | width: 100%; 19 | padding: 10px; 20 | margin: 10px 0; 21 | background-color: #007bff; 22 | color: white; 23 | border: none; 24 | cursor: pointer; 25 | } */ 26 | /* .btn.cancel { 27 | background-color: #dc3545; Red for cancel button */ 28 | /* } */ 29 | /* .toggle-button { 30 | cursor: pointer; 31 | margin-left: 5px; 32 | } 33 | .form-group { 34 | margin-bottom: 15px; 35 | } */ 36 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | const CACHE_NAME = 'my-pwa-cache-v1'; 2 | const urlsToCache = [ 3 | '/', 4 | '/index.html', 5 | '/styles.css', 6 | '/scripts.js', 7 | '/manifest.json', 8 | '/icon-192x192.png', 9 | '/icon-512x512.png' 10 | ]; 11 | 12 | 13 | self.addEventListener('install', (event) => { 14 | event.waitUntil( 15 | caches.open(CACHE_NAME) 16 | .then((cache) => { 17 | return cache.addAll(urlsToCache); 18 | }) 19 | ); 20 | }); 21 | 22 | 23 | self.addEventListener('fetch', (event) => { 24 | event.respondWith( 25 | caches.match(event.request) 26 | .then((response) => { 27 | return response || fetch(event.request); 28 | }) 29 | ); 30 | }); 31 | 32 | 33 | self.addEventListener('activate', (event) => { 34 | const cacheWhitelist = [CACHE_NAME]; 35 | event.waitUntil( 36 | caches.keys().then((cacheNames) => { 37 | return Promise.all( 38 | cacheNames.map((cacheName) => { 39 | if (!cacheWhitelist.includes(cacheName)) { 40 | return caches.delete(cacheName); 41 | } 42 | }) 43 | ); 44 | }) 45 | ); 46 | }); -------------------------------------------------------------------------------- /testp.js: -------------------------------------------------------------------------------- 1 | // Get current timestamp 2 | const getCurrentTimestamp = () => { 3 | return new Date().getTime(); 4 | }; 5 | 6 | // Store last active timestamp in LocalStorage 7 | const storeLastActive = () => { 8 | const lastActive = getCurrentTimestamp(); 9 | localStorage.setItem('lastActive', lastActive); 10 | }; 11 | 12 | // Get last active timestamp from LocalStorage 13 | const getLastActive = () => { 14 | return localStorage.getItem('lastActive'); 15 | }; 16 | 17 | // Update last active timestamp on page load and interaction 18 | document.addEventListener('DOMContentLoaded', storeLastActive); 19 | document.addEventListener('click', storeLastActive); 20 | document.addEventListener('scroll', storeLastActive); 21 | document.addEventListener('keydown', storeLastActive); 22 | 23 | // Example usage: 24 | const displayLastActive = () => { 25 | const lastActive = getLastActive(); 26 | if (lastActive) { 27 | const formattedTime = new Date(parseInt(lastActive)).toLocaleString(); 28 | document.getElementById('last-active').innerHTML = `Last active: ${formattedTime}`; 29 | } 30 | }; 31 | 32 | // Display the last active time on page load 33 | document.addEventListener('DOMContentLoaded', displayLastActive); 34 | --------------------------------------------------------------------------------