diff --git a/src/client/CookingAppReact/src/components/auth/SignOutButton.jsx b/src/client/CookingAppReact/src/components/auth/SignOutButton.jsx index 5dd8c756..a423475c 100644 --- a/src/client/CookingAppReact/src/components/auth/SignOutButton.jsx +++ b/src/client/CookingAppReact/src/components/auth/SignOutButton.jsx @@ -1,11 +1,13 @@ "use client"; import { handleLogout } from "@/msal/msal"; import { FaSignOutAlt } from "react-icons/fa"; +import { useTranslation } from "react-i18next"; import { ArrowRightStartOnRectangleIcon } from "@heroicons/react/24/outline"; // import { useTheme } from "next-themes"; const SignOutButton = () => { + const { i18n, t } = useTranslation(); // const { theme } = useTheme(); // const isDarkTheme = theme === 'dark'; @@ -20,7 +22,7 @@ const SignOutButton = () => { // color={isDarkTheme ? "white" : "black"} /> Sign Out + className={`text-primaryText`}>{t("SignOut")} ); }; diff --git a/src/client/CookingAppReact/src/components/chat/BotResponse.jsx b/src/client/CookingAppReact/src/components/chat/BotResponse.jsx index 539927ce..9a76b540 100644 --- a/src/client/CookingAppReact/src/components/chat/BotResponse.jsx +++ b/src/client/CookingAppReact/src/components/chat/BotResponse.jsx @@ -9,22 +9,36 @@ import { useEffect } from "react"; import { uiActions } from "@/store/uiSlice"; import { useTranslation } from "react-i18next"; import { useGeneration } from "@/utils/generationProvider"; +import toast from "react-hot-toast"; + export default function BotResponse({ message }) { const language = useSelector((state) => state.ui.lang); const limitations = useSelector((state) => state.user.role.limitations); - const { isGenerating, setIsGenerating } = useGeneration(); const { i18n, t } = useTranslation(); const role = useSelector((state) => state.user.role.type); const navigate = useNavigate(); const { save, isError, isPending, error, isSuccess } = useSaveRecipe(); + const { isGenerating, setIsGenerating, lastTimestamp, maxDuration } = useGeneration(); async function handleClick() { + const timeElapsed = Date.now() - lastTimestamp; + const isCooldown = timeElapsed < maxDuration; const token = await getToken(); - if (!isPending && !isGenerating) { + + if (isGenerating && isCooldown) { + const timeRemaining = maxDuration - timeElapsed; + const minutes = Math.floor(timeRemaining / 60000); + const seconds = Math.floor((timeRemaining % 60000) / 1000); + toast.error(`Meal generation cooldown - ${minutes}:${seconds} minutes.`); + return; + } else if(!isPending && !isGenerating){ setIsGenerating(true); save({ token, request: message.content }); + } else{ + setIsGenerating(false); } } + function handleFreeUser() { navigate("/subscription"); } @@ -50,8 +64,7 @@ export default function BotResponse({ message }) { isPending ? "border-dance animate-border-dance" : "hover:scale-105 transition-transform duration-300" - } - relative ${isPending && "sparkle"}`} + } `} onClick={handleClick} > diff --git a/src/client/CookingAppReact/src/components/chat/Thinking.jsx b/src/client/CookingAppReact/src/components/chat/Thinking.jsx index 1c36cb55..66318584 100644 --- a/src/client/CookingAppReact/src/components/chat/Thinking.jsx +++ b/src/client/CookingAppReact/src/components/chat/Thinking.jsx @@ -20,12 +20,12 @@ const Thinking = () => { /> - - - + + + diff --git a/src/client/CookingAppReact/src/components/recipes/MyRecipes.jsx b/src/client/CookingAppReact/src/components/recipes/MyRecipes.jsx index 640e82fb..ef9f846f 100644 --- a/src/client/CookingAppReact/src/components/recipes/MyRecipes.jsx +++ b/src/client/CookingAppReact/src/components/recipes/MyRecipes.jsx @@ -109,7 +109,7 @@ export default function MyRecipes() { onClick={handleRecipes} /> -

{t("MyMeals")}

+

{t("MyMeals")}

