Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4554133
feat: optimistic responses
yuskithedeveloper Sep 19, 2025
a911126
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Sep 22, 2025
f233264
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 21, 2025
808d9f1
feat: removed batching, getSavedForLater changed to apollo cached query
yuskithedeveloper Oct 23, 2025
e9c68fe
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 23, 2025
fa17118
chore: removed not used code
yuskithedeveloper Oct 23, 2025
15eb43a
Merge branch 'feat/VCST-3928-save-for-later-optimistic-response' of h…
yuskithedeveloper Oct 23, 2025
4e9ca92
refactor: undefined checks
yuskithedeveloper Oct 23, 2025
f1fd410
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 24, 2025
67c84ea
fix: accidentally removed file
yuskithedeveloper Oct 24, 2025
9ad902b
chore: removed not used code
yuskithedeveloper Oct 24, 2025
f170856
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 28, 2025
4a6ed42
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
Lenajava1 Oct 29, 2025
c5dc43d
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 29, 2025
839d335
refactor: duplicated code
yuskithedeveloper Oct 29, 2025
01f9a08
feat: apollo lazy query usage
yuskithedeveloper Oct 29, 2025
99e0b6b
feat: showing saved for later block for empty cart
yuskithedeveloper Oct 29, 2025
02cdd1b
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 29, 2025
0aba00b
refactor: cartId mutation parameter moved inside of useSaveForLater c…
yuskithedeveloper Oct 30, 2025
b617541
Merge branch 'feat/VCST-3928-save-for-later-optimistic-response' of h…
yuskithedeveloper Oct 30, 2025
11bba33
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 30, 2025
1df93dc
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 31, 2025
9997209
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
yuskithedeveloper Oct 31, 2025
5412bdf
Merge branch 'dev' into feat/VCST-3928-save-for-later-optimistic-resp…
Lenajava1 Nov 5, 2025
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

This file was deleted.

