fix(cart): pad RelatedProducts to 3 cards even on first pick
The catalog only has 4 products; with 2 already in the cart, the initial pick's pool shrinks to 2 and pickRandom's slice can't pad past what's available, so the grid rendered 2 cards instead of 3. The swap-after-add path already had fallback logic for this exact case — extracted it into pickWithFallback() and reused it for the initial pick too.
This commit is contained in:
@@ -16,6 +16,27 @@ function pickRandom(allIds: string[], excludeIds: string[], count: number): stri
|
||||
return shuffled.slice(0, count);
|
||||
}
|
||||
|
||||
// The catalog only has a handful of products — once the cart holds enough
|
||||
// distinct ones, "N recommendations that aren't already in the cart" can
|
||||
// become impossible (e.g. 4 products total, 2 already in the cart, but
|
||||
// DISPLAY_COUNT is 3 — only 2 non-cart products exist, period). Falling
|
||||
// back to re-suggesting something already in the cart (a normal "grab
|
||||
// another one" pattern) beats silently shrinking the grid below
|
||||
// DISPLAY_COUNT. Shared by both the initial pick and the swap-after-add
|
||||
// path so neither can under-fill the grid.
|
||||
function pickWithFallback(allIds: string[], excludeIds: string[], keep: string[], count: number): string[] {
|
||||
const missing = count - keep.length;
|
||||
if (missing <= 0) return keep;
|
||||
|
||||
let picks = pickRandom(allIds, [...excludeIds, ...keep], missing);
|
||||
if (picks.length < missing) {
|
||||
const stillMissing = missing - picks.length;
|
||||
const fallback = pickRandom(allIds, [...keep, ...picks], stillMissing);
|
||||
picks = [...picks, ...fallback];
|
||||
}
|
||||
return [...keep, ...picks];
|
||||
}
|
||||
|
||||
export function RelatedProducts() {
|
||||
const cart = useCart();
|
||||
const products = useProducts();
|
||||
@@ -55,7 +76,7 @@ export function RelatedProducts() {
|
||||
|
||||
if (!pickedRef.current) {
|
||||
pickedRef.current = true;
|
||||
setDisplayIds(pickRandom(productIds, cartIds, DISPLAY_COUNT));
|
||||
setDisplayIds(pickWithFallback(productIds, cartIds, [], DISPLAY_COUNT));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,24 +88,7 @@ export function RelatedProducts() {
|
||||
swapTimeoutRef.current = setTimeout(() => {
|
||||
setDisplayIds((prev) => {
|
||||
const stillRelevant = prev.filter((id) => !cartIds.includes(id));
|
||||
const missing = DISPLAY_COUNT - stillRelevant.length;
|
||||
if (missing <= 0) return stillRelevant;
|
||||
|
||||
let replacements = pickRandom(productIds, [...cartIds, ...stillRelevant], missing);
|
||||
|
||||
// The catalog only has a handful of products — once the cart
|
||||
// holds enough distinct ones, "N recommendations that aren't
|
||||
// already in the cart" can become impossible. Falling back to
|
||||
// re-suggesting something already in the cart (a normal "grab
|
||||
// another one" pattern) beats silently shrinking the grid below
|
||||
// DISPLAY_COUNT.
|
||||
if (replacements.length < missing) {
|
||||
const stillMissing = missing - replacements.length;
|
||||
const fallback = pickRandom(productIds, [...stillRelevant, ...replacements], stillMissing);
|
||||
replacements = [...replacements, ...fallback];
|
||||
}
|
||||
|
||||
return [...stillRelevant, ...replacements];
|
||||
return pickWithFallback(productIds, cartIds, stillRelevant, DISPLAY_COUNT);
|
||||
});
|
||||
}, FEEDBACK_MS);
|
||||
}, [cartKey, productIds]);
|
||||
|
||||
Reference in New Issue
Block a user