diff --git a/app/Filter-Window/AllergyFilterPage.module.css b/app/Filter-Window/AllergyFilterPage.module.css new file mode 100644 index 0000000..688a72b --- /dev/null +++ b/app/Filter-Window/AllergyFilterPage.module.css @@ -0,0 +1,41 @@ +/* AllergyFilterPage.module.css */ + +.container { + @apply flex flex-col justify-center items-center min-h-screen py-12; +} + +.columns { + @apply flex justify-center items-start space-x-8; +} + +.submitButtons { + @apply flex justify-center mt-8; /* Adjust the margin-top */ +} + +.submitButton { + @apply mx-2; /* Add margin to each button */ +} + +.filterText { + font-size: 3rem; /* Adjust the font size as needed */ + color: #003c6c; /* Set the text color */ + font-weight: 600; /* Set the font weight */ + padding-top: 1.25rem; /* Match the py-5 padding top */ + display: center; /* Match the flex display */ + align-items: center; /* Match the items-center alignment */ + justify-content: center; /* Match the justify-center alignment */ +} + +.filterTopLeft { + position: absolute; + align-items: center; + top: 10rem; /* Adjust the top position as needed */ + left: 80 rem; /* Adjust the left position as needed */ + margin: 0; /* Remove the margin */ +} + +.submitButton:hover { + text-decoration: underline; + text-underline-offset: 0.2em; + text-decoration-color: #ffea00; +} diff --git a/app/Filter-Window/page.tsx b/app/Filter-Window/page.tsx new file mode 100644 index 0000000..e6426c1 --- /dev/null +++ b/app/Filter-Window/page.tsx @@ -0,0 +1,164 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import styles from "./AllergyFilterPage.module.css"; + +const AllergyFilterPage = () => { + const [selectedHideAllergies, setSelectedHideAllergies] = useState(() => { + const storedHideAllergies = localStorage.getItem("hideAllergies"); + return storedHideAllergies ? JSON.parse(storedHideAllergies) : []; + }); + + const [selectedShowAllergies, setSelectedShowAllergies] = useState(() => { + const storedShowAllergies = localStorage.getItem("showAllergies"); + return storedShowAllergies ? JSON.parse(storedShowAllergies) : []; + }); + + const hideAllergies = [ + "milk", + "eggs", + "fish", + "shellfish", + "treenut", + "nuts", + "wheat", + "soy", + "gluten", + "sesame", + "alcohol", + ]; + const showAllergies = [ + "eggs", + "fish", + "gluten", + "milk", + "nuts", + "soy", + "vegan", + "veggie", + "pork", + "beef", + "halal", + "shellfish", + "treenut", + "alcohol", + "sesame", + ]; + + const handleReset = () => { + setSelectedHideAllergies([]); + setSelectedShowAllergies([]); + }; + + const handleCancel = () => { + setSelectedHideAllergies([]); + setSelectedShowAllergies([]); + window.location.href = "global_search"; + }; + + const handleApply = () => { + localStorage.setItem( + "hideAllergies", + JSON.stringify(selectedHideAllergies), + ); + localStorage.setItem( + "showAllergies", + JSON.stringify(selectedShowAllergies), + ); + console.log("Hide Allergies:", selectedHideAllergies); + console.log("Show Allergies:", selectedShowAllergies); + window.location.href = "global_search"; + }; + + const handleCheckboxChange = (allergy, type) => { + if (type === "hide") { + if (selectedHideAllergies.includes(allergy)) { + setSelectedHideAllergies( + selectedHideAllergies.filter((item) => item !== allergy), + ); + } else { + setSelectedHideAllergies([...selectedHideAllergies, allergy]); + } + } else { + if (selectedShowAllergies.includes(allergy)) { + setSelectedShowAllergies( + selectedShowAllergies.filter((item) => item !== allergy), + ); + } else { + setSelectedShowAllergies([...selectedShowAllergies, allergy]); + } + } + }; + + return ( +
+

Filter

+
+
+

+ Hide Items that contain: +

+
+ {hideAllergies.map((allergy, index) => ( +
+ +
+ ))} +
+
+ +
+

+ Show items that match: +

+
+ {showAllergies.map((allergy, index) => ( +
+ +
+ ))} +
+
+
+ +
+ + + +
+
+ ); +}; + +export default AllergyFilterPage; diff --git a/app/global_search/Search.module.css b/app/global_search/Search.module.css new file mode 100644 index 0000000..edf7cf6 --- /dev/null +++ b/app/global_search/Search.module.css @@ -0,0 +1,17 @@ +.filterText { + font-size: 3rem; /* Adjust the font size as needed */ + color: #003c6c; /* Set the text color */ + font-weight: 600; /* Set the font weight */ + padding-top: 1.25rem; /* Match the py-5 padding top */ + display: center; /* Match the flex display */ + align-items: center; /* Match the items-center alignment */ + justify-content: center; /* Match the justify-center alignment */ +} + +.filterTopLeft { + position: absolute; + align-items: center; + top: 3rem; /* Adjust the top position as needed */ + left: 80 rem; /* Adjust the left position as needed */ + margin: 0; /* Remove the margin */ +} diff --git a/app/global_search/page.tsx b/app/global_search/page.tsx index 01563b2..eb58105 100644 --- a/app/global_search/page.tsx +++ b/app/global_search/page.tsx @@ -1,9 +1,11 @@ "use client"; import React, { useState, useEffect } from "react"; import axios from "axios"; +import styles from "./Search.module.css"; interface Food { name: string; + restrictions: string[]; // Change to string array } interface subCategory { @@ -18,8 +20,29 @@ interface Category { interface DiningHall { name: string; - categories: Category[]; + categories: Array; } +interface RestrictionImageMap { + [key: string]: string; +} + +const restrictionImageMap = { + eggs: "/Images/egg.jpg", + vegan: "/Images/vegan.jpg", + fish: "/Images/fish.jpg", + veggie: "/Images/veggie.jpg", + gluten: "/Images/gluten.jpg", + pork: "/Images/pork.jpg", + milk: "/Images/milk.jpg", + beef: "/Images/beef.jpg", + nuts: "/Images/nuts.jpg", + halal: "/Images/halal.jpg", + soy: "/Images/soy.jpg", + shellfish: "/Images/shellfish.jpg", + treenut: "/Images/treenut.jpg", + sesame: "/Images/sesame.jpg", + alcohol: "/Images/alcohol.jpg", +}; const BarebonesComponent = () => { const [dhs, setDhs] = useState([]); @@ -30,6 +53,21 @@ const BarebonesComponent = () => { const [showSearchResults, setShowSearchResults] = useState(false); const [noFoodsFound, setNoFoodsFound] = useState(false); + // Retrieve hide and show allergies from local storage + const [selectedHideAllergies, setSelectedHideAllergies] = useState( + () => { + const storedHideAllergies = localStorage.getItem("hideAllergies"); + return storedHideAllergies ? JSON.parse(storedHideAllergies) : []; + }, + ); + + const [selectedShowAllergies, setSelectedShowAllergies] = useState( + () => { + const storedShowAllergies = localStorage.getItem("showAllergies"); + return storedShowAllergies ? JSON.parse(storedShowAllergies) : []; + }, + ); + useEffect(() => { axios .get("http://localhost:8000/myapi/locations/") @@ -48,6 +86,10 @@ const BarebonesComponent = () => { setSearchInput(event.target.value); }; + const handleFilter = () => { + window.location.href = "Filter-Window"; + }; + const handleSearch = () => { const allFoods: { food: Food; dhName: string; categoryName: string }[] = []; dhs.forEach((dh) => { @@ -68,8 +110,28 @@ const BarebonesComponent = () => { food.name.toLowerCase().includes(searchInput.toLowerCase()), ); - setNoFoodsFound(filtered.length === 0); - setFilteredFoods(filtered); + // Check if all boxes are unchecked + const allBoxesUnchecked = + selectedShowAllergies.length === 0 && selectedHideAllergies.length === 0; + + let finalFilteredFoods = filtered; + if (!allBoxesUnchecked) { + // Filter foods based on selectedShowAllergies and selectedHideAllergies + finalFilteredFoods = filtered.filter(({ food }) => { + const hasShowAllergy = + selectedShowAllergies.length === 0 || + selectedShowAllergies.every((allergy) => + food.name.toLowerCase().includes(allergy.toLowerCase()), + ); + const hasHideAllergy = selectedHideAllergies.some( + (allergy) => food.restrictions.includes(allergy.toLowerCase()), // Check if food's restrictions include the hide allergy + ); + return hasShowAllergy && !hasHideAllergy; + }); + } + + setNoFoodsFound(finalFilteredFoods.length === 0); + setFilteredFoods(finalFilteredFoods); setShowSearchResults(true); }; @@ -77,20 +139,46 @@ const BarebonesComponent = () => {
- {/* Title */} -

Welcome to Hungry Slugs!

- {/* Search bar */} -
- {" "} - {/* Adjust margin as needed */} - - + {/* Title and Search bar */} +
+

+ Global Search +

+
+ + + {/* Filter button */} +
+ Filter +
+
+ {/* Display search results if button clicked */} {showSearchResults && (
@@ -99,6 +187,16 @@ const BarebonesComponent = () => { {filteredFoods.map(({ food, dhName, categoryName }, index) => (
  • {food.name} - {categoryName} ({dhName}) +
    + {food.restrictions.map((restriction, index) => ( + {restriction} + ))} +
  • ))} diff --git a/app/locations/[location]/page.tsx b/app/locations/[location]/page.tsx index 3cbb6d2..c659265 100644 --- a/app/locations/[location]/page.tsx +++ b/app/locations/[location]/page.tsx @@ -5,12 +5,12 @@ import LocationFood from "@/components/location/food"; interface Food { name: string; - restrictions: Array; + restrictions: string[]; // Change to string array } interface SubCategory { name: string; - foods: Array; + foods: Food[]; // Update to use the Food interface } interface Category { @@ -23,31 +23,65 @@ interface Location { categories: Category[]; } +interface RestrictionImageMap { + [key: string]: string; +} + +const restrictionImageMap = { + eggs: "/Images/egg.jpg", + vegan: "/Images/vegan.jpg", + fish: "/Images/fish.jpg", + veggie: "/Images/veggie.jpg", + gluten: "/Images/gluten.jpg", + pork: "/Images/pork.jpg", + milk: "/Images/milk.jpg", + beef: "/Images/beef.jpg", + nuts: "/Images/nuts.jpg", + halal: "/Images/halal.jpg", + soy: "/Images/soy.jpg", + shellfish: "/Images/shellfish.jpg", + treenut: "/Images/treenut.jpg", + sesame: "/Images/sesame.jpg", + alcohol: "/Images/alcohol.jpg", +}; + export default function Page({ params }: { params: { location: number } }) { - const [location, setLocation] = useState(); - const [showCategories, setShowCategories] = useState(); + const [location, setLocation] = useState(null); + const [showCategories, setShowCategories] = useState([]); - // fetch location data useEffect(() => { axios - .get("http://localhost:8000/myapi/locations/") + .get("http://localhost:8000/myapi/locations/") .then((response) => { - // Fetch the locations data const locations: Location[] = response.data["locations"]; - - // get the location data const location = locations[params.location]; - - // Set the location setLocation(location); - // Set the show categories to array of booleans - setShowCategories(new Array(location.categories.length).fill(true)); + // Get current hour + const currentHour = new Date().getHours(); + + // Set showCategories based on the time of day + setShowCategories( + new Array(location.categories.length).fill(false).map((_, index) => { + switch (index) { + case 0: // Breakfast (6 AM - 11 AM) + return currentHour >= 6 && currentHour < 11; + case 1: // Lunch (11 AM - 1 PM) + return currentHour >= 11 && currentHour < 15; + case 2: // Dinner (6 PM - 9 PM) + return currentHour >= 15 && currentHour < 20; + case 3: // Late Night (9 PM - 12 AM) + return currentHour >= 20 && currentHour < 24; + default: + return false; + } + }), + ); }) .catch((error) => { console.log(error); }); - }, [params.location]); // params.location is a dependency + }, [params.location]); const handleDiningHallSearch = () => { @@ -86,7 +120,9 @@ export default function Page({ params }: { params: { location: number } }) { {/* Icon for accordion that will face up on false and down on true */}
    restrictionImageMap[restriction], + )} /> ))}
    diff --git a/backend/webscraper/food.py b/backend/webscraper/food.py index 3e9daa2..0a22f09 100644 --- a/backend/webscraper/food.py +++ b/backend/webscraper/food.py @@ -1,7 +1,27 @@ from bs4.element import Tag + class Food: + + restriction_image_map = { + "eggs": "app/locations/Images/egg.jpg", + "vegan": "app/locations/Images/vegan.jpg", + "fish": "app/locations/Images/fish.jpg", + "veggie": "app/locations/Images/veggie.jpg", + "gluten": "app/locations/Images/gluten.jpg", + "pork": "app/locations/Images/pork.jpg", + "milk": "app/locations/Images/milk.jpg", + "beef": "app/locations/Images/beef.jpg", + "nuts": "app/locations/Images/nuts.jpg", + "halal": "app/locations/Images/halal.jpg", + "soy": "app/locations/Images/soy.jpg", + "shellfish": "app/locations/Images/shellfish.jpg", + "treenut": "app/locations/Images/treenut.jpg", + "sesame": "app/locations/Images/sesame.jpg", + "alcohol": "app/locations/Images/alcohol.jpg", + } + def __init__(self, html: Tag) -> None: self.name = "Error: Food name not found" self.allergies = [] @@ -26,4 +46,4 @@ def __str__(self) -> str: def to_dict(self) -> dict: # foodObj = {self.name: self.allergies} - return {"name": self.name, "restrictions": self.allergies} + return {"name": self.name, "restrictions": self.allergies, "restriction_image_map": self.restriction_image_map} diff --git a/components/location/food.tsx b/components/location/food.tsx index 2fc9219..8e8f937 100644 --- a/components/location/food.tsx +++ b/components/location/food.tsx @@ -2,10 +2,10 @@ import Link from "next/link"; export default function LocationFood({ food_name, - restrictions, + restriction_images, }: { food_name: string; - restrictions: string[]; + restriction_images: string[]; // Change the type to string array }) { return ( @@ -13,9 +13,10 @@ export default function LocationFood({

    {food_name}

      - {restrictions.map((restriction, l) => ( -
    • - {restriction} + {restriction_images.map((image, index) => ( +
    • + {image}{" "} + {/* Display the image */}
    • ))}
    diff --git a/loader.js b/loader.js new file mode 100644 index 0000000..0f0c13e --- /dev/null +++ b/loader.js @@ -0,0 +1,5 @@ +function imageLoader({ src }) { + return `/images/${src}`; // REPLACE WITH YOUR IMAGE DIRECTORY +} + +module.exports = imageLoader;