6 changes: 1 addition & 5 deletions client-app/pages/cart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
<CartForLater
v-if="savedForLaterList?.items?.length && !shouldHide('cart-for-later')"
:saved-for-later-list="savedForLaterList"
:loading="moveFromSavedForLaterOverflowed"
class="mt-5"
@add-to-cart="(lineItemId) => handleMoveToCart([lineItemId])"
/>
Expand All @@ -84,7 +83,7 @@
:items-grouped-by-vendor="lineItemsGroupedByVendor"
:selected-item-ids="selectedItemIds"
:validation-errors="cart.validationErrors"
:disabled="changeItemQuantityBatchedOverflowed || moveToSavedForLaterOverflowed || selectionOverflowed"
:disabled="changeItemQuantityBatchedOverflowed || selectionOverflowed"
data-test-id="cart.products-section"
:hide-controls="hideControls"
@change:item-quantity="changeItemQuantityBatched($event.itemId, $event.quantity)"
Expand Down Expand Up @@ -114,7 +113,6 @@
<CartForLater
v-if="savedForLaterList?.items?.length && !shouldHide('cart-for-later')"
:saved-for-later-list="savedForLaterList"
:loading="moveFromSavedForLaterOverflowed"
class="mt-5"
@add-to-cart="(lineItemId) => handleMoveToCart([lineItemId])"
/>
Expand Down Expand Up @@ -292,9 +290,7 @@ const { couponCode, couponIsApplied, couponValidationError, applyCoupon, removeC
const {
savedForLaterList,
moveToSavedForLater,
moveToSavedForLaterOverflowed,
moveFromSavedForLater,
moveFromSavedForLaterOverflowed,
getSavedForLater,
loading: saveForLaterLoading,
} = useSavedForLater();
Expand Down
114 changes: 73 additions & 41 deletions client-app/shared/cart/composables/useSaveForLater.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,68 @@
import { ApolloError } from "@apollo/client/core";
import { useMutation } from "@vue/apollo-composable";
import { useMutation, useQuery } from "@vue/apollo-composable";
import { createSharedComposable } from "@vueuse/core";
import { ref, computed } from "vue";
import { computed } from "vue";
import { AbortReason } from "@/core/api/common/enums";
import { getSavedForLater as getSavedForLaterQuery } from "@/core/api/graphql/cart/queries/getSavedForLater";
import { MoveToSavedForLaterDocument, MoveFromSavedForLaterDocument } from "@/core/api/graphql/types";
import { useMutationBatcher } from "@/core/composables/useMutationBatcher";
import {
MoveToSavedForLaterDocument,
MoveFromSavedForLaterDocument,
GetSavedForLaterDocument,
} from "@/core/api/graphql/types";
import { globals } from "@/core/globals";
import { Logger } from "@/core/utilities";
import type { SavedForLaterListFragment } from "@/core/api/graphql/types";
import { useFullCart } from "@/shared/cart";

function _useSavedForLater() {
const { storeId, currencyCode, cultureName, userId } = globals;

const savedForLaterList = ref<SavedForLaterListFragment>();
const { cart } = useFullCart();

const { mutate: _moveToSavedForLater, loading: _moveToSavedForLaterLoading } =
useMutation(MoveToSavedForLaterDocument);
const savedForLaterList = computed(() => _savedForLaterQueryResult.value?.getSavedForLater);

const {
add: _moveToSavedForLaterBatched,
overflowed: moveToSavedForLaterOverflowed,
loading: _moveToSavedForLaterBatchedLoading,
} = useMutationBatcher(_moveToSavedForLater);
refetch: getSavedForLater,
loading: _getSavedForLaterLoading,
result: _savedForLaterQueryResult,
} = useQuery(
GetSavedForLaterDocument,
{
storeId,
userId,
cultureName,
currencyCode,
},
{
notifyOnNetworkStatusChange: true,
fetchPolicy: "cache-first",
},
);

const { mutate: _moveToSavedForLater, loading: _moveToSavedForLaterLoading } = useMutation(
MoveToSavedForLaterDocument,
{
optimisticResponse(vars, { IGNORE }) {
const movedItemIds = vars.command?.lineItemIds;

if (!movedItemIds?.length || !cart.value || !savedForLaterList.value) {
return IGNORE;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Bug

Optimistic responses for moving items to/from 'saved for later' are ignored if savedForLaterList.value is undefined. This happens when the savedForLaterList hasn't loaded yet, creating a race condition that prevents immediate UI updates.

Fix in Cursor Fix in Web


return {
moveToSavedForLater: {
cart: {
...cart.value,
items: cart.value.items.filter((item) => !movedItemIds.includes(item.id)),
},
list: savedForLaterList.value,
},
};
},
},
);

async function moveToSavedForLater(cartId: string, itemIds: string[]) {
try {
const moveResult = await _moveToSavedForLaterBatched({
await _moveToSavedForLater({
command: {
storeId,
userId,
Expand All @@ -36,8 +72,6 @@ function _useSavedForLater() {
lineItemIds: itemIds,
},
});

savedForLaterList.value = moveResult?.data?.moveToSavedForLater?.list;
} catch (err) {
if (err instanceof ApolloError && err.networkError?.toString() === (AbortReason.Explicit as string)) {
return;
Expand All @@ -46,18 +80,32 @@ function _useSavedForLater() {
}
}

const { mutate: _moveFromSavedForLater, loading: _moveFromSavedForLaterLoading } =
useMutation(MoveFromSavedForLaterDocument);
const { mutate: _moveFromSavedForLater, loading: _moveFromSavedForLaterLoading } = useMutation(
MoveFromSavedForLaterDocument,
{
optimisticResponse(vars, { IGNORE }) {
const movedItemIds = vars.command?.lineItemIds;

const {
add: _moveFromSavedForLaterBatched,
overflowed: moveFromSavedForLaterOverflowed,
loading: _moveFromSavedForLaterBatchedLoading,
} = useMutationBatcher(_moveFromSavedForLater);
if (!movedItemIds?.length || !cart.value || !savedForLaterList.value) {
return IGNORE;
}

return {
moveFromSavedForLater: {
cart: cart.value,
list: {
...savedForLaterList.value,
items: savedForLaterList.value.items.filter((item) => !movedItemIds.includes(item.id)),
},
},
};
},
},
);

async function moveFromSavedForLater(cartId: string, itemIds: string[]) {
try {
const moveResult = await _moveFromSavedForLaterBatched({
await _moveFromSavedForLater({
command: {
storeId,
userId,
Expand All @@ -67,8 +115,6 @@ function _useSavedForLater() {
lineItemIds: itemIds,
},
});

savedForLaterList.value = moveResult?.data?.moveFromSavedForLater?.list;
} catch (err) {
if (err instanceof ApolloError && err.networkError?.toString() === (AbortReason.Explicit as string)) {
return;
Expand All @@ -77,29 +123,15 @@ function _useSavedForLater() {
}
}

async function getSavedForLater() {
try {
savedForLaterList.value = await getSavedForLaterQuery();
} catch (err) {
Logger.error(`useSavedForLater.${getSavedForLater.name}`, err);
}
}

return {
savedForLaterList,

moveToSavedForLater,
moveToSavedForLaterOverflowed,
moveFromSavedForLater,
moveFromSavedForLaterOverflowed,
getSavedForLater,

loading: computed(
() =>
_moveToSavedForLaterLoading.value ||
_moveToSavedForLaterBatchedLoading.value ||
_moveFromSavedForLaterLoading.value ||
_moveFromSavedForLaterBatchedLoading.value,
() => _getSavedForLaterLoading.value || _moveToSavedForLaterLoading.value || _moveFromSavedForLaterLoading.value,
),
};
}
Expand Down
Loading