Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alex db work + contact us+ npm fix #161

Merged
merged 10 commits into from
Apr 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions back-end/app.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// import and instantiate express
const express = require("express"); // CommonJS import style!
const app = express(); // instantiate an Express object
const path = require("path");
const cookieParser = require("cookie-parser") // middleware useful for parsing cookies in requests
const cors = require('cors');
const cors = require('cors'); // middleware useful for enabling CORS (Cross-Origin Resource Sharing) in Express apps
const contactRoutes = require('./routes/contact'); // contact us form route

require("dotenv").config({ silent: true }) // load environmental variables from a hidden file named .env
app.use(express.static('static'));

app.use(express.static('static')); // serve static files from the static folder
app.use('../static/images', express.static(path.join(__dirname, 'front-end/static/images')));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public'))); // serve static files from the public folder
app.use('/contact', contactRoutes); // contact us form route

// the following are used for authentication with JSON Web Tokens
const _ = require("lodash") // the lodash module has some convenience functions for arrays that we use to sift through our mock user data... you don't need this if using a real database with user info
Expand Down
12 changes: 12 additions & 0 deletions back-end/models/utensils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// models/utensils.js
const mongoose = require("mongoose");

const utensilsSchema = new mongoose.Schema({
id: Number,
name: String,
image: String
});

const Utensils = mongoose.model("Utensils", utensilsSchema);

module.exports = Utensils;
2 changes: 1 addition & 1 deletion back-end/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.3.5",
"axios": "^1.3.6",
"bcryptjs": "^2.4.3",
"concurrently": "^8.0.1",
"cookie-parser": "^1.4.6",
Expand Down
37 changes: 37 additions & 0 deletions back-end/routes/contact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//contact us form route

const express = require('express');
const router = express.Router();
const nodemailer = require('nodemailer');

router.post('/submit', async (req, res) => {
const { name, email, message } = req.body;

// Configure Nodemailer transporter
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: '[email protected]',
pass: process.env.EMAIL_PASSWORD,
},
});

// Email options
const mailOptions = {
from: email,
to: '[email protected]',
subject: 'Contact Form Submission',
text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`,
};

try {
// Send the email
await transporter.sendMail(mailOptions);
res.status(200).json({ message: 'Email sent successfully' });
} catch (error) {
console.error('Error sending email:', error);
res.status(500).json({ message: 'Error sending email' });
}
});

module.exports = router;
89 changes: 58 additions & 31 deletions back-end/routes/utensils.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,77 @@
const express = require("express");
const router = express.Router();
const fs = require('fs');
const path = require('path');
const fs = require("fs");
const path = require("path");
const fetch = require("node-fetch");
const Utensils = require("../models/utensils");

const utensilsFilePath = path.join(__dirname, '../tmp_data/utensils.txt');
const utensilsFilePath = path.join(__dirname, "../tmp_data/utensils.txt");

// Route to get all utensils
router.get("/utensils", (req, res) => {
const fetchUtensilsFromAPI = async (recipeId) => {
const response = await fetch(
`https://api.spoonacular.com/recipes/${recipeId}/equipmentWidget.json?apiKey=${process.env.SPOONACULAR_API_KEY}`
);
if (response.status !== 200) {
throw new Error("Failed to fetch utensils from API");
}
const data = await response.json();
return data.equipment;
};

const saveUtensilsToDatabase = async (utensilsData) => {
for (const utensilData of utensilsData) {
const existingUtensil = await Utensils.findOne({ id: utensilData.id });

if (!existingUtensil) {
const newUtensil = new Utensils(utensilData);
await newUtensil.save();
console.log(`Saved utensil: ${utensilData.name}`);
}
}
};

// Add a new route to fetch and save utensils data from the API
router.get("/utensils/fetch-from-api", async (req, res) => {
try {
const fileContent = fs.readFileSync(utensilsFilePath, 'utf-8');
const utensils = fileContent.split('\n').map(line => {
try {
const { utensil_title, image_url, description } = JSON.parse(line);
return { utensil_title, image_url, description };
} catch (error) {
console.error(`Error parsing utensil: ${line}`, error);
return null;
}
}).filter(utensil => utensil !== null);

res.json(utensils);
const recipeId = req.query.recipeId;
const utensilsData = await fetchUtensilsFromAPI(recipeId);
await saveUtensilsToDatabase(utensilsData);
res.status(200).json({ message: "Utensils data saved to the database" });
} catch (error) {
console.error('Error fetching data from file:', error);
res.status(500).json({ error: 'Failed to fetch utensils' });
console.error("Error fetching and saving utensils:", error);
res.status(500).json({ error: "Failed to fetch and save utensils" });
}
});

// Route to get all utensils
router.get("/utensils", (req, res) => {
// Replace the file read with a database query
Utensils.find({}, { _id: 0, __v: 0 })
.then(utensils => {
res.json(utensils);
})
.catch(error => {
console.error('Error fetching data from the database:', error);
res.status(500).json({ error: 'Failed to fetch utensils' });
});
});

