Skip to content

Commit

Permalink
Merge pull request #100 from HungrySlugs-CSE115A/main
Browse files Browse the repository at this point in the history
main to other
tiwariakshat47 authored May 19, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents def9bc4 + d215a5b commit 83fd39d
Showing 8 changed files with 422 additions and 38 deletions.
41 changes: 41 additions & 0 deletions app/Filter-Window/AllergyFilterPage.module.css
Original file line number Diff line number Diff line change
@@ -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;
}
164 changes: 164 additions & 0 deletions app/Filter-Window/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.container}>
<h1 className={`${styles.filterText} ${styles.filterTopLeft}`}>Filter</h1>
<div className={styles.columns}>
<div className={styles.column}>
<h2 className="flex font-medium text-2xl text-[#003C6C] items-center justify-center pb-5">
Hide Items that contain:
</h2>
<div>
{hideAllergies.map((allergy, index) => (
<div key={index} className={styles.checkbox}>
<label>
<input
type="checkbox"
checked={selectedHideAllergies.includes(allergy)}
onChange={() => handleCheckboxChange(allergy, "hide")}
/>
{allergy}
</label>
</div>
))}
</div>
</div>

<div className={styles.column}>
<h2 className="flex font-medium text-2xl text-[#003C6C] items-center justify-center pb-5">
Show items that match:
</h2>
<div>
{showAllergies.map((allergy, index) => (
<div key={index} className={styles.checkbox}>
<label>
<input
type="checkbox"
checked={selectedShowAllergies.includes(allergy)}
onChange={() => handleCheckboxChange(allergy, "show")}
/>
{allergy}
</label>
</div>
))}
</div>
</div>
</div>

<div className={styles.submitButtons}>
<button
className={`${styles.submitButton} flex font-medium text-2xl text-[#003C6C] items-center justify-center pb-5`}
style={{ padding: "0 1rem", textDecoration: "none" }}
onClick={handleCancel}
>
Cancel
</button>
<button
className={`${styles.submitButton} flex font-medium text-2xl text-[#003C6C] items-center justify-center pb-5`}
style={{ padding: "0 1rem", textDecoration: "none" }}
onClick={handleReset}
>
Reset
</button>
<button
className={`${styles.submitButton} flex font-medium text-2xl text-[#003C6C] items-center justify-center pb-5`}
style={{ padding: "0 1rem", textDecoration: "none" }}
onClick={handleApply}
>
Apply
</button>
</div>
</div>
);
};

export default AllergyFilterPage;
17 changes: 17 additions & 0 deletions app/global_search/Search.module.css
Original file line number Diff line number Diff line change
@@ -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 */
}
130 changes: 114 additions & 16 deletions app/global_search/page.tsx
Original file line number Diff line number Diff line change
@@ -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<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",
};

