diff --git a/package-lock.json b/package-lock.json index 725667c..0719ba9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@supabase/supabase-js": "^2.76.1", "@tanstack/react-query": "5.69.0", "lucide-react": "0.484.0", + "motion": "^12.23.24", "next": "15.2.4", "react": "19.0.0", "react-dom": "19.0.0" @@ -1268,6 +1269,7 @@ "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1327,6 +1329,7 @@ "integrity": "sha512-LPcw1yHD3ToaDEoljFEfQ9j2xShY367h7FZ1sq5NJT9I3yj4LHer1Xd1yRSOdYy9BpsrxU7R+eoDokChYM53lQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.28.0", "@typescript-eslint/types": "8.28.0", @@ -1490,6 +1493,7 @@ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2310,6 +2314,7 @@ "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -2371,6 +2376,7 @@ "integrity": "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -2768,6 +2774,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/framer-motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", + "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3961,6 +3994,47 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz", + "integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.23.24", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4375,6 +4449,7 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -4525,6 +4600,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4534,6 +4610,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.25.0" }, @@ -5283,6 +5360,7 @@ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5355,6 +5433,7 @@ "integrity": "sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.0.0", "@typescript-eslint/types": "8.0.0", diff --git a/package.json b/package.json index d7139c9..e80dc53 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@supabase/supabase-js": "^2.76.1", "@tanstack/react-query": "5.69.0", "lucide-react": "0.484.0", + "motion": "^12.23.24", "next": "15.2.4", "react": "19.0.0", "react-dom": "19.0.0" diff --git a/src/app/history/page.tsx b/src/app/history/page.tsx index d26ce11..b509e6f 100644 --- a/src/app/history/page.tsx +++ b/src/app/history/page.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { getMealHistory, deleteMeal } from "@/database/api/meals"; import { MealWithRecipe } from "@/types/database"; +import { motion } from "motion/react"; const HistoryPage = () => { const [mealsList, setMealsList] = useState([]); @@ -59,7 +60,13 @@ const HistoryPage = () => {

) : ( -
+ {mealsList.map((mealItem) => { const mealDate = new Date(mealItem.eaten_at); const formattedDate = mealDate.toLocaleDateString("en-US", { @@ -71,8 +78,11 @@ const HistoryPage = () => { }); return ( -
@@ -96,15 +106,15 @@ const HistoryPage = () => {
-
+ ); })} - + )} diff --git a/src/app/inventory/page.tsx b/src/app/inventory/page.tsx index bfb27f1..5f35be0 100644 --- a/src/app/inventory/page.tsx +++ b/src/app/inventory/page.tsx @@ -9,6 +9,7 @@ import { } from "@/database/api/ingredients"; import { Ingredient } from "@/types/database"; import { INGREDIENT_CATEGORIES, UNITS } from "@/types/database"; +import { motion } from "motion/react"; const InventoryPage = () => { const [ingredients, setIngredients] = useState([]); @@ -143,17 +144,24 @@ const InventoryPage = () => { {showForm && ( -
+

{editingId ? "Edit Ingredient" : "Add Ingredient"}

+
+
)}
@@ -273,7 +281,7 @@ const InventoryPage = () => { @@ -266,6 +267,7 @@ const CreateRecipePage = () => { }) } step="0.01" + min="0" className="border-cookcraft-olive w-24 rounded-2xl border-3 p-2 text-sm" /> setSortBy(e.target.value as "rating" | "time")} - className="border-cookcraft-olive rounded-2xl border-3 p-3" + className="border-cookcraft-olive hover:border-cookcraft-red cursor-pointer rounded-2xl border-3 p-3" > @@ -131,10 +132,17 @@ const RecipesPage = () => {

) : ( -
+ {filteredRecipes.map((recipe) => { const totalTime = (recipe.prep_time || 0) + (recipe.cook_time || 0); + return (
{ showMealForm === recipe.id ? null : recipe.id, ) } - className="bg-cookcraft-red hover:bg-cookcraft-yellow flex-1 rounded-2xl p-2 font-bold text-white transition-colors" + className="bg-cookcraft-red hover:bg-cookcraft-yellow flex-1 cursor-pointer rounded-2xl p-2 font-bold text-white transition-colors" > {showMealForm === recipe.id ? "Cancel" : "Log Meal"} @@ -237,7 +245,7 @@ const RecipesPage = () => { @@ -245,7 +253,7 @@ const RecipesPage = () => {
); })} -
+ )}
diff --git a/src/components/home/ChatBot.tsx b/src/components/home/ChatBot.tsx index 1450adf..70067a7 100644 --- a/src/components/home/ChatBot.tsx +++ b/src/components/home/ChatBot.tsx @@ -1,9 +1,33 @@ +"use client"; +import { useState, useEffect } from "react"; +import { motion } from "motion/react"; import Image from "next/image"; import logo from "@/public/home/cookCraftLogo.webp"; const ChatBot = () => { + const fullText = "Welcome to CookCraft!"; + const [displayedText, setDisplayedText] = useState(""); + + useEffect(() => { + let index = 0; + const interval = setInterval(() => { + setDisplayedText(fullText.slice(0, index + 1)); + index++; + if (index === fullText.length) { + clearInterval(interval); + } + }, 80); + return () => clearInterval(interval); + }, []); + return (
- Welcome to CookCraft! + + {displayedText} + Cook Craft Logo