diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..509fe35 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,26 @@ +## Related Issue +[Cite any related issue(s) this pull request addresses. If none, simply state β€œNone”] + +## Description +[Please include a brief description of the changes or features added] + +## Type of PR + +- [ ] Bug fix +- [ ] Feature enhancement +- [ ] Documentation update +- [ ] Other (specify): _______________ + +## Screenshots / videos (if applicable) +[Attach any relevant screenshots or videos demonstrating the changes] + +## Checklist: +- [ ] I have performed a self-review of my code +- [ ] I have read and followed the Contribution Guidelines. +- [ ] I have tested the changes thoroughly before submitting this pull request. +- [ ] I have provided relevant issue numbers, screenshots, and videos after making the changes. +- [ ] I have commented my code, particularly in hard-to-understand areas. + + +## Additional context: +[Include any additional information or context that might be helpful for reviewers.] diff --git a/.github/workflows/auto-comment-pr-merge.yml b/.github/workflows/auto-comment-pr-merge.yml new file mode 100644 index 0000000..f07afe1 --- /dev/null +++ b/.github/workflows/auto-comment-pr-merge.yml @@ -0,0 +1,21 @@ +name: Auto Comment on PR Merge + +on: + pull_request: + types: [closed] + +jobs: + comment: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + + steps: + - name: Add Comment to Merged PR + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -d '{"body":"πŸŽ‰ Your pull request has been successfully merged! πŸŽ‰ Thank you for your contribution to our project. Your efforts are greatly appreciated. Keep up the fantastic work! πŸš€"}' \ + "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cee5c73..6041c6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,40 @@ in case you are stuck:
+### Alternatively, contribute using GitHub Desktop + +1. **Open GitHub Desktop:** + Launch GitHub Desktop and log in to your GitHub account if you haven't already. + +2. **Clone the Repository:** + - If you haven't cloned the Petari repository yet, you can do so by clicking on the "File" menu and selecting "Clone Repository." + - Choose the Petari repository from the list of repositories on GitHub and clone it to your local machine. + +3. **Switch to the Correct Branch:** + - Ensure you are on the branch that you want to submit a pull request for. + - If you need to switch branches, you can do so by clicking on the "Current Branch" dropdown menu and selecting the desired branch. + +4. **Make Changes:** + Make your changes to the code or files in the repository using your preferred code editor. + +5. **Commit Changes:** + - In GitHub Desktop, you'll see a list of the files you've changed. Check the box next to each file you want to include in the commit. + - Enter a summary and description for your changes in the "Summary" and "Description" fields, respectively. Click the "Commit to " button to commit your changes to the local branch. + +6. **Push Changes to GitHub:** + After committing your changes, click the "Push origin" button in the top right corner of GitHub Desktop to push your changes to your forked repository on GitHub. + +7. **Create a Pull Request:** + - Go to the GitHub website and navigate to your fork of the Petari repository. + - You should see a button to "Compare & pull request" between your fork and the original repository. Click on it. + +8. **Review and Submit:** + - On the pull request page, review your changes and add any additional information, such as a title and description, that you want to include with your pull request. + - Once you're satisfied, click the "Create pull request" button to submit your pull request. + +9. **Wait for Review:** + Your pull request will now be available for review by the project maintainers. They may provide feedback or ask for changes before merging your pull request into the main branch of the Petari repository. + ## **Issue Report Process πŸ“Œ** 1. Go to the project's issues. diff --git a/README.md b/README.md index 511e66f..b3670ae 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,68 @@ -# PETARI Documentation +#

Petari - The Food Donation Management System

+
+

Table of Contents

