Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLEAN-93 | Implement Caching with react-query #26

Open
wants to merge 8 commits into
base: CLEAN-74/MVP-for-showcase
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"run-py": "python manage.py runserver 0.0.0.0:8000"
},
"dependencies": {
"@tanstack/react-query": "^5.64.2",
"axios": "^1.7.7",
"dotenv": "^16.4.5",
"query-string": "^9.1.1",
Expand All @@ -29,6 +30,7 @@
},
"devDependencies": {
"@eslint/js": "^9.11.1",
"@tanstack/eslint-plugin-query": "^5.64.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
Expand Down
2 changes: 1 addition & 1 deletion src/api/dispenser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type DispenserItem = {
};

// Menu
export const getMenuItems = async (): Promise<DispenserItem[]> => {
export const fetchMenuItems = async (): Promise<DispenserItem[]> => {
const endpoint = `${baseUrl}/api/dispenser/menu-items`;

return axios
Expand Down
4 changes: 2 additions & 2 deletions src/components/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type ItemProps = {
name?: string;
price?: string;
description?: string;
imageUrl: string;
imageUrl?: string;
selection: string;
stateSelection: string;
setStateSelection: Dispatch<SetStateAction<string>>;
Expand All @@ -22,7 +22,7 @@ export const Item = ({
return (
<div
// eslint-disable-next-line tailwindcss/classnames-order
className={`shadow-item mx-8 flex h-[600px] w-[650px] flex-col rounded-3xl bg-white ${stateSelection ? (stateSelection === selection ? "outline outline-4 outline-green-600" : "opacity-60") : ""} `}
className={`mx-8 flex h-[600px] w-[650px] flex-col rounded-3xl bg-white shadow-item ${stateSelection ? (stateSelection === selection ? "outline outline-4 outline-green-600" : "opacity-60") : ""} `}
onClick={() => setStateSelection(selection)}
>
<img
Expand Down
31 changes: 23 additions & 8 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { DetectCupPage } from "./pages/DetectCupPage.tsx";
import { DispensingPage } from "./pages/DispensingPage.tsx";
import { IdlePage } from "./pages/IdlePage.tsx";
import { ThankYouPage } from "./pages/ThankYouPage.tsx";
import { QueryClient } from "@tanstack/react-query";
import { QueryClientProvider } from "@tanstack/react-query";

const router = createBrowserRouter([
{
Expand All @@ -37,21 +39,34 @@ const router = createBrowserRouter([
},
]);

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false, // Avoid refetching on focus
// staleTime: 1 * 60 * 1000, // 60 minutes stale time
staleTime: Infinity, // NOTE: To be tried. Cache will not be refreshed until the page is refreshed
gcTime: Infinity,
},
},
});

createRoot(document.getElementById("root")!).render(
// <StrictMode>
<PaperProvider>
<Fragment>
{Platform.OS === "web" ? (
<style type="text/css">{`
<QueryClientProvider client={queryClient}>
<PaperProvider>
<Fragment>
{Platform.OS === "web" ? (
<style type="text/css">{`
@font-face {
font-family: 'MaterialCommunityIcons';
src: url(${MaterialCommunityIconsFont}) format('truetype');
}
`}</style>
) : null}
) : null}

<RouterProvider router={router} />
</Fragment>
</PaperProvider>,
<RouterProvider router={router} />
</Fragment>
</PaperProvider>
</QueryClientProvider>,
// </StrictMode>,
);
24 changes: 9 additions & 15 deletions src/pages/DetectCupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,15 @@ export const DetectCupPage: React.FC = () => {
<Header {...DETECT_CUP_HEADER} />

<div className="flex h-full flex-col items-center">
{item === "Tap-A" ? (
<div
className="relative m-10 flex w-[650px]"
// onClick={() => navigate(`/${item}/${size}/dispense`)}
>
<img src="/media/tap.png" className="size-full" />
</div>
) : (
<div
className="relative m-10 flex w-[650px]"
// onClick={() => navigate(`/${item}/${size}/dispense`)}
>
<img src="/media/tap.png" className="size-full -scale-x-100" />
</div>
)}
<div
className="relative m-10 flex w-[650px]"
// onClick={() => navigate(`/${item}/${size}/dispense`)}
>
<img
src="/media/tap.png"
className={`size-full ${item === "Tap-B" && "-scale-x-100"}`} // Rotate image if on Tap B
/>
</div>
</div>

<div className="flex flex-col items-center gap-6">
Expand Down
27 changes: 9 additions & 18 deletions src/pages/DispensingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,24 +173,15 @@ export const DispensingPage = () => {
<Header {...DISPENSING_HEADER} />

<div className="flex h-full flex-col items-center">
{item === "Tap-A" ? (
<div
className="relative m-10 mb-20 flex w-[650px]"
// onClick={() => navigate(`/${item}/${size}/thank-you`)}
>
<img src="/media/pour.png" className="size-full object-contain" />
</div>
) : (
<div
className="relative m-10 mb-20 flex w-[650px]"
// onClick={() => navigate(`/${item}/${size}/thank-you`)}
>
<img
src="/media/pour.png"
className="size-full -scale-x-100 object-contain"
/>
</div>
)}
<div
className="relative m-10 mb-20 flex w-[650px]"
// onClick={() => navigate(`/${item}/${size}/thank-you`)}
>
<img
src="/media/pour.png"
className={`size-full object-contain ${item === "Tap-B" && "-scale-x-100"}`} // Rotate image if on Tap B
/>
</div>
</div>

<div className="flex flex-col items-center gap-6">
Expand Down
52 changes: 18 additions & 34 deletions src/pages/ItemSelectionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,43 @@ import {
import { Header } from "../components/Header";
import { Footer } from "../components/Footer";
import { Item } from "../components/Item";
// import { DispenserItem, getMenuItems } from "../api/dispenser";
import { useQuery } from "@tanstack/react-query";
import { fetchMenuItems } from "../api/dispenser";

// TAP_A and TAP_B values can be later configured
const ITEM_SELECTION_HEADER = {
title: "Please choose your drink",
};

const TAP_A = {
name: "Sesame Oat Latte (Cold)",
price: "HK$ 2.20",
description: "Sweet and addictive nougat-like oat drink",
imageUrl: "/media/drink-a.png",
};

const TAP_B = {
name: "Red Date & Ginger Tea (Hot)",
price: "HK$ 2.20",
description: "Warming and spicy tea, low sugar",
imageUrl: "/media/drink-b.png",
};

export const ItemSelectionPage: React.FC = () => {
// NOTE: Hardcode first since loading indicators are needed when pulling from BE
// useEffect(() => {
// const fetchMenuItems = async () => {
// const dispensers = await getMenuItems();
// setDispensers(dispensers);
// };

// fetchMenuItems();
// }, []);

// const [dispensers, setDispensers] = useState<DispenserItem[]>([]);
// const Tap_A = dispensers.find((dispenser) => dispenser.name === "Tap-A");
// const Tap_B = dispensers.find((dispenser) => dispenser.name === "Tap-B");
const [item, setItem] = useState<string>("");
const { data } = useQuery({
queryKey: ["menuItems"],
queryFn: fetchMenuItems,
});

// TODO: Add Skeleton loaders whenever the cache is reset, OR increase stale time and only change on refresh
const Tap_A = data?.find((dispenser) => dispenser.name === "Tap-A");
const Tap_B = data?.find((dispenser) => dispenser.name === "Tap-B");

return (
<div className="grid h-screen w-screen grid-rows-[12%,63%,25%]">
<Header {...ITEM_SELECTION_HEADER} />

<div className="flex h-full flex-row items-center justify-center">
<Item
{...TAP_A}
// price={`HK$ ${Tap_A?.price_small}`}
// imageUrl={Tap_A?.drink_image}
imageUrl={Tap_A?.drink_image}
price={`HK$ ${Tap_A?.price_small}`}
name={Tap_A?.drink_name}
description={Tap_A?.drink_name2} // Note: This can be renamed to description in the backend
stateSelection={item}
setStateSelection={setItem}
selection="Tap-A"
/>
<Item
{...TAP_B}
// price={`HK$ ${Tap_B?.price_small}`}
imageUrl={Tap_B?.drink_image}
price={`HK$ ${Tap_B?.price_small}`}
name={Tap_B?.drink_name}
description={Tap_B?.drink_name2}
stateSelection={item}
setStateSelection={setItem}
selection="Tap-B"
Expand Down
33 changes: 17 additions & 16 deletions src/pages/ItemSizePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,43 @@ import { Footer } from "../components/Footer";
import { useParams } from "react-router-dom";
import { useState } from "react";
import { SizeItem } from "../components/SizeItem";
import { useQuery } from "@tanstack/react-query";
import { fetchMenuItems } from "../api/dispenser";

const ITEM_SIZE_HEADER = {
title: "Please select the size of your drink",
};

const SIZE_A = {
name: "Small",
price: "HK$ 2.20",
size: "354 ml (12oz)",
imageUrl: "/media/small.png",
};

const SIZE_B = {
name: "Large",
price: "HK$ 3.00",
size: "473 ml (16oz)",
imageUrl: "/media/large.png",
};

export const ItemSizePage: React.FC = () => {
const { item } = useParams();
const [size, setSize] = useState<string>("");
const { data } = useQuery({
queryKey: ["menuItems"],
queryFn: fetchMenuItems,
});

// const [dispensers, setDispensers] = useState<DispenserItem[]>([]);
const Tap = data?.find((dispenser) => dispenser.name === item);

return (
<div className="grid h-screen w-screen grid-rows-[12%,63%,25%]">
<Header {...ITEM_SIZE_HEADER} />

<div className="flex h-full flex-row items-center justify-center">
<SizeItem
{...SIZE_A}
imageUrl="/media/small.png" // Note: A universal image can be added to this
name={"Small"} // Note: This needs to be added to the backend
price={`HK$ ${Tap?.price_small}`}
size="354 ml (12oz)" // NOTE: This needs to be added to the backend
stateSelection={size}
setStateSelection={setSize}
selection="Small"
/>
<SizeItem
{...SIZE_B}
imageUrl="/media/large.png" // Note: A universal image can be added to this
name={"Large"} // Note: This needs to be added to the backend
price={`HK$ ${Tap?.price_large}`}
size="473 ml (16oz)" // NOTE: This needs to be added to the backend
stateSelection={size}
setStateSelection={setSize}
selection="Large"
Expand Down
2 changes: 1 addition & 1 deletion src/pages/PaymentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export const PaymentPage = () => {
stateSelection={option}
setStateSelection={setOption}
containerStyles="bg-[#DB0011] !flex-row"
titleStyles="mt-0 text-white text-5xl"
titleStyles="!mt-0 text-white text-5xl"
/>
</div>
<Footer
Expand Down
Loading