Repository: boxwise/boxtribute
Pull request #2049: Cache reconciliation form input values as default values on subsequent usages
Author: login: fhenrich33
Created: 2025-03-26T13:11:48Z | Merged: 2025-03-31T09:07:55Z

![firefox_cJ4XbuTKJx](https://github.com/user-attachments/assets/8731e36a-a9a4-490e-a8b6-9bda486f9aaa)



Diff:
diff --git a/front/src/components/BoxReconciliationOverlay/components/BoxReconciliationAccordion.tsx b/front/src/components/BoxReconciliationOverlay/components/BoxReconciliationAccordion.tsx
index ecf4959001..f225d407b6 100644
--- a/front/src/components/BoxReconciliationOverlay/components/BoxReconciliationAccordion.tsx
+++ b/front/src/components/BoxReconciliationOverlay/components/BoxReconciliationAccordion.tsx
@@ -15,6 +15,11 @@ import { RiQuestionFill } from "react-icons/ri";
 import { ILocationData, IProductWithSizeRangeData } from "./BoxReconciliationView";
 import { IMatchProductsFormData, MatchProductsForm } from "./MatchProductsForm";
 import { IReceiveLocationFormData, ReceiveLocationForm } from "./ReceiveLocationForm";
+import { useAtom, useSetAtom } from "jotai";
+import {
+  reconciliationMatchProductAtom,
+  reconciliationReceiveLocationAtom,
+} from "stores/globalCacheStore";
 import { ShipmentDetailWithAutomatchProduct } from "queries/types";
 
 interface IBoxReconcilationAccordionProps {
@@ -59,8 +64,12 @@ export function BoxReconcilationAccordion({
       ? parseInt(shipmentDetail.autoMatchingTargetProduct?.id ?? "0")
       : undefined,
     sizeId: isProductAutoMatched ? parseInt(shipmentDetail.sourceSize?.id ?? "0") : undefined,
-    numberOfItems: isProductAutoMatched ? (shipmentDetail.sourceQuantity ?? 0) : undefined,
+    numberOfItems: shipmentDetail?.sourceQuantity ?? undefined,
   });
+  const [reconciliationMatchProductCache, setReconciliationMatchProductCache] = useAtom(
+    reconciliationMatchProductAtom,
+  );
+  const setReconciliationReceiveLocationCache = useSetAtom(reconciliationReceiveLocationAtom);
   const accordionHeaderColor = isProductAutoMatched || productManuallyMatched ? "#659A7E" : "#000";
   const accordionHeaderText = productManuallyMatched
     ? "PRODUCTS DELIVERED"
@@ -113,6 +122,13 @@ export function BoxReconcilationAccordion({
             onSubmitMatchProductsForm={(matchedProductsFormData: IMatchProductsFormData) => {
               setProductManuallyMatched(true);
               setAccordionIndex(1);
+
+              if (shipmentDetail.sourceProduct?.id) {
+                reconciliationMatchProductCache[shipmentDetail.sourceProduct.id] =
+                  matchedProductsFormData;
+                setReconciliationMatchProductCache(reconciliationMatchProductCache);
+              }
+
               setProductFormData({
                 sizeId: parseInt(matchedProductsFormData.sizeId.value, 10),
                 productId: parseInt(matchedProductsFormData.productId.value, 10),
@@ -146,6 +162,7 @@ export function BoxReconcilationAccordion({
             onSubmitReceiveLocationForm={(receiveLocationFormData: IReceiveLocationFormData) => {
               setLocationSpecified(true);
               setAccordionIndex(-1);
+              setReconciliationReceiveLocationCache(receiveLocationFormData);
 
               onBoxDelivered(
                 shipmentDetail.box.labelIdentifier,
diff --git a/front/src/components/BoxReconciliationOverlay/components/MatchProductsForm.tsx b/front/src/components/BoxReconciliationOverlay/components/MatchProductsForm.tsx
index 82efb95243..a5f6b889e6 100644
--- a/front/src/components/BoxReconciliationOverlay/components/MatchProductsForm.tsx
+++ b/front/src/components/BoxReconciliationOverlay/components/MatchProductsForm.tsx
@@ -1,5 +1,6 @@
 import { useEffect, useRef, useState } from "react";
 import { Box, Button, Flex, Text, Wrap, WrapItem } from "@chakra-ui/react";
+import { useAtomValue } from "jotai";
 import { z } from "zod";
 import { zodResolver } from "@hookform/resolvers/zod";
 import { groupBy } from "lodash";
@@ -9,6 +10,7 @@ import { BsFillCheckCircleFill } from "react-icons/bs";
 import { IProductWithSizeRangeData } from "./BoxReconciliationView";
 import NumberField from "components/Form/NumberField";
 import SelectField, { IDropdownOption } from "components/Form/SelectField";
+import { reconciliationMatchProductAtom } from "stores/globalCacheStore";
 import { ShipmentDetailWithAutomatchProduct } from "queries/types";
 
 export interface ICategoryData {
@@ -73,17 +75,31 @@ export function MatchProductsForm({
   onBoxUndelivered,
 }: IMatchProductsFormProps) {
   const isProductAutoMatched = !!shipmentDetail?.autoMatchingTargetProduct;
-  // default Values
+  const cachedReconciliationMatchProduct = useAtomValue(reconciliationMatchProductAtom);
+  /** Matching Source Product ID to look up a matching product in the cache store to prefill the form input. */
+  const matchingProductSourceId = (shipmentDetail.sourceProduct?.id as `${number}`) || "0";
+  const isProductIdMatchedInCache = !!cachedReconciliationMatchProduct[matchingProductSourceId];
+  /** Object key to match in the store to fetch the input values. */
+  const cacheId = isProductIdMatchedInCache ? matchingProductSourceId : "0";
+
+  // Default Values
+  // Automatched Products take precedence over cached products.
   const defaultValues: IMatchProductsFormData = {
     productId: {
-      // TODO: Handle caching when https://github.com/boxwise/boxtribute/pull/2049 is merged
-      label: shipmentDetail?.autoMatchingTargetProduct?.name ?? "Save Product As...",
-      value: shipmentDetail?.autoMatchingTargetProduct?.id ?? "",
+      label:
+        shipmentDetail?.autoMatchingTargetProduct?.name ??
+        cachedReconciliationMatchProduct[cacheId]?.productId.label,
+      value:
+        shipmentDetail?.autoMatchingTargetProduct?.id ??
+        cachedReconciliationMatchProduct[cacheId]?.productId.value,
     },
     sizeId: {
-      // TODO: Handle caching when https://github.com/boxwise/boxtribute/pull/2049 is merged
-      label: isProductAutoMatched ? (shipmentDetail.sourceSize?.label ?? "") : "Save Size As...",
-      value: isProductAutoMatched ? (shipmentDetail.sourceSize?.id ?? "") : "",
+      label: isProductAutoMatched
+        ? (shipmentDetail.sourceSize?.label ?? "")
+        : cachedReconciliationMatchProduct[cacheId]?.sizeId.label,
+      value: isProductAutoMatched
+        ? (shipmentDetail.sourceSize?.id ?? "")
+        : cachedReconciliationMatchProduct[cacheId]?.sizeId.value,
     },
     numberOfItems: shipmentDetail?.sourceQuantity ?? 0,
   };
@@ -133,12 +149,20 @@ export function MatchProductsForm({
         // if there is only one option select it directly
         if (prepSizesOptionsForCurrentProduct.length === 1) {
           resetField("sizeId", { defaultValue: prepSizesOptionsForCurrentProduct[0] });
+        } else if (cachedReconciliationMatchProduct[matchingProductSourceId]?.sizeId?.value) {
+          return;
         } else {
           resetField("sizeId", { defaultValue: { value: "", label: "Save Size As..." } });
         }
       }
     }
-  }, [productId, productAndSizesData, resetField]);
+  }, [
+    productId,
+    productAndSizesData,
+    resetField,
+    cachedReconciliationMatchProduct,
+    matchingProductSourceId,
+  ]);
 
   // Option Preparations for select fields
   const productsGroupedByCategory: Record<string, IProductWithSizeRangeData[]> = groupBy(
@@ -192,7 +216,10 @@ export function MatchProductsForm({
             errors={errors}
             control={control}
           />
-          <BsFillCheckCircleFill color={productId?.value !== "" ? "#659A7E" : "#fff"} size={18} />
+          <BsFillCheckCircleFill
+            color={control.getFieldState("productId").isDirty ? "#659A7E" : "#fff"}
+            size={18}
+          />
         </Flex>
         <Box>
           <Text fontSize={16} fontWeight="bold">
@@ -219,7 +246,10 @@ export function MatchProductsForm({
             errors={errors}
             control={control}
           />
-          <BsFillCheckCircleFill color={sizeId?.value !== "" ? "#659A7E" : "#fff"} size={18} />
+          <BsFillCheckCircleFill
+            color={control.getFieldState("sizeId").isDirty ? "#659A7E" : "#fff"}
+            size={18}
+          />
         </Flex>
         <Flex alignContent="center" alignItems="center">
           <Wrap alignItems="center">
diff --git a/front/src/components/BoxReconciliationOverlay/components/ReceiveLocationForm.tsx b/front/src/components/BoxReconciliationOverlay/components/ReceiveLocationForm.tsx
index fd3b4a81f7..f198d60401 100644
--- a/front/src/components/BoxReconciliationOverlay/components/ReceiveLocationForm.tsx
+++ b/front/src/components/BoxReconciliationOverlay/components/ReceiveLocationForm.tsx
@@ -7,6 +7,8 @@ import { BsFillCheckCircleFill } from "react-icons/bs";
 import { FaWarehouse } from "react-icons/fa";
 import { z } from "zod";
 import { ILocationData } from "./BoxReconciliationView";
+import { useAtomValue } from "jotai";
+import { reconciliationReceiveLocationAtom } from "stores/globalCacheStore";
 
 // Definitions for form validation with zod
 
@@ -42,9 +44,14 @@ export function ReceiveLocationForm({
     value: location.id,
   }));
 
+  const cachedReconciliationReceiveLocation = useAtomValue(reconciliationReceiveLocationAtom);
+
   // Form Default Values
   const defaultValues: IReceiveLocationFormData = {
-    locationId: { label: "Select Location", value: "" },
+    locationId: {
+      label: cachedReconciliationReceiveLocation.locationId.label,
+      value: cachedReconciliationReceiveLocation.locationId.value,
+    },
   };
 
   // react-hook-form
@@ -61,10 +68,14 @@ export function ReceiveLocationForm({
   const locationId = watch("locationId");
 
   useEffect(() => {
-    if (locationId != null && locationId.value !== "") {
+    if (
+      locationId != null &&
+      locationId.value !== "" &&
+      control.getFieldState("locationId").isDirty
+    ) {
       onLocationSpecified(true);
     }
-  }, [locationId, onLocationSpecified]);
+  }, [control, locationId, onLocationSpecified]);
 
   return (
     <form onSubmit={handleSubmit(onSubmitReceiveLocationForm)}>
@@ -81,7 +92,10 @@ export function ReceiveLocationForm({
             errors={errors}
             control={control}
           />
-          <BsFillCheckCircleFill color={locationId?.value !== "" ? "#659A7E" : "#fff"} size={18} />
+          <BsFillCheckCircleFill
+            color={control.getFieldState("locationId").isDirty ? "#659A7E" : "#fff"}
+            size={18}
+          />
         </Flex>
 
         <Flex alignContent="center" alignItems="center">
diff --git a/front/src/hooks/hooks.ts b/front/src/hooks/hooks.ts
index c8b5436564..5cccd2e5d3 100644
--- a/front/src/hooks/hooks.ts
+++ b/front/src/hooks/hooks.ts
@@ -12,6 +12,10 @@ export const useHandleLogout = () => {
   const { user, logout } = useAuth0();
 
   const handleLogout = () => {
+    // Clear reconciliation view form input cache.
+    localStorage.removeItem("reconciliationMatchProduct");
+    localStorage.removeItem("reconciliationReceiveLocation");
+
     // only redirect in staging and production environments
     if (import.meta.env.FRONT_ENVIRONMENT !== "development") {
       window.location.href = `${import.meta.env.FRONT_OLD_APP_BASE_URL}/index.php?action=logoutfromv2`;
diff --git a/front/src/stores/globalCacheStore.ts b/front/src/stores/globalCacheStore.ts
new file mode 100644
index 0000000000..0908ba7ab5
--- /dev/null
+++ b/front/src/stores/globalCacheStore.ts
@@ -0,0 +1,39 @@
+import { IMatchProductsFormData } from "components/BoxReconciliationOverlay/components/MatchProductsForm";
+import { IReceiveLocationFormData } from "components/BoxReconciliationOverlay/components/ReceiveLocationForm";
+import { atomWithStorage } from "jotai/utils";
+
+type ReconciliationMatchProductFields = Record<
+  `${number}`,
+  Pick<IMatchProductsFormData, "productId" | "sizeId">
+>;
+
+/** Persisted atom to cache the value for the Match Product form input in the Box Reconciliation View. */
+export const reconciliationMatchProductAtom = atomWithStorage<ReconciliationMatchProductFields>(
+  "reconciliationMatchProduct",
+  {
+    // Default form values.
+    "0": {
+      productId: {
+        label: "Save Product As...",
+        value: "",
+      },
+      sizeId: { label: "Save Size As...", value: "" },
+    },
+  },
+  undefined,
+  { getOnInit: true },
+);
+
+/** Persisted atom to cache the value for the Receive Location form input in the Box Reconciliation View. */
+export const reconciliationReceiveLocationAtom = atomWithStorage<IReceiveLocationFormData>(
+  "reconciliationReceiveLocation",
+  {
+    // Default form values.
+    locationId: {
+      label: "Select Location",
+      value: "",
+    },
+  },
+  undefined,
+  { getOnInit: true },
+);
diff --git a/front/src/stores/globalPreferenceStore.ts b/front/src/stores/globalPreferenceStore.ts
index 728bbbae6e..5fadb4df65 100644
--- a/front/src/stores/globalPreferenceStore.ts
+++ b/front/src/stores/globalPreferenceStore.ts
@@ -10,13 +10,13 @@ type IdOptionalNameTuple = {
   name?: string;
 };
 
-// the base name is only optional since it is also set through auth0 which only include ids
+/** The base name is only optional since it is also set through auth0 which only include ids. */
 export const selectedBaseAtom = atom<IdOptionalNameTuple>();
-// the base names are only optional since it is also set through auth0 which only include ids
+/** The base names are only optional since it is also set through auth0 which only include ids. */
 export const availableBasesAtom = atom<IdOptionalNameTuple[]>([]);
 export const organisationAtom = atom<IdNameTuple>();
 
-// read only atom to get the selected base id
+/** Read only atom to get the selected base id. */
 export const selectedBaseIdAtom = atom((get) => {
   const selectedBase = get(selectedBaseAtom) ?? { id: undefined };
   // return the stored selected base id if it exists


Reviews:
user: login: HaGuesto
body: ## functional review
- the default value of the product should be taken from the cache depending on the sourceProductId
- if location is set and cached it should only be the default value of the select box for location and not directly switch to the state 
![image](https://github.com/user-attachments/assets/0f4fc02f-a2f4-4f8b-b806-af48ccf6be1c)



state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T16:29:22Z
user: login: HaGuesto
body: thanks @fhenrich33 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-31T07:56:34Z

Review comments:
diff_hunk: @@ -26,3 +31,30 @@ export const selectedBaseIdAtom = atom((get) => {
   // This case should not happen except on initial render
   return "0";
 });
+
+/** Persisted atom to cache the value for the Match Product form input in the Box Reconciliation View. */
+export const reconciliationMatchProductAtom = atomWithStorage<ReconciliationMatchProductFields>(
path: front/src/stores/globalPreferenceStore.ts
user: login: HaGuesto
body: move please in a separate store file since it has nothing to do with gloable preferences
created_at: 2025-03-26T16:13:13Z
author_association: MEMBER
diff_hunk: @@ -26,3 +31,30 @@ export const selectedBaseIdAtom = atom((get) => {
   // This case should not happen except on initial render
   return "0";
 });
+
+/** Persisted atom to cache the value for the Match Product form input in the Box Reconciliation View. */
+export const reconciliationMatchProductAtom = atomWithStorage<ReconciliationMatchProductFields>(
+  "reconciliationMatchProduct",
+  {
path: front/src/stores/globalPreferenceStore.ts
user: login: HaGuesto
body: this atom should actually be a array or dictionary with a key being the sourceProductId and the value the prvious selection for this sourceProduct
created_at: 2025-03-26T16:27:37Z
author_association: MEMBER
diff_hunk: @@ -72,13 +74,18 @@ export function MatchProductsForm({
   onSubmitMatchProductsForm,
   onBoxUndelivered,
 }: IMatchProductsFormProps) {
+  const cachedReconciliationMatchProduct = useAtomValue(reconciliationMatchProductAtom);
+
   // default Values
   const defaultValues: IMatchProductsFormData = {
     productId: {
-      label: "Save Product As...",
-      value: "",
+      label: cachedReconciliationMatchProduct.productId.label,
path: front/src/components/BoxReconciliationOverlay/components/MatchProductsForm.tsx
user: login: HaGuesto
body: these values should be looked up in the atom based on the sourceProductId
created_at: 2025-03-26T16:28:15Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-03-26T13:18:47Z
body: ## [Codecov](https://app.codecov.io/gh/boxwise/boxtribute/pull/2049?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 79.00%. Comparing base [(`5b82119`)](https://app.codecov.io/gh/boxwise/boxtribute/commit/5b82119f24810ce2acfc62018b273da5bc5d2438?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise) to head [(`0be8d7e`)](https://app.codecov.io/gh/boxwise/boxtribute/commit/0be8d7e76d4cec303c7a37effd0797c4aa47c0d8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise).
> Report is 18 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #2049      +/-   ##
==========================================
- Coverage   80.62%   79.00%   -1.63%     
==========================================
  Files         259      267       +8     
  Lines       16642    17644    +1002     
  Branches     1691     1724      +33     
==========================================
+ Hits        13418    13939     +521     
- Misses       3191     3669     +478     
- Partials       33       36       +3     
```

| [Flag](https://app.codecov.io/gh/boxwise/boxtribute/pull/2049/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise) | Coverage Δ | |
|---|---|---|
| [backend](https://app.codecov.io/gh/boxwise/boxtribute/pull/2049/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise) | `99.66% <ø> (ø)` | |
| [frontend](https://app.codecov.io/gh/boxwise/boxtribute/pull/2049/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise) | `71.16% <ø> (-1.68%)` | :arrow_down: |
| [sharedComponents](https://app.codecov.io/gh/boxwise/boxtribute/pull/2049/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise) | `66.88% <ø> (+0.30%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/boxwise/boxtribute/pull/2049?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boxwise).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: HaGuesto
created_at: 2025-03-27T19:46:19Z
body: https://www.loom.com/share/5d2c3656964841d882eaf851f9ab7f70?sid=cd4c242f-be9f-48e6-88ab-4544fccd5e44
author_association: MEMBER
user: login: HaGuesto
created_at: 2025-03-27T19:48:14Z
body: @fhenrich33 when the BoxReconciliationOverlay opens and is done with its querys it throws a massive type error:

`Uncaught TypeError: Cannot read properties of undefined (reading 'productId')
    at MatchProductsForm (MatchProductsForm.tsx:91:51)
    at renderWithHooks (react-dom.development.js:15486:18)
    at mountIndeterminateComponent (react-dom.development.js:20103:13)
    at beginWork (react-dom.development.js:21626:16)
    at HTMLUnknownElement.callCallback2 (react-dom.development.js:4164:14)
    at HTMLUnknownElement.sentryWrapped (helpers.ts:116:17)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16)
    at invokeGuardedCallback (react-dom.development.js:4277:31)
    at beginWork$1 (react-dom.development.js:27490:7)
    at performUnitOfWork (react-dom.development.js:26596:12)
    at workLoopSync (react-dom.development.js:26505:5)
    at renderRootSync (react-dom.development.js:26473:7)
    at recoverFromConcurrentError (react-dom.development.js:25889:20)
    at performSyncWorkOnRoot (react-dom.development.js:26135:20)
    at flushSyncCallbacks (react-dom.development.js:12042:22)
    at react-dom.development.js:25690:13
MatchProductsForm @ MatchProductsForm.tsx:91
renderWithHooks @ react-dom.development.js:15486
mountIndeterminateComponent @ react-dom.development.js:20103
beginWork @ react-dom.development.js:21626
callCallback2 @ react-dom.development.js:4164
sentryWrapped @ helpers.ts:116
invokeGuardedCallbackDev @ react-dom.development.js:4213
invokeGuardedCallback @ react-dom.development.js:4277
beginWork$1 @ react-dom.development.js:27490
performUnitOfWork @ react-dom.development.js:26596
workLoopSync @ react-dom.development.js:26505
renderRootSync @ react-dom.development.js:26473
recoverFromConcurrentError @ react-dom.development.js:25889
performSyncWorkOnRoot @ react-dom.development.js:26135
flushSyncCallbacks @ react-dom.development.js:12042
(anonymous) @ react-dom.development.js:25690
hook.js:608 The above error occurred in the <MatchProductsForm> component:

    at MatchProductsForm (http://localhost:3000/src/components/BoxReconciliationOverlay/components/MatchProductsForm.tsx:53:37)
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at div
    at MotionComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:17872:40)
    at PresenceChild (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:19077:24)
    at AnimatePresence (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:19135:26)
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:21571:11
    at AccordionPanel2 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:21901:13)
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at AccordionItem2 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:21407:13)
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at Accordion2 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:21922:51)
    at BoxReconcilationAccordion (http://localhost:3000/src/components/BoxReconciliationOverlay/components/BoxReconciliationAccordion.tsx:19:45)
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:32995:11
    at section
    at MotionComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:17872:40)
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:31837:13
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:32203:21
    at div
    at FocusLockUI (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:24980:33)
    at FocusLockUICombination
    at FocusLock2 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:26522:5)
    at ModalFocusScope (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:32705:7)
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:32742:7
    at DefaultPortal (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:3279:11)
    at Portal
    at PresenceChild (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:19077:24)
    at AnimatePresence (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:19135:26)
    at Modal (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:32161:19)
    at BoxReconciliationView (http://localhost:3000/src/components/BoxReconciliationOverlay/components/BoxReconciliationView.tsx:13:41)
    at BoxReconciliationOverlay (http://localhost:3000/src/components/BoxReconciliationOverlay/BoxReconciliationOverlay.tsx:24:44)
    at ShipmentView (http://localhost:3000/src/views/Transfers/ShipmentView/ShipmentView.tsx:114:30)
    at Protected (http://localhost:3000/src/App.tsx:39:22)
    at RenderedRoute (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4089:5)
    at RenderedRoute (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4089:5)
    at Outlet (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4495:26)
    at Protected (http://localhost:3000/src/App.tsx:39:22)
    at RenderedRoute (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4089:5)
    at Outlet (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4495:26)
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at Flex2 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:28411:22)
    at div
    at http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2252:45
    at ChakraComponent (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11085:35)
    at Container22 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:28339:19)
    at Layout (http://localhost:3000/src/components/Layout.tsx:25:29)
    at RenderedRoute (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4089:5)
    at RenderedRoute (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4089:5)
    at Routes (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4559:5)
    at App (http://localhost:3000/src/App.tsx:117:38)
    at ApolloProvider (http://localhost:3000/node_modules/.vite/deps/@apollo_client.js?v=0c0f2ea0:6543:19)
    at ApolloAuth0Provider (http://localhost:3000/src/providers/ApolloAuth0Provider.tsx:22:32)
    at _c
    at WithAuthenticationRequired (http://localhost:3000/node_modules/.vite/deps/@auth0_auth0-react.js?v=6562b45f:1475:14)
    at Profiler (http://localhost:3000/node_modules/.vite/deps/@sentry_react.js?v=3e226843:24103:5)
    at profiler(WithAuthenticationRequired)
    at Auth0Provider (http://localhost:3000/node_modules/.vite/deps/@auth0_auth0-react.js?v=6562b45f:1197:23)
    at Auth0ProviderWithHistory (http://localhost:3000/src/providers/Auth0ProviderWithHistory.tsx:15:37)
    at Router (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:4502:15)
    at BrowserRouter (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=71d53dae:5248:5)
    at EnvironmentProvider (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11222:11)
    at ColorModeProvider (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:3510:5)
    at ThemeProvider2 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:2279:23)
    at ThemeProvider3 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11162:11)
    at ChakraProvider (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:11250:5)
    at ChakraProvider22 (http://localhost:3000/node_modules/.vite/deps/chunk-ZNADK4L4.js?v=c8593bcb:20845:5)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.`
author_association: MEMBER
user: login: HaGuesto
created_at: 2025-03-27T20:22:55Z
body: ![](https://github.trello.services/images/mini-trello-icon.png) [FE: caching the last selected pairs of old and new product/size during reconciliation and use it as default for other boxes to be reconciled. #medium](https://trello.com/c/KnkfD9g4/1714-fe-caching-the-last-selected-pairs-of-old-and-new-product-size-during-reconciliation-and-use-it-as-default-for-other-boxes-to-be)
author_association: MEMBER
user: login: HaGuesto
created_at: 2025-03-27T20:23:30Z
body: ![](https://github.trello.services/images/mini-trello-icon.png) [FE: caching the last selected location in a shipment reconciliation and use it as a default for other boxes to be reconciled. #medium](https://trello.com/c/sgTBpvEE/1713-fe-caching-the-last-selected-location-in-a-shipment-reconciliation-and-use-it-as-a-default-for-other-boxes-to-be-reconciled-medi)
author_association: MEMBER
user: login: fhenrich33
created_at: 2025-03-28T09:58:00Z
body: @HaGuesto I believe the problem happened because you still had the old product cache format in the localStore, so make sure to delete it to try again. Placed optional chains to avoid errors just in case.
author_association: CONTRIBUTOR
user: login: HaGuesto
created_at: 2025-03-28T15:11:17Z
body: @fhenrich33 you are right. I also experienced the problem that when switching between user accounts the cache is still there. Could we somehow include that we flush the local storage cache if a user logs out? Otherwise, this is super annoying to test and might bring some weird bugs later on.
author_association: MEMBER
user: login: fhenrich33
created_at: 2025-03-28T15:28:26Z
body: > @fhenrich33 you are right. I also experienced the problem that when switching between user accounts the cache is still there. Could we somehow include that we flush the local storage cache if a user logs out? Otherwise, this is super annoying to test and might bring some weird bugs later on.

Done!
author_association: CONTRIBUTOR