-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
375 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { ReactNode, useContext, useState } from 'react'; | ||
import { DateTime } from 'luxon'; | ||
import MenuModal from '../lists/MenuModal'; | ||
import MenuContext from '../../contexts/MenuContext'; | ||
|
||
|
||
type ActionButtonProps = { | ||
children: ReactNode, | ||
now: boolean, | ||
note?: string, | ||
onClick?: () => void | ||
} | ||
function ActionButton(props: ActionButtonProps) { | ||
const { children, now, note, onClick } = props; | ||
return ( | ||
<button | ||
className={`mt-2 w-full px-3.5 py-1.5 right-5 top-0 rounded-md ${note || 'sm:w-fit sm:absolute sm:my-auto sm:h-fit'} ${now ? 'bottom-8' : 'bottom-0'} bg-black/10 dark:bg-black/20 hover:bg-black/20 dark:hover:bg-black/30 transition duration-75`} | ||
onClick={onClick} | ||
> | ||
{children} | ||
</button> | ||
) | ||
} | ||
|
||
type PeriodActionButtonProps = { | ||
date: DateTime, | ||
name: string, | ||
now: boolean, | ||
note?: string | ||
} | ||
export default function PeriodActionButton(props: PeriodActionButtonProps) { | ||
const { name } = props; | ||
|
||
if (name === 'PRIME' || name === 'Study Hall') | ||
return <FlexiSCHEDAction {...props} /> | ||
|
||
if (name === 'Brunch' || name === 'Lunch') | ||
return <MenuAction {...props} /> | ||
|
||
return <></> | ||
} | ||
|
||
function FlexiSCHEDAction(props: PeriodActionButtonProps) { | ||
return ( | ||
<a href="https://gunn.flexisched.net" target="_blank" className="text-inherit"> | ||
<ActionButton {...props}> | ||
FlexiSCHED | ||
</ActionButton> | ||
</a> | ||
) | ||
} | ||
|
||
function MenuAction(props: PeriodActionButtonProps) { | ||
const { name, date } = props; | ||
const { menu } = useContext(MenuContext); | ||
const [modal, setModal] = useState(false); | ||
|
||
const formatted = date.toFormat('MM-dd'); | ||
const meal = name.toLowerCase() as 'brunch' | 'lunch'; | ||
|
||
if (formatted in menu && menu[formatted][meal]) | ||
return ( | ||
<> | ||
<ActionButton {...props} onClick={() => setModal(true)}> | ||
Menu | ||
</ActionButton> | ||
<MenuModal | ||
name={name} | ||
items={menu[formatted][meal]} | ||
isOpen={modal} | ||
setIsOpen={setModal} | ||
/> | ||
</> | ||
) | ||
|
||
return <></> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { useState } from 'react'; | ||
import { Dialog } from '@headlessui/react'; | ||
|
||
import NutritionModal from './NutritionModal'; | ||
import CenteredModal from '../layout/CenteredModal'; | ||
import { DangerOutlineButton } from '../layout/OutlineButton'; | ||
|
||
import type { Entry } from '../../contexts/MenuContext'; | ||
|
||
|
||
type MenuModalProps = { | ||
name: string, | ||
items: { [item: string]: Entry }, | ||
isOpen: boolean, | ||
setIsOpen: (open: boolean) => void | ||
} | ||
export default function MenuModal(props: MenuModalProps) { | ||
const { name, items, isOpen, setIsOpen } = props; | ||
const [nutritionModal, setNutritionModal] = useState<string | null>(null); | ||
|
||
return ( | ||
<CenteredModal className="relative flex flex-col bg-content rounded-md w-[28rem] max-h-[90%] mx-2 p-6 shadow-xl" isOpen={isOpen} setIsOpen={setIsOpen}> | ||
<Dialog.Title className="text-xl font-semibold mb-3 pr-6"> | ||
{name} Menu | ||
</Dialog.Title> | ||
|
||
<section className="mb-4 space-y-1 overflow-scroll scroll-smooth scrollbar-none"> | ||
{Object.entries(items).map(([item, nutrition]) => ( | ||
<div key={item}> | ||
<div | ||
className="truncate text-center cursor-pointer px-8 py-4 text-secondary rounded-md bg-black/10 dark:bg-black/20 hover:bg-black/20 dark:hover:bg-black/30 transition duration-75" | ||
onClick={() => setNutritionModal(item)} | ||
> | ||
{item} | ||
</div> | ||
<NutritionModal | ||
item={item} | ||
nutrition={nutrition} | ||
isOpen={(nutritionModal === item)} | ||
setIsOpen={() => setNutritionModal(null)} | ||
/> | ||
</div> | ||
))} | ||
</section> | ||
|
||
<section className="flex gap-3 flex-wrap justify-end"> | ||
<DangerOutlineButton onClick={() => setIsOpen(false)}> | ||
Close | ||
</DangerOutlineButton> | ||
</section> | ||
</CenteredModal> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import { ReactNode } from 'react'; | ||
import { Dialog } from '@headlessui/react'; | ||
|
||
import CenteredModal from '../layout/CenteredModal'; | ||
import { DangerOutlineButton } from '../layout/OutlineButton'; | ||
|
||
import type { Entry } from '../../contexts/MenuContext'; | ||
|
||
|
||
type ItemProps = { | ||
children: ReactNode, | ||
value?: number, | ||
dv?: number | ||
} | ||
function Item(props: ItemProps) { | ||
const { children, value, dv } = props; | ||
|
||
if (value === null) | ||
return <></> | ||
|
||
return ( | ||
<> | ||
<hr className="my-0.5 opacity-50" /> | ||
<div className="flex justify-between"> | ||
{children} | ||
{dv && <strong>{Math.floor((value! / dv) * 100)}%</strong>} | ||
</div> | ||
</> | ||
) | ||
} | ||
|
||
type NutritionModalProps = { | ||
item: string, | ||
nutrition: Entry, | ||
isOpen: boolean, | ||
setIsOpen: (open: boolean) => void | ||
} | ||
export default function NutritionModal(props: NutritionModalProps) { | ||
const { | ||
item, | ||
nutrition: { | ||
serving, | ||
nutrition, | ||
ingredients | ||
}, | ||
isOpen, | ||
setIsOpen | ||
} = props; | ||
|
||
return ( | ||
<CenteredModal className="relative flex flex-col bg-content rounded-md w-[28rem] max-h-[90%] mx-2 p-6 shadow-xl" isOpen={isOpen} setIsOpen={setIsOpen}> | ||
<Dialog.Title className="text-xl font-semibold mb-3 pr-6"> | ||
{item} | ||
</Dialog.Title> | ||
|
||
{nutrition ? ( | ||
<section className="mb-4 py-2 px-4 rounded-md overflow-scroll scroll-smooth scrollbar-none text-secondary bg-black/10 dark:bg-black/20"> | ||
<strong className="text-2xl">Nutrition Facts</strong> | ||
<hr className="my-0.5" /> | ||
{serving && serving.serving_size_amount && serving.serving_size_unit && ( | ||
<> | ||
<div className="flex justify-between space-x-8"> | ||
<strong>Serving size</strong> | ||
<strong>{serving.serving_size_amount} {serving.serving_size_unit}</strong> | ||
</div> | ||
<hr className="my-0.5" /> | ||
</> | ||
)} | ||
{nutrition.calories && ( | ||
<> | ||
<div className="flex justify-between text-xl"> | ||
<strong>Calories</strong> | ||
<strong>{nutrition.calories}</strong> | ||
</div> | ||
<hr className="my-0.5" /> | ||
</> | ||
)} | ||
<div className="flex justify-end"> | ||
<strong className="text-right">% Daily Value*</strong> | ||
</div> | ||
|
||
<Item value={nutrition.g_fat} dv={78}> | ||
<p><strong>Total Fat</strong> {nutrition.g_fat}g</p> | ||
</Item> | ||
<Item value={nutrition.g_saturated_fat} dv={20}> | ||
<p className="ml-4">Saturated Fat {nutrition.g_saturated_fat}g</p> | ||
</Item> | ||
<Item value={nutrition.g_trans_fat}> | ||
<p className="ml-4"><i>Trans</i> Fat {nutrition.g_trans_fat}g</p> | ||
</Item> | ||
|
||
<Item value={nutrition.mg_cholesterol} dv={300}> | ||
<p><strong>Cholesterol</strong> {nutrition.mg_cholesterol}mg</p> | ||
</Item> | ||
|
||
<Item value={nutrition.mg_sodium} dv={2300}> | ||
<p><strong>Sodium</strong> {nutrition.mg_sodium}mg</p> | ||
</Item> | ||
|
||
<Item value={nutrition.g_carbs} dv={275}> | ||
<p><strong>Total Carbohydrate</strong> {nutrition.g_carbs}g</p> | ||
</Item> | ||
<Item value={nutrition.g_fiber} dv={28}> | ||
<p className="ml-4">Dietary Fiber {nutrition.g_fiber}g</p> | ||
</Item> | ||
<Item value={nutrition.g_sugar}> | ||
<p className="ml-4">Total Sugars {nutrition.g_sugar}g</p> | ||
</Item> | ||
<Item value={nutrition.g_added_sugar} dv={50}> | ||
<p className="ml-8">Includes {nutrition.g_added_sugar}g Added Sugars</p> | ||
</Item> | ||
|
||
<Item value={nutrition.g_protein} dv={50}> | ||
<p><strong>Protein</strong> {nutrition.g_protein}g</p> | ||
</Item> | ||
|
||
<Item> | ||
<p className="text-sm font-light"> | ||
* Percent Daily Values are based on a 2,000 calorie diet. | ||
</p> | ||
</Item> | ||
|
||
{ingredients && ( | ||
<> | ||
<hr className="mt-0.5 mb-1" /> | ||
<strong className="text-2xl">Ingredients</strong> | ||
<p>{ingredients}</p> | ||
</> | ||
)} | ||
</section> | ||
) : ( | ||
<p>No nutrition information available.</p> | ||
)} | ||
|
||
<section className="flex gap-3 flex-wrap justify-end"> | ||
<DangerOutlineButton onClick={() => setIsOpen(false)}> | ||
Close | ||
</DangerOutlineButton> | ||
</section> | ||
</CenteredModal> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { createContext } from 'react'; | ||
|
||
|
||
export type Entry = { | ||
serving?: { | ||
serving_size_amount: string, | ||
serving_size_unit: string | ||
}, | ||
nutrition?: { | ||
calories?: number, | ||
g_fat?: number, | ||
g_saturated_fat?: number, | ||
g_trans_fat?: number, | ||
mg_cholesterol?: number, | ||
g_carbs?: number, | ||
g_added_sugar?: number, | ||
g_sugar?: number, | ||
mg_potassium?: number, | ||
mg_sodium?: number, | ||
g_fiber?: number, | ||
g_protein?: number, | ||
mg_iron?: number, | ||
mg_calcium?: number, | ||
mg_vitamin_c?: number, | ||
iu_vitamin_a?: number, | ||
re_vitamin_a?: number, | ||
mcg_vitamin_a?: number, | ||
mg_vitamin_d?: number, | ||
mcg_vitamin_d?: number, | ||
}, | ||
ingredients?: string | ||
} | ||
|
||
export type Menu = { | ||
timestamp: string, | ||
menu: { | ||
[date: string]: { | ||
brunch: { [item: string]: Entry }, | ||
lunch: { [item: string]: Entry } | ||
} | ||
} | ||
} | ||
|
||
export const defaultMenu: Menu = { | ||
timestamp: new Date().toISOString(), | ||
menu: {} | ||
} | ||
|
||
const MenuContext = createContext<Menu>(defaultMenu); | ||
|
||
export const MenuProvider = MenuContext.Provider; | ||
export default MenuContext; |
Oops, something went wrong.