diff --git a/package-lock.json b/package-lock.json index c943e81..b5da231 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.2.0", "dependencies": { "@heroicons/react": "^2.1.5", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-tooltip": "^1.1.3", "@remixicon/react": "^4.5.0", "@tremor/react": "^3.18.3", @@ -1396,6 +1398,42 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", + "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", @@ -1423,6 +1461,46 @@ } } }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", + "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", @@ -1441,6 +1519,29 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", + "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", @@ -3259,6 +3360,12 @@ "node": ">=6" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/dialog-polyfill": { "version": "0.4.10", "resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.4.10.tgz", @@ -4411,6 +4518,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -4850,6 +4966,15 @@ "node": ">=12" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -6509,6 +6634,53 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-remove-scroll": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", + "integrity": "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.6", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-smooth": { "version": "4.0.1", "resolved": "https://npm.sitewrench.com/react-smooth/-/react-smooth-4.0.1.tgz", @@ -6524,6 +6696,29 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://npm.sitewrench.com/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -7690,6 +7885,49 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", diff --git a/package.json b/package.json index 7a62e87..ab4b964 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ }, "dependencies": { "@heroicons/react": "^2.1.5", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-tooltip": "^1.1.3", "@remixicon/react": "^4.5.0", "@tremor/react": "^3.18.3", diff --git a/pages/api/foods.js b/pages/api/foods.js index 3e18cc7..4640e45 100644 --- a/pages/api/foods.js +++ b/pages/api/foods.js @@ -22,8 +22,19 @@ export default async function handler(req, res) { await client.connect(); const db = client.db(dbName); const collection = db.collection("foods"); - const food = await collection.find({ name: { $in: req.body } }).toArray(); + const newBody = req.body.map((f) => f.toLowerCase()); + const foods = await collection.find({ name: { $in: newBody } }).toArray(); client.close(); - return res.status(200).json(food); + req.body.forEach((food) => { + if (foods.findIndex((f) => f.name.toLowerCase() == food.toLowerCase()) == -1) { + foods.push({ + _id: `${Math.random() * 10000}`, + name: food.toLowerCase(), + rating: 0, + ratings: 0 + }) + } + }) + return res.status(200).json(foods); } } diff --git a/pages/api/meals.js b/pages/api/meals.js index 9d5e355..0e25a2a 100644 --- a/pages/api/meals.js +++ b/pages/api/meals.js @@ -1,19 +1,23 @@ import { MongoClient } from "mongodb"; import firebaseAdmin from "firebase-admin"; +import { revalidatePath } from "next/cache"; export default async function handler(req, res) { // Retrieving meals by date if (req.method == "GET") { + console.log(req.query.date); const client = new MongoClient(process.env.CAFMONGO); const dbName = process.env.CAFMONGO_DB; await client.connect(); const db = client.db(dbName); const collection = db.collection("menu"); const foods = await collection.findOne({ - date: req.query.date, + date: req.query.date }); client.close(); return res - .setHeader("Cache-Control", "max-age=7200, public") + .setHeader("Cache-Control", "no-cache, no-store, must-revalidate") + .setHeader("Pragma", "no-cache") + .setHeader("Expires", "0") .status(200) .json(foods); } else if (req.method == "POST") { @@ -74,7 +78,12 @@ export default async function handler(req, res) { ); await client.close(); await firebaseApp.delete(); - return res.status(200).json(result); + revalidatePath(`/api/meals?date=${req.body.date}`); + return res + .setHeader("Cache-Control", "no-cache, no-store, must-revalidate") + .setHeader("Pragma", "no-cache") + .setHeader("Expires", "0") + .status(200).json(result); } else { res.status(401).json({ error: "You are not a Caf App administrator.", diff --git a/src/app/admin/page.js b/src/app/admin/page.js index 0b6fc27..2086fb5 100644 --- a/src/app/admin/page.js +++ b/src/app/admin/page.js @@ -19,6 +19,7 @@ import { TableBody, TableCell, TextInput, + Callout, } from "@tremor/react"; import { useRouter } from "next/navigation"; import firebase from "firebase/compat/app"; @@ -33,7 +34,10 @@ import { UserMinusIcon, Cog6ToothIcon, MoonIcon, + ArrowDownIcon, + PencilIcon, } from "@heroicons/react/24/outline"; +import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle } from "../../components/TremorDrawer"; import { Calendar } from "../../components/TremorCalendar"; import { Tooltip } from "../../components/TremorTooltip"; import TimeInput from "../../components/TimeInput"; @@ -41,6 +45,8 @@ import { useEffect, useState } from "react"; import FoodList from "../../components/admin/FoodList"; import useFirebaseUser from "../../hooks/useFirebaseUser"; import useHasChanged from "../../hooks/useHasChanged"; +import { Label } from "../../components/TremorLabel"; +import { CardHeader } from "@nextui-org/react"; const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_APIKEY, authDomain: "login.thecaf.app", @@ -65,6 +71,13 @@ export default function Admin({ searchParams }) { const [mealtime, setMealtime] = useState(0); const [updated, setUpdated] = useState(null); const [mealFoods, setMealFoods] = useState([]); + const [exception, setException] = useState(null); + const [exceptionInputs, setExceptionInputs] = useState({ + name: "", + start: "", + end: "" + }) + const [exceptionFormOpen, setExceptionFormOpen] = useState(false); const datahasChanged = useHasChanged(data); @@ -80,6 +93,7 @@ export default function Admin({ searchParams }) { }, []); useEffect(() => { + console.log('Data change\n', data); if (!!data && mealtime != undefined) { fetch("/api/foods", { method: "POST", @@ -100,17 +114,23 @@ export default function Admin({ searchParams }) { }, [user]); useEffect(() => { + console.log("UPDATED", updated); if (user) { user.getIdToken().then((token) => { + console.log("Mutate"); mutate(async () => { await fetch("/api/meals", { method: "POST", + cache: 'no-store', headers: { "Content-Type": "application/json", "x-firebase-token": token, }, body: JSON.stringify(updated), }); + }, { + optimisticData: updated, + revalidate: true }); }); } @@ -178,7 +198,7 @@ export default function Admin({ searchParams }) { > Sign Out - + - + {!!data && } + : + <> + {!!data && +

{data.meals[mealtime].name} from {data.meals[mealtime].times}

+
} + + +