const BarebonesComponent = () => {
const [dhs, setDhs] = useState<DiningHall[]>([]);
@@ -30,6 +53,21 @@ const BarebonesComponent = () => {
const [showSearchResults, setShowSearchResults] = useState<boolean>(false);
const [noFoodsFound, setNoFoodsFound] = useState<boolean>(false);

// Retrieve hide and show allergies from local storage
const [selectedHideAllergies, setSelectedHideAllergies] = useState<string[]>(
() => {
const storedHideAllergies = localStorage.getItem("hideAllergies");
return storedHideAllergies ? JSON.parse(storedHideAllergies) : [];
},
);

const [selectedShowAllergies, setSelectedShowAllergies] = useState<string[]>(
() => {
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,29 +110,75 @@ 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);
};

return (
<div
style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
{/* Title */}
<h1 className="text-8xl">Welcome to Hungry Slugs!</h1>
{/* Search bar */}
<div className="search-bar" style={{ marginTop: "20px" }}>
{" "}
{/* Adjust margin as needed */}
<input
type="text"
placeholder="Search foods..."
value={searchInput}
onChange={handleSearchInputChange}
/>
<button onClick={handleSearch}>Search</button>
{/* Title and Search bar */}
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
marginBottom: "20px",
}}
>
<h1 className={`${styles.filterText} ${styles.filterTopLeft}`}>
Global Search
</h1>
<div
className="search-bar"
style={{ marginTop: "100px", display: "flex", alignItems: "center" }}
>
<input
type="text"
placeholder="Search foods..."
value={searchInput}
onChange={handleSearchInputChange}
/>
<button onClick={handleSearch}>Search</button>
{/* Filter button */}
<div
style={{
marginLeft: "10px",
padding: "10px 20px",
backgroundColor: "#4CAF50",
color: "white",
cursor: "pointer",
borderRadius: "5px",
}}
onClick={handleFilter}
>
Filter
</div>
</div>
</div>

{/* Display search results if button clicked */}
{showSearchResults && (
<div>
@@ -99,6 +187,16 @@ const BarebonesComponent = () => {
{filteredFoods.map(({ food, dhName, categoryName }, index) => (
<li key={index}>
{food.name} - {categoryName} ({dhName})
<div style={{ display: "flex", flexWrap: "nowrap" }}>
{food.restrictions.map((restriction, index) => (
<img
key={index}
src={restrictionImageMap[restriction]}
alt={restriction}
style={{ width: "25px", height: "25px", margin: "5px" }}
/>
))}
</div>
</li>
))}
</ul>
70 changes: 54 additions & 16 deletions app/locations/[location]/page.tsx
Original file line number Diff line number Diff line change
@@ -5,12 +5,12 @@ import LocationFood from "@/components/location/food";

interface Food {
name: string;
restrictions: Array<string>;
restrictions: string[]; // Change to string array
}

interface SubCategory {
name: string;
foods: Array<Food>;
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<Location>();
const [showCategories, setShowCategories] = useState<boolean[]>();
const [location, setLocation] = useState<Location | null>(null);
const [showCategories, setShowCategories] = useState<boolean[]>([]);

// fetch location data
useEffect(() => {
axios
.get("http://localhost:8000/myapi/locations/")
.get<Location[]>("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 */}
<div className="flex justify-center items-center">
<svg
className={`h-6 w-6 mr-6 ${showCategories && showCategories[i] ? "rotate-180" : ""}`}
className={`h-6 w-6 mr-6 ${
showCategories && showCategories[i] ? "rotate-180" : ""
}`}
fill="#000000"
height="800px"
width="800px"
@@ -113,7 +149,9 @@ export default function Page({ params }: { params: { location: number } }) {
<LocationFood
key={k}
food_name={food.name}
restrictions={food.restrictions}
restriction_images={food.restrictions.map(
(restriction) => restrictionImageMap[restriction],
)}
/>
))}
</div>
22 changes: 21 additions & 1 deletion backend/webscraper/food.py
Original file line number Diff line number Diff line change
@@ -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}
11 changes: 6 additions & 5 deletions components/location/food.tsx
Original file line number Diff line number Diff line change
@@ -2,20 +2,21 @@ 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 (
<Link href={`/foods/${encodeURIComponent(food_name)}`}>
<div className="flex flex-row justify-between hover:border-gray-300 hover:rounded-[2px] border-white border bg-[#F9F9F9] font-medium text-gray-700 py-1 my-1 text-sm">
<h4 className="ml-3">{food_name}</h4>
<div className="flex flex-row mr-3">
<ul className="flex flex-row px-1">
{restrictions.map((restriction, l) => (
<li key={l} className="px-1">
{restriction}
{restriction_images.map((image, index) => (
<li key={index} className="px-1">
<img src={image} height={20} width={20} alt={image} />{" "}
{/* Display the image */}
</li>
))}
</ul>
5 changes: 5 additions & 0 deletions loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function imageLoader({ src }) {
return `/images/${src}`; // REPLACE WITH YOUR IMAGE DIRECTORY
}

module.exports = imageLoader;

0 comments on commit 83fd39d

Please sign in to comment.