diff --git a/api/index.js b/api/index.js index aad0428..d7fd8ad 100644 --- a/api/index.js +++ b/api/index.js @@ -497,13 +497,41 @@ app.http('checkUser', { // ***TODO*** // FUNCTION NAME: getExpiringFoods -// DESCRIPTION: get list of foods expiring soon +// DESCRIPTION: get list of foods expiring soon - next week ? +// ROUTE: ?? // RETURN: // ***TODO*** // FUNCTION NAME: getLowQuantityFoods // DESCRIPTION: get list of foods low in quantity -// RETURN: +// ROUTE: ?? +// RETURN: [] +app.http('getLowQuantityFoods', { + methods: ['GET'], + authLevel: 'anonymous', + route: 'food', + handler: async (request, context) => { + const auth_header = request.headers.get('X-MS-CLIENT-PRINCIPAL'); + let token = null; + if (auth_header) { + token = Buffer.from(auth_header, "base64"); + token = JSON.parse(token.toString()); + context.log("token= " + JSON.stringify(token)); + const userId = token.userId; + const client = await mongoClient.connect(process.env.AZURE_MONGO_DB); + const lowQuantityFoods = await client.db("LetMeCookDB").collection("foods").find({ userId: userId, quantity: { $lte: 3 } }).toArray(); + client.close(); + context.log("Foods that are low in quantity: ", lowQuantityFoods); + return { + // jsonBody: { data : lowQuantityFoods } + jsonBody: {data: []} + } + } + }, +}); + + + app.http('searchRecipesFromIngredients', { @@ -529,4 +557,126 @@ app.http('searchRecipesFromIngredients', { }); +app.http('editFood', { + methods: ['POST'], + authLevel: 'anonymous', + route: 'food/edit/{id}', + handler: async (request, context) => { + const _id = request.params.id; + if (ObjectId.isValid(_id)) { + const auth_header = request.headers.get('X-MS-CLIENT-PRINCIPAL'); + let token = null; + if (auth_header) { + token = Buffer.from(auth_header, "base64"); + token = JSON.parse(token.toString()); + context.log("token= " + JSON.stringify(token)); + const userId = token.userId; + const body = await request.json(); + if (body) { + const name = body.name; + const image = body.image; + const quantity = body.quantity; + const expirationDate = body.expirationDate; + const client = await mongoClient.connect(process.env.AZURE_MONGO_DB); + const result = await client.db("LetMeCookDB").collection("foods").updateOne({_id: new ObjectId(_id), userId: userId}, {$set: {name: name, image: image, quantity: quantity, expirationDate: expirationDate}}); + client.close(); + if (result.matchedCount > 0) { + return { + status: 201, + jsonBody: {_id: _id} + } + } + return { + status: 403, + jsonBody: {error: "Authorization failed for editing food with id: " + _id} + } + } + return { + status: 405, + jsonBody: {error: "Empty content sent to edit food with id: " + _id} + } + } + return { + status: 403, + jsonBody: {error: "Authorization failed for editing food with id: " + _id} + } + } + return { + status: 404, + jsonBody: {error: "Unknown food with id: " + _id} + } + }, +}); + +app.http('editRecipe', { + methods: ['POST'], + authLevel: 'anonymous', + route: 'recipe/edit/{id}', + handler: async (request, context) => { + const _id = request.params.id; + if (ObjectId.isValid(_id)) { + const auth_header = request.headers.get('X-MS-CLIENT-PRINCIPAL'); + let token = null; + if (auth_header) { + token = Buffer.from(auth_header, "base64"); + token = JSON.parse(token.toString()); + context.log("token= " + JSON.stringify(token)); + const userId = token.userId; + const body = await request.json(); + const name = body.name; + const image = body.image; + const instructions = body.instructions; + const client = await mongoClient.connect(process.env.AZURE_MONGO_DB); + const result = await client.db("LetMeCookDB").collection("recipes").updateOne({_id: new ObjectId(_id), userId: userId}, {$set: {name: name, image: image, instructions: instructions}}); + client.close(); + if (result.matchedCount > 0) { + return { + status: 201, + jsonBody: {_id: _id} + } + } + return { + status: 403, + jsonBody: {error: "Authorization failed for editing recipe with id: " + _id} + } + } + return { + status: 403, + jsonBody: {error: "Authorization failed for editing recipe with id: " + _id} + } + } + return { + status: 404, + jsonBody: {error: "Unknown recipe with id: " + _id} + } + }, +}); + +app.http('addRecipeToQueue', { + methods: ['POST'], + authLevel: 'anonymous', + route: 'recipe/queue/{id}', + handler: async (request, context) => { + const _id = request.params.id; + if (ObjectId.isValid(_id)) { + const auth_header = request.headers.get('X-MS-CLIENT-PRINCIPAL'); + let token = null; + if (auth_header) { + token = Buffer.from(auth_header, "base64"); + token = JSON.parse(token.toString()); + context.log("token= " + JSON.stringify(token)); + const userId = token.userId; + } + return { + status: 403, + jsonBody: {error: "Authorization failed for adding to queue with recipe id: " + _id} + } + } + return { + status: 404, + jsonBody: {error: "Unknown recipe with id: " + _id} + } + }, +}); + // GET https://api.spoonacular.com/recipes/findByIngredients?ingredients=apples,+flour,+sugar&number=2 \ No newline at end of file diff --git a/src/App.js b/src/App.js index 1796014..07b1b23 100644 --- a/src/App.js +++ b/src/App.js @@ -1,17 +1,19 @@ import { Link, Outlet } from "react-router-dom"; import NavBar from "./components/NavBar"; -import SaveButton from './components/SaveButton' -import AddButton from './components/AddButton' -import ItemHeader from './components/ItemHeader' -import ItemHeaderEditable from './components/ItemHeaderEditable' + function App() { return (
Landing
- Ingredients
Food
- - + Food Edit
+ Food Create
+ Recipes
+ Recipe indiviual
+ Recipes
+ Recipes create
+ Queue
+ {/* - -
- ) + return
+ +
} \ No newline at end of file diff --git a/src/components/EditButton.jsx b/src/components/EditButton.jsx index 950032a..534178f 100644 --- a/src/components/EditButton.jsx +++ b/src/components/EditButton.jsx @@ -1,7 +1,6 @@ import styles from '../css/Buttons.module.css'; -export default function EditButton(props){ - return (
- -
- ) +export default function EditButton({...props}){ + return
+ +
} \ No newline at end of file diff --git a/src/components/ItemHeader.jsx b/src/components/ItemHeader.jsx index e3f68b8..2abd622 100644 --- a/src/components/ItemHeader.jsx +++ b/src/components/ItemHeader.jsx @@ -1,12 +1,9 @@ import styles from '../css/ItemHeader.module.css'; -export default function ItemHeader(props){ - return(
-
-

{props.title}

- {props.alt} - - -
-
- ) +export default function ItemHeader({ name, image, alt }){ + return
+
+

{name}

+ {alt} +
+
} \ No newline at end of file diff --git a/src/components/ItemHeaderEditable.jsx b/src/components/ItemHeaderEditable.jsx index c41474f..0984bc9 100644 --- a/src/components/ItemHeaderEditable.jsx +++ b/src/components/ItemHeaderEditable.jsx @@ -1,17 +1,11 @@ import styles from '../css/ItemHeader.module.css'; -export default function ItemHeaderEditable(props){ - return(
+export default function ItemHeaderEditable({ name, image, alt, updateName }){ + return
- - - - {props.alt} + updateName(e.target.value)}/> + {alt} - -
- ) - - } \ No newline at end of file +} \ No newline at end of file diff --git a/src/components/ListItem.jsx b/src/components/ListItem.jsx index 2fc57cf..0aaf7d7 100644 --- a/src/components/ListItem.jsx +++ b/src/components/ListItem.jsx @@ -6,14 +6,14 @@ import styles from '../css/ListItem.module.css' export default function ListItem({name, description, image, viewLink, editLink}) { const navigate = useNavigate() return ( - navigate(viewLink)}> + {image ? - + navigate(viewLink)}> food : <>} - {name} - {description} + navigate(viewLink)}>{name} + navigate(viewLink)}>{description} Edit diff --git a/src/components/NavBar.jsx b/src/components/NavBar.jsx index 435483b..659f84e 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar.jsx @@ -1,20 +1,19 @@ import Nav from 'react-bootstrap/Nav'; import Navbar from 'react-bootstrap/Navbar'; import { Link } from 'react-router-dom'; -import 'bootstrap/dist/css/bootstrap.min.css'; import styles from "../css/NavBar.module.css" function NavBar() { return (
- - - + +
+ Food + Recipe + Queue + My Account +
+
); } diff --git a/src/components/NavBarOld2.jsx b/src/components/NavBarOld2.jsx new file mode 100644 index 0000000..435483b --- /dev/null +++ b/src/components/NavBarOld2.jsx @@ -0,0 +1,22 @@ +import Nav from 'react-bootstrap/Nav'; +import Navbar from 'react-bootstrap/Navbar'; +import { Link } from 'react-router-dom'; +import 'bootstrap/dist/css/bootstrap.min.css'; +import styles from "../css/NavBar.module.css" + +function NavBar() { + return ( +
+ + + +
+ ); +} + +export default NavBar; \ No newline at end of file diff --git a/src/components/QueueItem.jsx b/src/components/QueueItem.jsx index 19a39d4..4529f0e 100644 --- a/src/components/QueueItem.jsx +++ b/src/components/QueueItem.jsx @@ -1,7 +1,5 @@ export default function Queue(props){ - return(
+ return
- -
- ) +
} \ No newline at end of file diff --git a/src/components/RemoveButton.jsx b/src/components/RemoveButton.jsx index 4e548aa..76fde36 100644 --- a/src/components/RemoveButton.jsx +++ b/src/components/RemoveButton.jsx @@ -1,8 +1,6 @@ import styles from '../css/Buttons.module.css'; export default function RemoveButton(props){ - return(
- + return
-
- ) +
} \ No newline at end of file diff --git a/src/components/SaveButton.jsx b/src/components/SaveButton.jsx index c49fac6..eb07025 100644 --- a/src/components/SaveButton.jsx +++ b/src/components/SaveButton.jsx @@ -1,7 +1,6 @@ import styles from '../css/Buttons.module.css'; -export default function SaveButton(props) { - return (
- -
- ) +export default function SaveButton({...props}) { + return
+ +
} \ No newline at end of file diff --git a/src/components/SearchButton.jsx b/src/components/SearchButton.jsx index 9c0c9a3..d67ae8b 100644 --- a/src/components/SearchButton.jsx +++ b/src/components/SearchButton.jsx @@ -1,7 +1,5 @@ export default function SearchButton(props){ - return(
+ return
- -
- ) +
} \ No newline at end of file diff --git a/src/css/Login.module.css b/src/css/Login.module.css new file mode 100644 index 0000000..6cb9d5f --- /dev/null +++ b/src/css/Login.module.css @@ -0,0 +1,13 @@ +.button { + padding: 0.5em; + margin: 0.5em; + margin-left: auto; + margin-right: auto; + border: none; + border-radius: 5px ; + width: 8em; +} + +.google { + +} \ No newline at end of file diff --git a/src/css/Search.module.css b/src/css/Search.module.css new file mode 100644 index 0000000..d56d994 --- /dev/null +++ b/src/css/Search.module.css @@ -0,0 +1,14 @@ +.searchicon{ + height: 2em; + width: 2em; + +} +.searchIconContainer{ + border:none; + background-color: #00000000; +} +.container{ + border: 1px solid #000000; + border-radius: 5px; + width:14.5em; +} diff --git a/src/index.js b/src/index.js index 80d3058..67c3510 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,6 @@ import { redirect } from "react-router-dom"; import Login, { authLoader as loginAuthLoader } from './pages/Login' -import loginRedirectLoader from './pages/LoginRedirectLoader' import Landing from './pages/Landing' import Foods from './pages/food/Foods' import Food from './pages/food/Food' @@ -29,6 +28,13 @@ async function authLoader() { return redirect('/login') } +async function loginRedirectLoader() { + // check user first time login + const res = await fetch('/api/login', { method: "GET" }) + console.log("Logging in: ", res) + return redirect('/') +} + const router = createBrowserRouter([ { path: "/", diff --git a/src/pages/Login.js b/src/pages/Login.js index bc1be1a..404af58 100644 --- a/src/pages/Login.js +++ b/src/pages/Login.js @@ -1,4 +1,6 @@ import { redirect } from 'react-router-dom' +import Stack from 'react-bootstrap/Stack' +import styles from '../css/Login.module.css'; export async function authLoader() { const res = await fetch("/.auth/me") @@ -13,12 +15,19 @@ export async function authLoader() { const loginBaseUrl = '/.auth/login/' const redirectParam = '?post_login_redirect_uri=/login/redirect' +function loginOnClick(url) { + return () => window.location.href = loginBaseUrl+url+redirectParam +} + export default function Login() { - return
- Log in with the Microsoft Identity Platform
- Log in with Facebook
- Log in with Google
- Log in with Twitter
- Log in with Apple
-
+ return +

Let Me Cook

+ Login/Signup + + + + + + +
} \ No newline at end of file diff --git a/src/pages/LoginRedirectLoader.js b/src/pages/LoginRedirectLoader.js deleted file mode 100644 index 9e5b352..0000000 --- a/src/pages/LoginRedirectLoader.js +++ /dev/null @@ -1,8 +0,0 @@ -import { redirect } from "react-router-dom" - -export default async function loginRedirectLoader() { - // check user first time login - const res = await fetch('/api/login', { method: "GET" }) - console.log(res) - return redirect('/') -} \ No newline at end of file diff --git a/src/pages/food/CreateFood.js b/src/pages/food/CreateFood.js index a3b1785..11a5312 100644 --- a/src/pages/food/CreateFood.js +++ b/src/pages/food/CreateFood.js @@ -1,6 +1,6 @@ import { useState } from "react"; import { useNavigate } from "react-router-dom" - +import searchStyle from "../../css/Search.module.css" export default function CreateFood() { const navigate = useNavigate() const [food, setFood] = useState("") @@ -36,9 +36,14 @@ export default function CreateFood() { navigate('/food/'+body._id) } - return <> - {setFood(e.target.value)}}/> - + return (
+
+ {setFood(e.target.value)}} className={searchStyle.container}/> + +
{ results && results.length !== 0 ? results.map((e,i) => // using array index as keys here is fine so long as there is no way to add/remove elements from the array @@ -46,5 +51,5 @@ export default function CreateFood() { { e.name }
) : <> } - +
) } \ No newline at end of file diff --git a/src/pages/food/EditFood.js b/src/pages/food/EditFood.js index 086e789..36e1a8f 100644 --- a/src/pages/food/EditFood.js +++ b/src/pages/food/EditFood.js @@ -1,14 +1,45 @@ -import { useParams } from 'react-router-dom' +import { useState } from 'react' +import { useParams, useNavigate } from 'react-router-dom' import SaveButton from '../../components/SaveButton' -export default function EditFood() { - const { id } = useParams() +import ItemHeaderEditable from '../../components/ItemHeaderEditable' - function editFood() { +export default function EditFood() { + const navigate = useNavigate() + const { id: _id } = useParams() + const [name, setName] = useState("") + const image = useState("") + const [quantity, setQuantity] = useState(0) + const [expirationDate, setExpirationDate] = useState("") + async function editFood() { + const res = await fetch('/api/food/edit/'+_id, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + name, + image, + quantity, + expirationDate + }) + }) + console.log('Editing Food', res) + if (!res.ok) { + window.alert("Failed editing food!") + return + } + navigate('/food/' + _id) } return
-

Edit Food

- + +
+ Quantity: + setQuantity(e.target.value)}/> + Expiration Date: + setExpirationDate(e.target.value)}/> +
+
} \ No newline at end of file