{exception.name} from {exception.start} - {exception.end}

+
+ + + + + } + { + if (!modalOpened) { + setExceptionFormOpen(false); + } + }} + > + + + Exception editor + {!!data && + {data.meals[mealtime].name} on {data.date} + } + + + + setExceptionInputs({ ...exceptionInputs, name: v })} /> +
+ + + setExceptionInputs({ ...exceptionInputs, start: e.target.value })} /> + setExceptionInputs({ ...exceptionInputs, end: e.target.value })} /> +
+ + Exceptions allow changing meal times and names on specific dates. These are designed to override regular meals, and if there is to be a permanent change in schedule or naming you should use the Recurring Mealtimes panel. + +
+ + + + + + +
+
); } diff --git a/src/components/FoodTable.js b/src/components/FoodTable.js index 35ffe40..55182d5 100644 --- a/src/components/FoodTable.js +++ b/src/components/FoodTable.js @@ -7,7 +7,6 @@ import { TableHeaderCell, TableBody, TableCell, - Badge, } from "@tremor/react"; import RatingBadge from "../components/RatingBadge"; @@ -27,7 +26,6 @@ export default function FoodTable({ Name Score - Ratings {showId && ID} {!!customLastColumn && ( {customLastColumnTitle} @@ -36,14 +34,11 @@ export default function FoodTable({ {foods.map((item) => ( - + {item.name} - - {item.ratings} - {showId && ( {item._id} diff --git a/src/components/RatingBadge.js b/src/components/RatingBadge.js index fb22817..5b88f21 100644 --- a/src/components/RatingBadge.js +++ b/src/components/RatingBadge.js @@ -16,12 +16,12 @@ export default function RatingBadge({ size={size} className={className} tooltip={ - ratingCount != undefined - ? `Based on ${ratingCount} rating${rating > 1 ? "s" : ""}` - : undefined + ratingCount === 0 + ? "No ratings yet" + : (ratingCount != undefined ? `Based on ${ratingCount} rating${rating > 1 ? "s" : ""}` : undefined) } > - {Number(rating).toFixed(decimalPoints)} + {rating != undefined ? Number(rating).toFixed(decimalPoints) : "--"} ); } diff --git a/src/components/TimeInput.js b/src/components/TimeInput.js index 425ae1a..6419f01 100644 --- a/src/components/TimeInput.js +++ b/src/components/TimeInput.js @@ -1,4 +1,4 @@ -const TimeInput = ({ value = "", onChange = () => {}, min = "", max = "" }) => { +const TimeInput = ({ value = "", onChange = () => { }, min = "", max = "" }) => { const convertTime12to24 = (time12h) => { const [time, modifier] = time12h.split(" "); let [hours, minutes] = time.split(":"); @@ -10,29 +10,26 @@ const TimeInput = ({ value = "", onChange = () => {}, min = "", max = "" }) => { } return ("0" + hours).slice(-2) + ":" + ("0" + minutes).slice(-2); }; - if (value.includes("AM") || value.includes("PM")) { - console.log("Converting Value"); - } return (
-
+
onChange(e)} diff --git a/src/components/TremorDrawer.tsx b/src/components/TremorDrawer.tsx new file mode 100644 index 0000000..87c78ae --- /dev/null +++ b/src/components/TremorDrawer.tsx @@ -0,0 +1,195 @@ +// Tremor Drawer [v0.0.1] + +import * as React from "react" +import * as DrawerPrimitives from "@radix-ui/react-dialog" +import { RiCloseLine } from "@remixicon/react" + +import { cx, focusRing } from "../lib/utils" + +import { Button } from "@tremor/react" + +const Drawer = ( + props: React.ComponentPropsWithoutRef, +) => { + return +} +Drawer.displayName = "Drawer" + +const DrawerTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + ) +}) +DrawerTrigger.displayName = "Drawer.Trigger" + +const DrawerClose = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + ) +}) +DrawerClose.displayName = "Drawer.Close" + +const DrawerPortal = DrawerPrimitives.Portal + +DrawerPortal.displayName = "DrawerPortal" + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, forwardedRef) => { + return ( + + ) +}) + +DrawerOverlay.displayName = "DrawerOverlay" + +const DrawerContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, forwardedRef) => { + return ( + + + + + + ) +}) + +DrawerContent.displayName = "DrawerContent" + +const DrawerHeader = React.forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef<"div"> +>(({ children, className, ...props }, ref) => { + return ( +
+
+ {children} +
+ + + +
+ ) +}) + +DrawerHeader.displayName = "Drawer.Header" + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, forwardedRef) => ( + +)) + +DrawerTitle.displayName = "DrawerTitle" + +const DrawerBody = React.forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef<"div"> +>(({ className, ...props }, ref) => { + return
+}) +DrawerBody.displayName = "Drawer.Body" + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, forwardedRef) => { + return ( + + ) +}) + +DrawerDescription.displayName = "DrawerDescription" + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( +
+ ) +} + +DrawerFooter.displayName = "DrawerFooter" + +export { + Drawer, + DrawerBody, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} \ No newline at end of file diff --git a/src/components/TremorLabel.tsx b/src/components/TremorLabel.tsx new file mode 100644 index 0000000..4e76b0f --- /dev/null +++ b/src/components/TremorLabel.tsx @@ -0,0 +1,38 @@ +// Tremor Label [v0.0.2] + +import React from "react" +import * as LabelPrimitives from "@radix-ui/react-label" + +import { cx } from "../lib/utils" + +interface LabelProps + extends React.ComponentPropsWithoutRef { + disabled?: boolean +} + +const Label = React.forwardRef< + React.ElementRef, + LabelProps +>(({ className, disabled, ...props }, forwardedRef) => ( + +)) + +Label.displayName = "Label" + +export { Label } \ No newline at end of file diff --git a/src/components/admin/FoodList.js b/src/components/admin/FoodList.js index 5747079..40b4037 100644 --- a/src/components/admin/FoodList.js +++ b/src/components/admin/FoodList.js @@ -1,6 +1,5 @@ import { useState, useEffect } from "react"; import FoodTable from "../../components/FoodTable"; -import useHasChanged from "../../hooks/useHasChanged"; import RatingBadge from "../../components/RatingBadge"; import { QuestionMarkCircleIcon, @@ -19,11 +18,13 @@ import { SelectItem, SearchSelect, SearchSelectItem, + Badge, } from "@tremor/react"; +import { Tooltip } from "../TremorTooltip"; + export default function FoodList({ foods, setFoods, - meal, setMeal, meals, heading, @@ -32,6 +33,10 @@ export default function FoodList({ const [searchFoods, setSearchFoods] = useState([]); const [selectedFood, setSelectedFood] = useState(""); + useEffect(() => { + console.log("FOODS\n", foods); + }, [foods]) + useEffect(() => { if (foodQuery) { fetch(`/api/foods?q=${encodeURIComponent(foodQuery)}`) @@ -44,6 +49,15 @@ export default function FoodList({ useEffect(() => { if (selectedFood) { + if (selectedFood === "create") { + setFoods([...foods, { + id: (Math.random() * 100000).toString(), + name: foodQuery.toLowerCase(), + rating: 0, + ratings: 0 + }]) + return; + } const f = [...foods, searchFoods.find((f) => f._id == selectedFood)]; setFoods(f); setSelectedFood(""); @@ -91,7 +105,7 @@ export default function FoodList({ .map((food) => ( ( ))} + {foodQuery.length > 2 && ( + Create new + )}>{foodQuery}} {foods.length > 0 ? ( @@ -113,24 +134,23 @@ export default function FoodList({ showId={false} showTitle={false} customLastColumn={(food) => ( - <> + - - )} + onClick={() => + setFoods(foods.filter((f) => f._id != food._id)) + } + /> + + ) + } customLastColumnTitle="" foods={foods} - > + > ) : ( )} - + ); } diff --git a/src/lib/ratingToColor.js b/src/lib/ratingToColor.js index eaea898..cecd14b 100644 --- a/src/lib/ratingToColor.js +++ b/src/lib/ratingToColor.js @@ -10,8 +10,10 @@ const ratingToColor = (ratingNumber) => { return "yellow"; } else if (ratingNumber > 2) { return "orange"; - } else { + } else if (ratingNumber > 0) { return "red"; + } else { + return "gray" } }; diff --git a/src/tabs/AverageOverTime.js b/src/tabs/AverageOverTime.js index e1cfeea..9a7f76c 100644 --- a/src/tabs/AverageOverTime.js +++ b/src/tabs/AverageOverTime.js @@ -33,12 +33,13 @@ import { import { useEffect, useState } from "react"; export default function AverageOverTime() { const [chartData, setChartData] = useState([]); - const [timeInterval, setTimeInterval] = useState("7"); + const [timeInterval, setTimeInterval] = useState("1"); const [numOfDataPoints, setNumOfDataPoints] = useState(20); const [selectedDot, setSelectedDot] = useState(null); const [categories, setCategories] = useState(["Meal Rating"]); const [dayData, setDayData] = useState(null); const [actualScale, setActualScale] = useState("no"); + const [initialLoad, setInitialLoad] = useState(true); useEffect(() => { if (selectedDot?.eventType == "dot" && selectedDot?.date) { @@ -54,8 +55,9 @@ export default function AverageOverTime() { }, [selectedDot]); const loadChart = () => { + console.log("LOADCHART CALLED"); let promiseArr = []; - for (let i = numOfDataPoints * timeInterval; i > 0; i -= timeInterval) { + for (let i = numOfDataPoints * timeInterval; i > 0 - 1; i -= timeInterval) { promiseArr.push(fetch(`/api/average?offset=${i}`)); } Promise.all(promiseArr).then((resArray) => { @@ -83,19 +85,28 @@ export default function AverageOverTime() { console.log(""); }); }); + setInitialLoad(false); }); }; useEffect(() => { + if (initialLoad) { + return; + } if (numOfDataPoints > 2 && numOfDataPoints < 61) { setChartData([]); - setTimeout(loadChart, 1000); + setTimeout(loadChart, 500); } }, [timeInterval, numOfDataPoints]); useEffect(() => { - console.log(chartData); - }, [chartData]); + setChartData([]); + setTimeout(loadChart, 500); + }, []); + + // useEffect(() => { + // console.log(chartData); + // }, [chartData]); const computeMealAvg = (ratingArray) => { let sum = 0, @@ -201,7 +212,7 @@ export default function AverageOverTime() { className="self-start" > Meals on {dayData.date.replaceAll("-", "/")} - {} + { } {Object.keys(dayData.meals).map((key) => ( @@ -214,7 +225,7 @@ export default function AverageOverTime() { className="ml-2" color={ computeMealAvg(dayData.meals[key].mealRatings) == - 0 && "red" + 0 && "red" } > {computeMealAvg(dayData.meals[key].mealRatings)} diff --git a/tailwind.config.js b/tailwind.config.js index 6411177..299794c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -121,6 +121,15 @@ module.exports = { from: { opacity: "0", transform: "translateX(-6px)" }, to: { opacity: "1", transform: "translateX(0)" }, }, + drawerSlideLeftAndFade: { + from: { opacity: "0", transform: "translateX(100%)" }, + to: { opacity: "1", transform: "translateX(0)" }, + }, + drawerSlideRightAndFade: { + from: { opacity: "1", transform: "translateX(0)" }, + to: { opacity: "0", transform: "translateX(100%)" }, + }, + }, animation: { hide: "hide 150ms cubic-bezier(0.16, 1, 0.3, 1)", @@ -131,6 +140,11 @@ module.exports = { slideUpAndFade: "slideUpAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", slideRightAndFade: "slideRightAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", + // Drawer + drawerSlideLeftAndFade: + "drawerSlideLeftAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", + drawerSlideRightAndFade: "drawerSlideRightAndFade 150ms ease-in", + }, },