+ +- [Introduction](#introduction) +- [Tech Stack](#tech-stack) +
+ - [Frontend](#frontend)
+ - [Backend](#backend) +- [Features](#features) +- [Contribution Guidelines](#contribution-guidelines) + - [How Can You Contribute?](#how-can-you-contribute) +- [Getting Started](#getting-started) +- [Code Style Guidelines](#code-style-guidelines) +- [Code Review Process](#code-review-process) +- [Community Guidelines](#community-guidelines) +- [Output Screenshot](#output-screenshot) +
## Introduction - + PETARI, The Food Donate WebApp, is an initiative by an organization aiming to redistribute excess food from various events to those in need. The project aligns with India's Sustainable Development Goals to achieve a hunger-free world and zero food waste. By leveraging technology, PETARI seeks to bridge the gap between surplus food and hunger, thereby benefiting society and the environment. ## Tech Stack - + ### Frontend -- HTML -- CSS -- Bootstrap -- JavaScript + + +

+ HTML + CSS + JS + Bootstrap + +

### Backend -- Node.js -- EJS -- Express.js -- MongoDB -- Authentication (Auth 2.0) + +Node.js +expressjs +MongoDB +EJS +Auth2.0 -# Contribution Guidelines +

(back to top)

+# Features + +-

Redistribution of Excess Food:

+ Petari focuses on collecting excess food from weddings, parties, and events to redistribute it to individuals who are hungry, thereby reducing food waste and ensuring that surplus food benefits those in need. +-

Societal Impact:

The organization aims to bring benefits across society by providing access to food for individuals who may not have adequate means to access it, addressing food insecurity and hunger issues. +-

Alignment with Sustainable Development Goals:

Petari's mission aligns with India's Sustainable Development Goals, particularly focusing on making the world hunger-free and reducing food waste to contribute to a sustainable and equitable society. +-

Environmental Consciousness:

By redistributing excess food and reducing food waste, Petari helps alleviate the pressure on finite natural resources and minimizes the environmental impact associated with food wastage. +-

Economic Impact:

Through its activities, Petari not only addresses social issues related to hunger but also contributes to economic sustainability by efficiently utilizing excess resources and reducing wastage. +-

Collaboration with Events:

Petari likely collaborates with various events, such as weddings and parties, to collect surplus food, emphasizing the importance of partnerships and community involvement in achieving its goals. + +

(back to top)

+ + +# Contribution Guidelines + Thank you for considering contributing to PETARI! We appreciate your efforts to make a positive impact on society through food redistribution and combating hunger. By contributing to PETARI, you are helping us move closer to our goal of creating a hunger-free world and reducing food waste. ## How Can You Contribute? - + There are several ways you can contribute to PETARI: 1. **Code Contributions**: Help improve the PETARI codebase by fixing bugs, adding new features, or enhancing existing functionalities. @@ -37,8 +75,10 @@ There are several ways you can contribute to PETARI: 5. **Feedback**: Provide feedback on the existing features, suggest new ideas, or share your thoughts on how PETARI can be further improved. -## Getting Started +

(back to top)

+## Getting Started + If you're new to contributing to open-source projects, don't worry! Here's how you can get started: 1. **Fork the Repository**: Fork the PETARI repository to your GitHub account. @@ -164,9 +204,11 @@ If you're new to contributing to open-source projects, don't worry! Here's how y 15. **Create a Pull Request**: Open a pull request from your forked repository to the main PETARI repository. Provide a clear description of your changes in the pull request. Follow these steps - Add the issue number, that you have been assigned[Formate:- Isuue number #(your issue number)] - Brief description of the changes + +

(back to top)

## Code Style Guidelines - + To maintain consistency and readability, please follow these code style guidelines: - Use meaningful variable and function names. @@ -174,16 +216,19 @@ To maintain consistency and readability, please follow these code style guidelin - Write clear and concise comments to explain complex logic or algorithms. ## Code Review Process - + All contributions to PETARI will go through a code review process to ensure code quality, consistency, and adherence to project standards. Your contributions are valuable, and we appreciate your patience during the review process. ## Community Guidelines + PETARI is committed to fostering an inclusive and welcoming community. We encourage respectful and constructive communication among contributors. Please refer to our [Code of Conduct]-(https://github.com/Sahil1786/Petari/blob/dffa12d5f33b3227ec287af602762ef1f7bc3f89/Code_of_conduct.md) for more information. +

(back to top)

-## Output Screenshot +## Output Screenshot + ### 1. Index Page The index page serves as the main landing page for PETARI. It showcases the mission and purpose of the organization, along with relevant information about its activities. @@ -205,3 +250,5 @@ Admins have special privileges and access to manage the platform. The admin logi ![Admin Login Page](https://github.com/Sahil1786/Petari/assets/111786720/fb1cc46a-e3ad-40fd-93c6-268849e05e91) **Please follow me and give a star to my repository +

(back to top)

+ diff --git a/app.js b/app.js index e4331e0..70e2d1b 100644 --- a/app.js +++ b/app.js @@ -60,4 +60,7 @@ app.listen( process.env.port|| 3000,function(){ console.log("server is running on port 3000 "); }); - \ No newline at end of file +// Route for policies.ejs +app.get('/policies', (req, res) => { + res.render('policies'); // Ensure policies.ejs is in the views directory +}); \ No newline at end of file diff --git a/model/query.js b/model/query.js index 9fa0ab9..1d80237 100644 --- a/model/query.js +++ b/model/query.js @@ -7,6 +7,12 @@ const querySchema = new mongoose.Schema( subject: String, message: String, approved: { type: Boolean, default: false }, + // adding models for storing ANSWERE ans USER_ID + answere: String, + user_id: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + }, }, { timestamps: true } ); diff --git a/model/user.js b/model/user.js index 01c6015..9f12416 100644 --- a/model/user.js +++ b/model/user.js @@ -31,6 +31,8 @@ const userSchema = new mongoose.Schema({ approved: { type: Boolean, default: false }, // googleId: String, // profile: String, + resetTokenExpiration: Date, + resetToken:String }); const User = new mongoose.model("User", userSchema); diff --git a/public/css/admin_css.css b/public/css/admin_css.css index a37489a..4ad6d37 100644 --- a/public/css/admin_css.css +++ b/public/css/admin_css.css @@ -5,6 +5,46 @@ body{ background: #ececec; } +/* Dark Mode */ + +.dark-mode { + background-color: #121212; + color: #e0e0e0; +} + +.dark-mode #darkModeToggle { + background-color: #333; /* Dark background color */ +} + +.dark-mode #darkModeIcon { + color: #e0e0e0; /* Dark mode icon color */ +} + +/* Dark Mode Toggle Button - Hover */ +#darkModeToggle:hover { + background-color: #555; /* Dark background color on hover */ +} + +/* Dark Mode Toggle Button */ +#darkModeToggle { + width: 50px; + height: 50px; + border: none; /* Remove border */ + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; /* Ensure it's above other content */ +} + +#darkModeIcon { + font-size: 24px; +} + +.dark-mode .custom-box { + background-color: #333 !important; /* Dark mode background color for the login container */ + color: #e0e0e0; /* Dark mode text color for the login container */ +} + /*------------ Login container ------------*/ .box-area{ diff --git a/public/css/policies.css b/public/css/policies.css new file mode 100644 index 0000000..7982c1b --- /dev/null +++ b/public/css/policies.css @@ -0,0 +1,24 @@ +.container{ + margin: 40px; + font-family: 'Poppins', sans-serif; + font-size: 18px; +} + +.section{ + padding: 15px; +} + +.heading{ + font-size: 25px; + text-decoration: underline; + color: rgb(4, 101, 10); + +} + +.content{ + padding: 15px; +} + +hr#line{ + border: 1px solid rgb(4, 101, 10); +} \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index dfc6e44..dd753ab 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -29,29 +29,11 @@ body{ } body { background-color:#ECF2FF; - } - header{ - background-color: #24252A; - } - -.max-width{ - max-width: 1300px; - padding: 0 80px; - margin: auto; -} -.about{ - font-family: 'Poppins', sans-serif; -} -.about .about-content{ - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; -} + } /* landing section */ .hero{ - height: 90vh; + height: 100vh; display: flex; align-items: center; } @@ -97,11 +79,51 @@ body{ transition: all 0.3s ease 0s; margin-bottom: 40px; } -/* */ +/* about section */ +.about{ + font-family: 'Poppins', sans-serif; + display: flex; + padding: 20px; +} +.hero2_img{ + width: 500px; + height: 500px; + border-radius: 50%; + margin-left: 80px; +} +.about-txt{ + display: flex; + flex-wrap: wrap; + margin-left: 50px; + margin-top: 70px; +} +#more {display: none;} +#myBtn {cursor: pointer;} +/* contact section */ +.contact{ + font-family: 'Poppins', sans-serif; + display: flex; + padding: 20px; + align-items: center; + gap: 500px; + justify-content: center; +} +.contact1{ + display: block; + margin-top: 70px; + font-size:medium; +} +.contact2{ + margin-top: 70px; + width: 400px; +} +.address-info{ + display: block; +} /* all similar content styling codes */ -section{ +/* section{ padding: 100px 0; } .max-width{ @@ -139,7 +161,7 @@ section .title{ background: #111; transform: translateX(-50%); } */ -section .title::after{ +/* section .title::after{ position: absolute; bottom: -8px; left: 50%; @@ -149,14 +171,11 @@ section .title::after{ background: #fff; transform: translateX(-50%); } - + */ /* about section styling */ -.about .title::after{ - content: "Who We are"; -} -.about .about-content .left{ +/* .about .about-content .left{ width: 45%; } .about .about-content .left img{ @@ -194,11 +213,11 @@ section .title::after{ .about .about-content .right a:hover{ color: crimson; background: none; -} +} */ /* contact section styling */ -.contact .title::after{ +/* .contact .title::after{ content: "Get In Touch"; } .contact .contact-content .column{ @@ -290,7 +309,7 @@ section .title::after{ background: crimson; border: 2px solid crimson; transition: all 0.3s ease; -} +} */ @@ -298,7 +317,7 @@ section .title::after{ /* footer section styling */ footer{ - background: #111; + background: #353535; padding: 15px 23px; color: #fff; text-align: center; @@ -328,4 +347,127 @@ body{ #contact { margin-bottom: 50px; /* Adjust the value as needed */ -} \ No newline at end of file +} + +body { + transition: background-color 0.3s, color 0.3s; +} + +.dark-mode { + background-color: #121212; + color: #e0e0e0; +} + +.navbar.bg-body-secondary { + background-color: var(--bs-body-bg); +} + +.dark-mode .navbar.bg-body-secondary { + background-color: #1c1c1c; +} + +.dark-mode .hero_text, +.dark-mode .hero_text2, +.dark-mode .hero_text3 { + color: #e0e0e0; +} + +/* Dark mode styles */ +.dark-mode .navbar { + background-color: #333 !important; +} + +.dark-mode .navbar .navbar-brand, +.dark-mode .navbar .nav-link, +.dark-mode .navbar .btn { + color: #e0e0e0; +} + +.dark-mode .navbar-toggler { + border-color: #e0e0e0; +} + +.dark-mode .navbar-toggler-icon { + filter: invert(1); +} + +.dark-mode .contact .text { + color: #e0e0e0; +} + +.dark-mode .title { + color: #e0e0e0; +} + +.dark-mode .icons .info .head, +.dark-mode .icons .info .sub-title { + color: #e0e0e0; +} +@media (min-width: 768px) { + #myBtn { display: none; } + #dots { display: none; } + #more { display: inline; } +} +@media (max-width: 768px) { + .hero{ + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + margin-top: 120px; + } + .hero_img{ + width: 300px; + height: 300px; + margin-left: 0px; + } + .left{ + display: none; + } + .hero_text{ + margin-left: auto; + } + .hero_text2{ + margin-left: auto; + } + .hero_text3{ + margin-left: auto; + } + .hero_btn{ + margin-left: auto; + } + /* about section */ + .abt-txt{ + margin-top: 20px; + } + .about{ + flex-direction: column-reverse; + text-align: center; + align-items: center; + justify-content: center; + } + .hero2_img{ + width: 280px; + height: 280px; + margin-left: 0px; + } + .about-txt{ + margin-left: auto; + font-size:17px; + margin-top: 0px; + } + /* contact section */ + .contact{ + flex-direction: column; + text-align: center; + align-items: center; + justify-content: center; + gap: 10px; + } + .con-txt{ + margin-top: 20px; + } + .contact2{ + width: 350px; + } +} diff --git a/routers/NgoRoutes.js b/routers/NgoRoutes.js index 8f7d274..61706c9 100644 --- a/routers/NgoRoutes.js +++ b/routers/NgoRoutes.js @@ -1,8 +1,8 @@ const express = require("express"); const router = new express.Router(); const fs = require("fs"); -const path = require('path'); -const Handlebars = require('handlebars'); +const path = require("path"); +const Handlebars = require("handlebars"); const bcrypt = require("bcrypt"); const saltRounds = 10; @@ -19,19 +19,29 @@ router.post("/NGO-login", async (req, res) => { const username = req.body.username; const password = req.body.password; const ngo = await NGO.findOne({ username: username, password: password }); + try { - const dooner = await User.find(); // Assuming User is your Mongoose model for users - - res.render("NGO-Dashboard", { - fullName: ngo.NGOName, - email: ngo.username, - id: ngo.NGOID, - phoneNo: ngo.Mobile, - address: ngo.NGOLocation, - Donation: dooner, - Pickup: dooner, - complain: "", - }); + if (ngo) { + //checking NGO is APPROVED or NOT + if (ngo.approved == false) { + res.status(500).json({ messaage: "NGO is send for approval" }); + } + + const dooner = await User.find(); // Assuming User is your Mongoose model for users + + res.render("NGO-Dashboard", { + fullName: ngo.NGOName, + email: ngo.username, + id: ngo.NGOID, + phoneNo: ngo.Mobile, + address: ngo.NGOLocation, + Donation: dooner, + Pickup: dooner, + complain: "", + }); + } else { + res.status(404).json({ message: "NGO is not registered" }); + } } catch (err) { console.error(err); res.status(500).send("An internal server error occurred."); @@ -61,24 +71,28 @@ router.post("/NGO-Registarion", async (req, res) => { // Save the new NGO to the database await newNGO.save(); - const templatePath = path.join(__dirname, '../views', 'Email.template.handlebars'); - const templateContent = fs.readFileSync(templatePath, 'utf8'); - -// Compile the Handlebars template with the provided context data -const compiledHtml = Handlebars.compile(templateContent)({ - user: { - _id: newNGO.NgoID, // Example ID - username: newNGO.NGOName, // Example username - email: newNGO.username, // Example email - fname: newNGO.NGOName // Example first name - } - }); + const templatePath = path.join( + __dirname, + "../views", + "Email.template.handlebars" + ); + const templateContent = fs.readFileSync(templatePath, "utf8"); + + // Compile the Handlebars template with the provided context data + const compiledHtml = Handlebars.compile(templateContent)({ + user: { + _id: newNGO.NgoID, // Example ID + username: newNGO.NGOName, // Example username + email: newNGO.username, // Example email + fname: newNGO.NGOName, // Example first name + }, + }); // Send an email to the admin for approval const mailOptions = { to: newNGO.username, // Admin's email address subject: "New NGO Registration", text: "A new NGO registration is pending approval. Login to the admin panel to review and approve.", - html: compiledHtml + html: compiledHtml, // Include any necessary information in the email body }; transporter.transporter.sendMail(mailOptions, function (error, info) { diff --git a/routers/adminRoutes.js b/routers/adminRoutes.js index 150352a..1e6061e 100644 --- a/routers/adminRoutes.js +++ b/routers/adminRoutes.js @@ -54,8 +54,8 @@ router.post("/admin-login", async (req, res) => { const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); - + //return UNRESOLVED query + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.email, @@ -141,7 +141,8 @@ router.post("/approve-ngo/:id/:userId", async (req, res) => { const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); + //return UNRESOLVED query + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.fullName, @@ -178,7 +179,8 @@ router.post("/decline-ngo/:id/:userId", async (req, res) => { const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); + //return UNRESOLVED query + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.fullName, @@ -215,7 +217,8 @@ router.post("/decline-donor/:id/:userId", async (req, res) => { const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); + //return UNRESOLVED query + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.fullName, @@ -253,7 +256,8 @@ router.post("/accept-donor/:id/:userId", async (req, res) => { const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); + //return UNRESOLVED query + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.fullName, @@ -289,7 +293,8 @@ router.post("/delete-complain/:id/:userId", async (req, res) => { const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); + //return UNRESOLVED query + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.fullName, @@ -325,7 +330,8 @@ router.post("/accept-complain/:id/:userId", async (req, res) => { const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); + //return UNRESOLVED query + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.fullName, @@ -343,71 +349,78 @@ router.post("/accept-complain/:id/:userId", async (req, res) => { }); //making a POST method for RESPONDE to email -router.post("/complains-response/:email/:userId", async (req, res) => { +router.post("/complains-response/:email/:userId/:id", async (req, res) => { const email = req.params.email; const answere = req.body.answere; const userId = req.params.userId; - console.log(email, answere, userId); + const compId = req.params.id; - //sending email to the COMPLAIN email try { - const transporter = nodemailer.createTransport({ - service: "gmail", - host: "smtp.gmail.com", - port: 465, - secure: true, - auth: { - user: process.env.mail_id, - pass: process.env.pass_id, - }, - }); - - let MailGenerator = new Mailgen({ - theme: "default", - product: { - name: "PETARI", - link: "https://mailgen.js", - }, - }); - - let response = { - body: { - name: "", - intro: "Welcome to PETARI! We're very excited to have you on board.", - action: { - instructions: answere, - button: { - color: "#22BC66", // Optional action button color - text: "Have a good day", - link: "", + //checking user EXIST or NOT + const userExist = await User.findOne({ email }); + if (userExist) { + const complaint = await Query.findByIdAndUpdate(compId, { + answere, + }); + } else { + //sending email to the COMPLAIN email + const transporter = nodemailer.createTransport({ + service: "gmail", + host: "smtp.gmail.com", + port: 465, + secure: true, + auth: { + user: process.env.mail_id, + pass: process.env.pass_id, + }, + }); + let MailGenerator = new Mailgen({ + theme: "default", + product: { + name: "PETARI", + link: "https://mailgen.js", + }, + }); + let response = { + body: { + name: "", + intro: "Welcome to PETARI! We're very excited to have you on board.", + action: { + instructions: answere, + button: { + color: "#22BC66", + text: "Have a good day", + link: "", + }, }, + outro: "Thankyou for a part of PETARI", }, - outro: "Thankyou for a part of PETARI", - }, - }; - - let mail = MailGenerator.generate(response); - - let message = { - to: email, - subject: "Petari Support team", - html: mail, - }; + }; + let mail = MailGenerator.generate(response); + let message = { + to: email, + subject: "Petari Support team", + html: mail, + }; + transporter + .sendMail(message) + .then(() => { + console.log("email is send successfully"); + }) + .catch((error) => { + console.log("Email is not send", error); + }); + + //DELETING complain after response because user does not exist + await Query.findByIdAndDelete(compId); + } - transporter - .sendMail(message) - .then(() => { - console.log("email is send successfully"); - }) - .catch((error) => { - console.log("Email is not send", error); - }); const admin = await Admin.findById(userId); console.log("admin details in approve-ngo", admin); const dooner = await User.find(); // Assuming User is your Mongoose model for users const ngo = await NGO.find(); - const query1 = await problem.find(); + const query1 = await problem.find({ answere: { $exists: false } }); res.render("Admin_Dashboard", { name: admin.fullName, email: admin.fullName, diff --git a/routers/userRoutes.js b/routers/userRoutes.js index 673704c..1463311 100644 --- a/routers/userRoutes.js +++ b/routers/userRoutes.js @@ -1,9 +1,11 @@ const express = require("express"); const router = new express.Router(); +const path = require("path"); const bcrypt = require("bcrypt"); const saltRounds = 10; const jwt = require("jsonwebtoken"); +const nodemailer = require("nodemailer"); const User = require("../model/user"); const Query = require("../model/query"); // Adjust the path based on your project structure @@ -36,18 +38,34 @@ router.get("/success", function (req, res) { }); router.post("/", async function (req, res) { + const email = req.body.Email; try { - const newQuery = new Query({ - name: req.body.Fname, - email: req.body.Email, - subject: req.body.sub, - message: req.body.sms, - }); + //finding the user EXIST or NOT in DATABASE + const userExist = await User.findOne({ email }); + if (userExist) { + const newQuery = new Query({ + name: req.body.Fname, + email: req.body.Email, + subject: req.body.sub, + message: req.body.sms, + user_id: userExist._id, + }); + + await newQuery.save(); + console.log(newQuery); + } else { + const newQuery = new Query({ + name: req.body.Fname, + email: req.body.Email, + subject: req.body.sub, + message: req.body.sms, + }); - await newQuery.save(); + await newQuery.save(); + console.log(newQuery); + } res.status(200).send("Successfully Received Message... Thank You!"); - console.log(newQuery); } catch (error) { console.error(error); res.status(500).send("Internal Server Error"); @@ -67,12 +85,14 @@ router.post("/login", async function (req, res) { const result = await bcrypt.compare(password, foundUser.password); + const userQuerys = await Query.find({ user_id: foundUser._id }); if (result) { return res.render("UserDashBoard", { fullName: foundUser.fullName, email: foundUser.email, phoneNo: foundUser.Mobile, address: foundUser.address, + complain: userQuerys, }); } else { return res.render("loginError", { message: "Incorrect password" }); @@ -83,83 +103,6 @@ router.post("/login", async function (req, res) { } }); -router.route("/forgot-password").get(async (req, res) => { - res.render("forget-password"); -}); - -router.route("/reset-password").get(async (req, res) => { - const { email, token } = req.query; - - try { - const user = await User.findOne({ - email, - resetToken: token, - resetTokenExpiration: { $gt: Date.now() }, - }); - - if (!user) { - return res.status(400).send("Invalid or expired reset token"); - } - - // Verify the token - try { - const decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET); - // Process the decoded token (e.g., extract information from it) - console.log(decodedToken); - // Continue with the reset-password logic - res.render("reset-password", { email, token }); - } catch (error) { - // Handle JWT verification errors - console.error("JWT verification error:", error.message); - // You might want to send an error response or redirect the user - res.status(401).send("Unauthorized"); - } - } catch (error) { - console.error(error); - res.status(500).send("Internal Server Error"); - } -}); - -router.post(async (req, res) => { - const { email, token, newPassword } = req.body; - - try { - // Verify the token again - const user = await User.findOne({ - email, - resetToken: token, - resetTokenExpiration: { $gt: Date.now() }, - }); - - if (!user) { - return res.status(400).send("Invalid or expired reset token"); - } - - try { - const decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET); - // Process the decoded token (e.g., extract information from it) - console.log(decodedToken); - - // Update the user's password and reset the resetToken fields - const hash = await bcrypt.hash(newPassword, saltRounds); - user.password = hash; - user.resetToken = null; - user.resetTokenExpiration = null; - await user.save(); - - res.redirect("/login"); // Redirect to login page or any other desired page - } catch (error) { - // Handle JWT verification errors - console.error("JWT verification error:", error.message); - // You might want to send an error response or redirect the user - res.status(401).send("Unauthorized"); - } - } catch (error) { - console.error(error); - res.status(500).send("Internal Server Error"); - } -}); - // extra details added for the user router.post("/add-details", async (req, res) => { try { @@ -284,6 +227,7 @@ router.get("/logout", function (req, res) { // user Registration router.post("/User_singUp", async function (req, res) { const { username } = req.body; + const fullName = req.body.fname; try { // Check if the email already exists @@ -310,43 +254,63 @@ router.post("/User_singUp", async function (req, res) { password: hash, }); + // creating a message for USER + const message = `Thank you, ${(fullName).toUpperCase()}, for connecting with the PETARI organization.` + await newUser.save().then((user) => { let mailOptions = { to: user.email, subject: "Welcome To Petari", template: "Email.template", - context: { - user: { - fname: user.fullName, - _id: user._id, - username: user.fullName, - email: user.email, - }, - year: new Date().getFullYear(), - }, + // context: { + // user: { + // fname: user.fullName, + // _id: user._id, + // username: user.fullName, + // email: user.email, + // }, + // year: new Date().getFullYear(), + // }, + text: message, attachments: [ { filename: "logo.png", - path: path.join(__dirname, "public", "img", "logo.png"), + path: path.join(__dirname, "..", "public", "img", "logo.png"), cid: "logo", }, ], }; + + const transporter = nodemailer.createTransport({ + service: "gmail", + host: "smtp.gmail.com", + port: 465, + secure: true, + auth: { + user: process.env.mail_id, + pass: process.env.pass_id, + }, + }); + transporter.sendMail(mailOptions, function (error, info) { if (error) { console.log(error); } else { - console.log("Email sent: " + info.response); + console.log("Email sent successfully: " + info.response); } }); }); + const foundUser = await User.findOne({ email: username }); + + const userQuerys = await Query.find({ user_id: foundUser._id }); + return res.render("UserDashBoard", { - fullName: req.body.fname, - email: username, - phoneNo: req.body.phn, - address: req.body.address, - // Do not include 'profile' here unless it's relevant to registration + fullName: foundUser.fullName, + email: foundUser.email, + phoneNo: foundUser.Mobile, + address: foundUser.address, + complain: userQuerys, }); } catch (error) { console.error("Error during user registration:", error); @@ -354,7 +318,12 @@ router.post("/User_singUp", async function (req, res) { } }); -router.post(async (req, res) => { +router.route("/forgot-password").get(async (req, res) => { + res.render("forget-password"); +}); + +//send Email for the reset password +router.route("/forgot-password").post(async (req, res) => { const { email } = req.body; try { const user = await User.findOne({ email }); @@ -370,6 +339,9 @@ router.post(async (req, res) => { ); user.resetTokenExpiration = Date.now() + 300000; // 5 minutes + user.resetToken = resetToken; + + console.log("use after setting ", user); await user.save(); // Send the reset link to the user via email @@ -393,7 +365,7 @@ router.post(async (req, res) => { attachments: [ { filename: "logo.png", - path: path.join(__dirname, "public", "img", "logo.png"), + path: path.join("public", "img", "logo.png"), cid: "logo", }, ], @@ -414,4 +386,111 @@ router.post(async (req, res) => { } }); +// verify Email and render reset password page +router.route("/reset-password").get(async (req, res) => { + const { email, token } = req.query; + try { + const user = await User.findOne({ + email, + resetToken: token, + resetTokenExpiration: { $gt: Date.now() }, + }); + + if (!user) { + return res.status(400).send("Invalid or expired reset token"); + } + + // Verify the token + try { + const decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET); + // Process the decoded token (e.g., extract information from it) + console.log(decodedToken); + // Continue with the reset-password logic + res.render("set_password", { email, token }); + } catch (error) { + // Handle JWT verification errors + console.error("JWT verification error:", error.message); + // You might want to send an error response or redirect the user + res.status(401).send("Unauthorized"); + } + } catch (error) { + console.error(error); + res.status(500).send("Internal Server Error"); + } +}); + +//verify the password +router.route("/reset-password").post(async (req, res) => { + const { email, token } = req.query; + const { newPassword } = req.body; + // console.log(" User Info",email,token,newPassword); + + try { + // Verify the token again + const user = await User.findOne({ + email, + resetToken: token, + resetTokenExpiration: { $gt: Date.now() }, + }); + + if (!user) { + return res.status(400).send("Invalid or expired reset token"); + } + + try { + const decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET); + // Process the decoded token (e.g., extract information from it) + console.log(decodedToken); + + // Update the user's password and reset the resetToken fields + const hash = await bcrypt.hash(newPassword, saltRounds); + user.password = hash; + user.resetToken = null; + user.resetTokenExpiration = null; + await user.save(); + + return res.render("UserDashBoard", { + fullName: user.fullName, + email: user.email, + phoneNo: user.Mobile, + address: user.address, + }); + + // Redirect to login page or any other desired page + } catch (error) { + // Handle JWT verification errors + console.error("JWT verification error:", error.message); + // You might want to send an error response or redirect the user + res.status(401).send("Unauthorized"); + } + } catch (error) { + console.error(error); + res.status(500).send("Internal Server Error"); + } +}); + +//making a POST method for DELETING the COMPLAINt +router.post("/delete-query/:id/:email", async (req, res) => { + const compId = req.params.id; + const email = req.params.email; + try { + const dele = await Query.findByIdAndDelete(compId); + + //rendering USER DASHBOARD + const foundUser = await User.findOne({ email }); + const userQuerys = await Query.find({ user_id: foundUser._id }); + + return res.render("UserDashBoard", { + fullName: foundUser.fullName, + email: foundUser.email, + phoneNo: foundUser.Mobile, + address: foundUser.address, + complain: userQuerys, + }); + } catch (error) { + console.error(error); + res.status(500).send("Internal Server Error"); + } +}); + module.exports = router; diff --git a/views/Admin_Dashboard.ejs b/views/Admin_Dashboard.ejs index 48b34ae..12ca23c 100644 --- a/views/Admin_Dashboard.ejs +++ b/views/Admin_Dashboard.ejs @@ -670,7 +670,8 @@

-
@@ -678,11 +679,6 @@
-
-
- -
-
<% } %> <% }); %> @@ -809,8 +805,10 @@ const textarea = document.getElementById('myTextarea'); textarea.addEventListener('input', function () { - this.style.height = 'auto'; - this.style.height = (this.scrollHeight) + 'px'; + if (this.style.height < "65px") { + this.style.height = 'auto'; + this.style.height = (this.scrollHeight) + 'px'; + } }); diff --git a/views/NGO-Registration.ejs b/views/NGO-Registration.ejs index d1ac6f3..3567b09 100644 --- a/views/NGO-Registration.ejs +++ b/views/NGO-Registration.ejs @@ -4,6 +4,7 @@ + @@ -88,12 +89,15 @@ - + +
NGO Registration Form