// Route to add a new utensil
router.post("/utensils", (req, res) => {
try {
const newUtensil = {
id: null,
utensil_title: req.body.utensil_title
};
const fileContent = fs.readFileSync(utensilsFilePath, 'utf-8');
const utensils = fileContent.split('\n').filter((line) => line.trim() !== '').map(line => JSON.parse(line));
newUtensil.id = utensils.length + 1;
utensils.push(newUtensil);
fs.writeFileSync(utensilsFilePath, `${fileContent}\n${JSON.stringify(newUtensil)}`, 'utf8');
res.status(200).json(newUtensil);
const newUtensil = new Utensils(req.body);
newUtensil.save()
.then(savedUtensil => {
res.status(200).json(savedUtensil);
})
.catch(error => {
console.error('Error adding utensil:', error);
res.status(500).json({ error: 'Failed to add utensil' });
});
} catch (error) {
console.error('Error adding utensil:', error);
res.status(500).json({ error: 'Failed to add utensil' });
}
});

// Other routes remain unchanged

module.exports = router;
14 changes: 13 additions & 1 deletion back-end/test/utensils.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//unit test for utensils
const chai = require('chai');
const chaiHttp = require('chai-http');
const app = require('../app');
Expand All @@ -7,6 +6,19 @@ chai.use(chaiHttp);
chai.should();

describe('Utensils', () => {
describe('GET /utensils/fetch-from-api', () => {
it('should fetch utensils from the Spoonacular API and save them to the database', (done) => {
chai.request(app)
.get('/utensils/fetch-from-api')
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body.should.have.property('message').eql('Utensils fetched from API and saved to the database');
done();
});
});
});

describe('GET /utensils', () => {
it('should return all utensils', (done) => {
chai.request(app)
Expand Down
5 changes: 4 additions & 1 deletion front-end/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.12.4",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@mui/material": "^5.12.1",
"@mui/system": "^5.12.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
Expand Down
103 changes: 16 additions & 87 deletions front-end/src/App.css
Original file line number Diff line number Diff line change
@@ -1,98 +1,27 @@
/* styles shared by all screens within the app */

body {
background-image: url('https://thefridgeagency.com/wp-content/uploads/sites/3/2020/04/Fridge_Zoom_Background_inside.jpg');
background-size: auto;
background-position: relative;
}

.container {
background-color: rgba(255, 255, 255, 0.8);
border: 0px solid black;
max-width: 375px; /* adjust the max-width for iPhone 14 screen */
margin: 0 auto;
padding: 0;
}

/* each screen has a main content area with an image, and a paragraph */
.main-content {
margin: 20px;
.AboutUs {
display: flex;
/* use css flexbox for elements inside of this element */
}

.main-content p,
.main-content img {
flex: auto;
flex-direction: column;
align-items: center;
justify-content: top;
height: 100vh;
}

.main-content p {
margin-left: 0px;
}

.main-content img {
object-fit: contain;
}
.main-content form {
flex: auto;
margin-left: 0px;
}
h1 {
font-family: georgia;
margin-left: 0px;
}
p {
font-family: helvetica;
}
/* styles for form text fields */
input[type="text"],
input[type="password"],
input[type="number"] {
padding: 10px;
margin: 10px 0;
width: 200px; /* Changed the width to fit in the smaller screen */
max-width: 90%; /* Added a max-width to prevent overflowing on smaller screens */
}

/* styles for the form submit button */
input[type="submit"] {
margin-top: 20px;
background-color: black;
color: white;
border: 1px solid white;
padding: 20px;
font-size: 20px;
}

/* changes to the submit button when it is being clicked */
input[type="submit"]:active {
color: black;
background-color: white;
border-color: black;
li.ContactLink
{
color: grey;
}

.menu-button {
position: fixed;
top: 55px;
left: 0px; /* Change the left position to 0px to always align it to the left corner */
padding: 0.5rem 1rem;
font-size: 1.1rem;
background-color: #3498db;
color: #fff;
border: none;
cursor: pointer;
border-radius: 4px;
z-index: 1001; /* Place the button above the menu overlay */
li.ContactLink:hover
{
color: teal;
}

.menu-button:hover {
background-color: #2980b9;
li.ContactLink:active
{
color: teal;
}

/* media query to override the menu button style for larger screens */
@media only screen and (min-width: 768px) {
.menu-button {
left: 650px; /* Align the button with the menu overlay on larger screens */
}
li.ContactLink:visited
{
color: green;
}
20 changes: 11 additions & 9 deletions front-end/src/ContactUs.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
import React, { useState } from 'react';
import axios from 'axios';

const ContactUs = () => {
// Use the useState hook to manage the form submission status
const [formStatus, setFormStatus] = useState('Send');

// Handle form submission
const handleSubmit = (event) => {
const handleSubmit = async (event) => {
event.preventDefault();

// Update the form status to indicate that the form is being submitted
setFormStatus('Submitting...');

// Extract the values of the form fields
const { name, email, message } = event.target.elements;

// Create an object with the form data
const formData = {
name: name.value,
email: email.value,
message: message.value,
};

// Log the form data to the console
console.log(formData);
try {
const response = await axios.post('/contact/submit', formData);
setFormStatus('Sent!');
console.log(response.data.message);
} catch (error) {
setFormStatus('Error sending');
console.error('Error submitting contact form:', error);
}
};


return (
<div className="container mt-5">
<h2 className="mb-3">Contact Form</h2>
Expand Down
Loading