{t("LoadMore")} - . - . - . + . + . + . )} diff --git a/src/client/CookingAppReact/src/hooks/useSaveRecipe.js b/src/client/CookingAppReact/src/hooks/useSaveRecipe.js index 0632c2f9..195e5fdf 100644 --- a/src/client/CookingAppReact/src/hooks/useSaveRecipe.js +++ b/src/client/CookingAppReact/src/hooks/useSaveRecipe.js @@ -30,19 +30,19 @@ const useSaveRecipe = () => { mutationFn: createRecipe, onMutate: () => { setIsGenerating(true); - dispatch(userActions.reduceRecipeGeneration()); }, onSuccess: async (response) => { + setIsGenerating(false); + dispatch(userActions.reduceRecipeGeneration()); dispatch(uiActions.showToast(response)); const token = await getToken(); const decoded = jwtDecode(token); console.log("trigerring"); getFirstPageRecipes({ token: token, page: 1, userId: decoded.sub }); - setIsGenerating(false); }, onError: (error) => { - dispatch(uiActions.setResponseError(error.message)); setIsGenerating(false); + dispatch(uiActions.setResponseError(error.message)); }, }); diff --git a/src/client/CookingAppReact/src/i18n/config.js b/src/client/CookingAppReact/src/i18n/config.js index 1d66a666..4114f6c2 100644 --- a/src/client/CookingAppReact/src/i18n/config.js +++ b/src/client/CookingAppReact/src/i18n/config.js @@ -59,7 +59,7 @@ i18n.use(initReactI18next).init({ CopyUserId: "Copy User Id", Profile: "Profile", WhatToCook: "What do you want to cook today?", - LookingForFav: "Looking for your favourite recipe?", + LookingForFav: "Looking for your favourite meal?", AddAllergens: "Add your allergens", AddDisliked: "Add your disliked foods" }, diff --git a/src/client/CookingAppReact/src/index.css b/src/client/CookingAppReact/src/index.css index ba25827a..5ead4b1b 100644 --- a/src/client/CookingAppReact/src/index.css +++ b/src/client/CookingAppReact/src/index.css @@ -15,8 +15,8 @@ ::-webkit-scrollbar-thumb { background: linear-gradient( 135deg, - #a0a0a0, - #606060 + #808080, + #808080 ); /* Gradient thumb for a cool effect */ border-radius: 10px; /* Rounded corners for the thumb */ } @@ -25,7 +25,7 @@ background: linear-gradient( 135deg, #808080, - #404040 + #808080 ); /* Darker gradient on hover */ } .light { diff --git a/src/client/CookingAppReact/src/pages/chat/Chat.jsx b/src/client/CookingAppReact/src/pages/chat/Chat.jsx index 6ea77981..0620d593 100644 --- a/src/client/CookingAppReact/src/pages/chat/Chat.jsx +++ b/src/client/CookingAppReact/src/pages/chat/Chat.jsx @@ -53,7 +53,7 @@ export default function Chat() { return (
    *:last-child]:pb-5 + className={`flex flex-col gap-14 justify-start items-center [&>*:last-child]:pb-1 ${ isOpenRecipes || isOpenSideBar ? "w-5/5 md:w-5/5 xl:w-4/5" diff --git a/src/client/CookingAppReact/src/pages/subscribtion/SubscriptionDetails.jsx b/src/client/CookingAppReact/src/pages/subscribtion/SubscriptionDetails.jsx index 178169e3..2b626a9a 100644 --- a/src/client/CookingAppReact/src/pages/subscribtion/SubscriptionDetails.jsx +++ b/src/client/CookingAppReact/src/pages/subscribtion/SubscriptionDetails.jsx @@ -18,8 +18,8 @@ export default function SubscriptionDetails() { function formatDateIso(dateString) { const date = new Date(dateString); - - // Adjust for time zone offset + + // Define options for formatting the date const options = { year: "numeric", month: "long", @@ -27,11 +27,10 @@ export default function SubscriptionDetails() { hour: "2-digit", minute: "2-digit", second: "2-digit", - timeZone: "UTC", // Ensure the date is interpreted as UTC hour12: true, // Use 12-hour time format }; - - // Format date + + // Format the date according to the user's local timezone return new Intl.DateTimeFormat("en-US", options).format(date); } @@ -68,7 +67,7 @@ export default function SubscriptionDetails() { ) : (
  • - {`Your next charge will be on ${data.subscriptions[0].currentPeriodEnd}`} + {`Your next charge will be on ${formatDateIso(data.subscriptions[0].currentPeriodEnd)}`}
  • )}
diff --git a/src/client/CookingAppReact/src/utils/generationProvider.jsx b/src/client/CookingAppReact/src/utils/generationProvider.jsx index 57e1f824..d8ab5f6e 100644 --- a/src/client/CookingAppReact/src/utils/generationProvider.jsx +++ b/src/client/CookingAppReact/src/utils/generationProvider.jsx @@ -1,21 +1,36 @@ import React, { createContext, useContext, useState, useEffect } from "react"; +import toast from "react-hot-toast"; const GenerationContext = createContext(); +const calculateElapsed = (lastTimestamp, maxDuration) => { + const now = Date.now(); + const timeElapsed = now - lastTimestamp; + return timeElapsed < maxDuration; +}; + export const GenerationProvider = ({ children }) => { + const maxDuration = 2 * 60 * 1000; // 2 minutes in milliseconds + const [isGenerating, setIsGenerating] = useState(() => { - // Retrieve the initial state from local storage const savedState = localStorage.getItem('isGenerating'); - return savedState ? JSON.parse(savedState) : false; + const lastTimestamp = JSON.parse(localStorage.getItem('generationTimestamp')); + const isElapsed = lastTimestamp ? calculateElapsed(lastTimestamp, maxDuration) : false; + return savedState && isElapsed ? JSON.parse(savedState) : false; }); + const lastTimestamp = JSON.parse(localStorage.getItem('generationTimestamp')) || null; + const isElapsed = lastTimestamp ? calculateElapsed(lastTimestamp, maxDuration) : false; + useEffect(() => { - // Save the state to local storage whenever it changes localStorage.setItem('isGenerating', JSON.stringify(isGenerating)); + if (isGenerating) { + localStorage.setItem('generationTimestamp', JSON.stringify(Date.now())); + } }, [isGenerating]); return ( - + {children} ); diff --git a/src/server/CookingApp/Properties/launchSettings.json b/src/server/CookingApp/Properties/launchSettings.json index 53febc9a..1f9e41a0 100644 --- a/src/server/CookingApp/Properties/launchSettings.json +++ b/src/server/CookingApp/Properties/launchSettings.json @@ -8,7 +8,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "http://192.168.100.221:8000" + "applicationUrl": "http://192.168.0.103:8000" }, "https": { "commandName": "Project",