-
+
@@ -125,6 +129,31 @@
+ + diff --git a/views/Ngo_login.ejs b/views/Ngo_login.ejs index 3a2bec7..fd343f9 100644 --- a/views/Ngo_login.ejs +++ b/views/Ngo_login.ejs @@ -6,12 +6,17 @@ + NGO Login + + @@ -19,7 +24,7 @@ -
+
@@ -74,6 +79,29 @@
+ \ No newline at end of file diff --git a/views/UserDashBoard.ejs b/views/UserDashBoard.ejs index 88d4b38..3e9779a 100644 --- a/views/UserDashBoard.ejs +++ b/views/UserDashBoard.ejs @@ -62,7 +62,7 @@ } .top-border { - + border-style: double; border-color: #F07070; } @@ -112,76 +112,94 @@ cursor: pointer; z-index: 2; } - .dash{ + + .dash { position: absolute; left: 35px; } + body.dark .top { background-color: #333; color: #FFF; } - .donation{ + + .donation { display: none; } .container { - display: flex; - flex-direction: column; - align-items: center; - padding: 20px; - } - .form-container { - border: 1px solid #ddd; - padding: 20px; - width: 400px; - margin-bottom: 20px; - } - label { - display: block; - margin-bottom: 8px; - } - input[type="text"], input[type="number"], input[type="email"], select { - width: 100%; - padding: 8px; - border: 1px solid #ccc; - border-radius: 4px; - box-sizing: border-box; - margin-bottom: 10px; - } - select { - margin-bottom: 10px; - } - table { - border-collapse: collapse; - width: 100%; - margin-bottom: 20px; - font-family: Arial, sans-serif; - } - th, td { - padding: 8px; - text-align: left; - border-bottom: 1px solid #ddd; - } - - th:last-child, td:last-child { - text-align: center; - } - .button { - background-color: #4CAF50; /* Green */ - border: none; - color: white; - padding: 8px 16px; - text-align: center; - text-decoration: none; - display: inline-block; - font-size: 16px; - margin: 4px 2px; - cursor: pointer; - border-radius: 7%; - } - .button:hover { - background-color: #3e8e41; /* Green hover */ - } + display: flex; + flex-direction: column; + align-items: center; + padding: 20px; + } + + .form-container { + border: 1px solid #ddd; + padding: 20px; + width: 400px; + margin-bottom: 20px; + } + + label { + display: block; + margin-bottom: 8px; + } + + input[type="text"], + input[type="number"], + input[type="email"], + select { + width: 100%; + padding: 8px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + margin-bottom: 10px; + } + + select { + margin-bottom: 10px; + } + + table { + border-collapse: collapse; + width: 100%; + margin-bottom: 20px; + font-family: Arial, sans-serif; + } + + th, + td { + padding: 8px; + text-align: left; + border-bottom: 1px solid #ddd; + } + + th:last-child, + td:last-child { + text-align: center; + } + + .button { + background-color: #4CAF50; + /* Green */ + border: none; + color: white; + padding: 8px 16px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + cursor: pointer; + border-radius: 7%; + } + + .button:hover { + background-color: #3e8e41; + /* Green hover */ + } /* aditya End */ /* colors */ @@ -326,6 +344,56 @@ background-color: #333; color: #FFF; } + + .query-container { + display: flex; + flex-direction: column; + align-items: center; + margin: 2rem; + } + + .query-list { + list-style-type: none; + padding: 0; + margin: 0; + } + + .query-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + background-color: #f9f9f9; + border-radius: 8px; + padding: 1rem; + margin-bottom: 1rem; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .query-details { + display: flex; + flex-direction: column; + } + + .query-ques { + font-weight: bold; + margin-bottom: 0.5rem; + } + + .query-ans { + color: #666; + margin-bottom: 0.5rem; + } + + .btn-decline { + padding: 5px 10px; + border: none; + border-radius: 3px; + background-color: #4CAF50; + color: white; + cursor: pointer; + margin-right: 5px; + } @@ -344,17 +412,21 @@
DashBoard
Welcome, <%= fullName %>!
- +
-

Full Name: <%= fullName %>

-

Email: <%= email %>

-

Phone No: <%= phoneNo %>

-

Address: <%= address %>

+

Full Name: <%= fullName %> +

+

Email: <%= email %> +

+

Phone No: <%= phoneNo %> +

+

Address: <%= address %> +

@@ -365,20 +437,28 @@

Donation Form


- - - - + + + + + + + + + + + + + + + - - + + +

Food Inventory

- + @@ -435,106 +519,210 @@ - + + + + - - - - + + + + + const body = document.querySelector("body"); + const modeToggle = body.querySelector(".mode-toggle"); + + modeToggle.addEventListener("click", () => { + body.classList.toggle("dark"); + }); + function addRow() { + const tableBody = document.querySelector('#donationForm tbody'); + const newRow = tableBody.insertRow(); + const foodItemCell = newRow.insertCell(); + const quantityCell = newRow.insertCell(); + + foodItemCell.innerHTML = ``; + quantityCell.innerHTML = ``; + } + + function deleteRow() { + const tableBody = document.querySelector('#donationForm tbody'); + if (tableBody.rows.length > 1) { + tableBody.deleteRow(tableBody.rows.length - 1); + } else { + alert("Cannot delete the only row"); + } + } + + function validateForm() { + var isValid = true; + + // Validate Email + var email = document.getElementById("userId").value; + var emailError = document.getElementById("emailError"); + if (!email.match(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/)) { + emailError.textContent = "Please enter a valid email address"; + isValid = false; + } else { + emailError.textContent = ""; + } + + // Validate Flat/Door/Building No. + var flatNo = document.getElementById("flatNo").value; + var flatNoError = document.getElementById("flatNoError"); + if (!flatNo.match(/^[\w\s]*$/)) { + flatNoError.textContent = "Please enter a valid flat/door/building number"; + isValid = false; + } else { + flatNoError.textContent = ""; + } + + // Validate Street/Locality + var addressLine1 = document.getElementById("addressLine1").value; + var addressLine1Error = document.getElementById("addressLine1Error"); + if (addressLine1 === "") { + addressLine1Error.textContent = "This field is required"; + isValid = false; + } else { + addressLine1Error.textContent = ""; + } + + // Validate City + var city = document.getElementById("city").value; + var cityError = document.getElementById("cityError"); + if (!city.match(/^[\w\s]*$/)) { + cityError.textContent = "Please enter a valid city name"; + isValid = false; + } else { + cityError.textContent = ""; + } + + // Validate State + var state = document.getElementById("state").value; + var stateError = document.getElementById("stateError"); + if (state === "") { + stateError.textContent = "Please select a state"; + isValid = false; + } else { + stateError.textContent = ""; + } + + // Validate ZIP/Postal Code + var zip = document.getElementById("zip").value; + var zipError = document.getElementById("zipError"); + if (!zip.match(/^\d{6}$/)) { + zipError.textContent = "Please enter a valid ZIP/Postal code (6 digits)"; + isValid = false; + } else { + zipError.textContent = ""; + } + + return isValid; + } + - + \ No newline at end of file diff --git a/views/User_singUp.ejs b/views/User_singUp.ejs index 42076d3..e6aaeba 100644 --- a/views/User_singUp.ejs +++ b/views/User_singUp.ejs @@ -5,7 +5,9 @@ User Signup Form + + - + +
User Signup Form


-
+
@@ -143,7 +148,30 @@ body {
- + diff --git a/views/admin_login.ejs b/views/admin_login.ejs index e1bae1a..8093e38 100644 --- a/views/admin_login.ejs +++ b/views/admin_login.ejs @@ -1,17 +1,20 @@ - - + Admin Login + + @@ -19,7 +22,7 @@ -
+
@@ -74,6 +77,30 @@
+ + \ No newline at end of file diff --git a/views/forget-password.ejs b/views/forget-password.ejs index 1ec00f2..e2a37b5 100644 --- a/views/forget-password.ejs +++ b/views/forget-password.ejs @@ -125,8 +125,7 @@
- - +
diff --git a/views/index.ejs b/views/index.ejs index 17ea65b..d091063 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -22,30 +22,33 @@
-
@@ -208,6 +211,28 @@ }); }); + document.addEventListener('DOMContentLoaded', function () { + const darkModeToggle = document.getElementById('darkModeToggle'); + const darkModeIcon = document.getElementById('darkModeIcon'); + const body = document.body; + const isDarkMode = localStorage.getItem('darkMode') === 'true'; + + // Function to toggle dark mode + function toggleDarkMode() { + const isDark = body.classList.toggle('dark-mode'); + localStorage.setItem('darkMode', isDark); + darkModeIcon.className = isDark ? 'fas fa-moon' : 'fas fa-sun'; // Change icon based on mode + } + + // Set initial mode based on localStorage + if (isDarkMode) { + toggleDarkMode(); + } + + // Event listener for dark mode toggle button + darkModeToggle.addEventListener('click', toggleDarkMode); + }); + diff --git a/views/policies.ejs b/views/policies.ejs new file mode 100644 index 0000000..7b5fb59 --- /dev/null +++ b/views/policies.ejs @@ -0,0 +1,192 @@ + + + + + + Policies + + + + + + + + + + + + + +
+ +
+ + + +
+
+
+

+
Food Safety
+
+ Objective: To ensure that all food distributed is safe for + consumption, thereby protecting the health of recipients. +

+ Policy Details:
+ Food Inspection: All donated food items must be inspected upon receipt + for signs of spoilage, damage, or contamination.
+ Hygiene Standards: Staff and volunteers must follow strict hygiene + protocols, including regular handwashing, wearing gloves, and using + sanitized utensils and surfaces.
+ Temperature Control: Perishable food items must be stored and + transported at appropriate temperatures (below 40Β°F for cold foods and + above 140Β°F for hot foods) to prevent bacterial growth.
+ Training: All individuals involved in food handling must undergo food + safety training covering topics such as cross-contamination + prevention, proper storage, and personal hygiene. +
+
+
+
+
Quality Assurance
+
+ Objective: To maintain a high standard of food quality in all + donations and distributions.

+ Policy Details:
Quality Checks: Implement regular quality checks + for all food items, including visual inspections and taste tests when + applicable.
Feedback Mechanism: Establish a system for + beneficiaries to provide feedback on food quality, which will be used + to make necessary improvements. Supplier Standards: Work with donors + and suppliers to ensure they follow good food safety and quality + practices. +
+
+
+
+
Nutritional Guidelines
+
+ Objective: To provide nutritious and balanced meals to the homeless + and needy.

+ Policy Details:
+ Balanced Diet: Ensure meals include a variety of food groups such as + proteins, vegetables, fruits, grains, and dairy to meet dietary + requirements. +
Dietary Restrictions: Accommodate dietary restrictions and + preferences, including vegetarian, vegan, gluten-free, and + allergen-free options.
Portion Control: Serve appropriately + sized portions to avoid waste and ensure everyone receives adequate + nutrition. +
+
+
+
+
Documentation and Reporting
+
+ Objective: To maintain transparency and accountability in food + distribution activities.

+ Policy Details:
+ Record Keeping: Keep detailed records of all food donations, including + the source, type, quantity, and date of receipt.
+ Distribution Logs: Maintain logs of food distribution activities, + including dates, locations, and quantities distributed.
+ Impact Reporting: Regularly report on the impact of food distribution + efforts, such as the number of meals served and the population + reached.
+ Compliance Documentation: Ensure all documentation meets local health + department and regulatory requirements. +
+
+
+
+
Freshness and Shelf Life
+
+ Objective: To ensure that all distributed food is fresh and safe to + consume.

+ Policy Details:
+ Expiration Dates: Accept only food items that are within their + expiration dates. Clearly label items with expiration dates during + storage.
+ FIFO System: Implement a First In, First Out (FIFO) system to manage + inventory and ensure older items are used first.
+ Regular Inspections: Conduct regular inspections of stored food to + check for spoilage and remove any items that are no longer fresh. +
+
+
+
+
Food Handling and Storage
+
+ Objective: To prevent contamination and spoilage through proper food + handling and storage practices.

+ Policy Details:
+ Proper Storage Conditions: Store food in clean, pest-free environments + at appropriate temperatures and humidity levels.
+ Separation of Food Types: Store raw and cooked foods separately to + avoid cross-contamination.
+ Safe Transportation: Use insulated containers and refrigerated + vehicles for transporting perishable items.
+ Cleaning Protocols: Regularly clean and sanitize storage areas, + transportation vehicles, and all food handling equipment. +
+
+
+
+
Food Waste Management in Inventory
+
+ Objective: To minimize food waste and ensure efficient use of + resources.

+ Policy Details:
+ Inventory Management: Keep accurate inventory records and monitor + stock levels to prevent overstocking and waste.
+ Repurposing Surplus: Identify and implement methods to repurpose + surplus food, such as creating new meals from leftovers.
+ Composting: Partner with local composting facilities to divert food + waste from landfills.
+ Donation of Excess: Establish protocols for redistributing any excess + food to other charitable organizations when it cannot be used in your + own programs. +
+
+
+ + diff --git a/views/set_password.ejs b/views/set_password.ejs new file mode 100644 index 0000000..74b9ed6 --- /dev/null +++ b/views/set_password.ejs @@ -0,0 +1,133 @@ + + + + + + + + Password Reset + + + +
+ +
+
+

Reset Password

+ +
+
+ + + + +
+
+ + +
+ +
+
+ + + + + \ No newline at end of file diff --git a/views/user_login.ejs b/views/user_login.ejs index 1d386c7..c3970b9 100644 --- a/views/user_login.ejs +++ b/views/user_login.ejs @@ -6,12 +6,17 @@ + User Login + + @@ -19,7 +24,7 @@ -
+
@@ -79,6 +84,31 @@
+ +
Food Item Quantity