[FIX] website_sale_aplicoop: stamp localStorage cart with cycle and evict on rollover
The cart was reappearing on stage with ghost items that had never been saved as drafts on the server. Root cause: the localStorage cart had no cycle awareness. Users who added items but never clicked Save left the items in localStorage forever, and the only existing eviction path (_clearCurrentOrderCartSilently via _checkGroupOrderStatus) only fires when cutoff_passed flips true — which it rarely does, because the stored cutoff_date is normally in the future for an active cycle. On the next visit, _loadCart happily rehydrated the stale items and _autoLoadDraftOnInit short-circuited because the cart was no longer empty. Stamp every cart written to localStorage with the cutoff_date it belongs to and reject on cycle mismatch in _loadCart. The cutoff_date is captured from /eskaera/check-status (which already returned it). Legacy unstamped values and corrupt JSON are also dropped, so existing browsers self-heal on first reload after the deploy. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
9a9139711f
commit
965c5c2495
1 changed files with 70 additions and 8 deletions
|
|
@ -436,18 +436,74 @@
|
||||||
|
|
||||||
_loadCart: function () {
|
_loadCart: function () {
|
||||||
var cartKey = "eskaera_" + this.orderId + "_cart";
|
var cartKey = "eskaera_" + this.orderId + "_cart";
|
||||||
var savedCart = localStorage.getItem(cartKey);
|
var raw = localStorage.getItem(cartKey);
|
||||||
this.cart = savedCart ? JSON.parse(savedCart) : {};
|
this.cart = {};
|
||||||
console.log("Cart loaded from localStorage[" + cartKey + "]:", this.cart);
|
if (!raw) {
|
||||||
console.log("Raw localStorage value:", savedCart);
|
console.log("Cart loaded: localStorage[" + cartKey + "] is empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(raw);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Cart load: invalid JSON in localStorage, dropping", e);
|
||||||
|
localStorage.removeItem(cartKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Legacy / corrupt shape (raw items object without cycle stamp) → drop.
|
||||||
|
if (
|
||||||
|
!parsed ||
|
||||||
|
typeof parsed !== "object" ||
|
||||||
|
!parsed.items ||
|
||||||
|
!("cutoff_date" in parsed)
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
"Cart load: legacy/unstamped value, dropping localStorage[" + cartKey + "]"
|
||||||
|
);
|
||||||
|
localStorage.removeItem(cartKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Cycle mismatch (or no current cycle known) → drop. This is the
|
||||||
|
// primary defence against stale carts surviving cycle rollover.
|
||||||
|
if (!this._currentCutoffDate || parsed.cutoff_date !== this._currentCutoffDate) {
|
||||||
|
console.log(
|
||||||
|
"Cart load: cycle mismatch (stored=" +
|
||||||
|
parsed.cutoff_date +
|
||||||
|
", current=" +
|
||||||
|
this._currentCutoffDate +
|
||||||
|
"), dropping localStorage[" +
|
||||||
|
cartKey +
|
||||||
|
"]"
|
||||||
|
);
|
||||||
|
localStorage.removeItem(cartKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.cart = parsed.items;
|
||||||
|
console.log(
|
||||||
|
"Cart loaded from localStorage[" +
|
||||||
|
cartKey +
|
||||||
|
"] (cycle " +
|
||||||
|
parsed.cutoff_date +
|
||||||
|
"):",
|
||||||
|
this.cart
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_saveCart: function () {
|
_saveCart: function () {
|
||||||
var cartKey = "eskaera_" + this.orderId + "_cart";
|
var cartKey = "eskaera_" + this.orderId + "_cart";
|
||||||
var cartJson = JSON.stringify(this.cart);
|
var payload = JSON.stringify({
|
||||||
localStorage.setItem(cartKey, cartJson);
|
cutoff_date: this._currentCutoffDate || null,
|
||||||
console.log("Cart saved to localStorage[" + cartKey + "]:", this.cart);
|
items: this.cart,
|
||||||
console.log("Verification - immediately read back:", localStorage.getItem(cartKey));
|
});
|
||||||
|
localStorage.setItem(cartKey, payload);
|
||||||
|
console.log(
|
||||||
|
"Cart saved to localStorage[" +
|
||||||
|
cartKey +
|
||||||
|
"] (cycle " +
|
||||||
|
(this._currentCutoffDate || "unknown") +
|
||||||
|
"):",
|
||||||
|
this.cart
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_clearCurrentOrderCartSilently: function () {
|
_clearCurrentOrderCartSilently: function () {
|
||||||
|
|
@ -493,11 +549,15 @@
|
||||||
|
|
||||||
xhr.onload = function () {
|
xhr.onload = function () {
|
||||||
if (xhr.status !== 200) {
|
if (xhr.status !== 200) {
|
||||||
|
self._currentCutoffDate = null;
|
||||||
done();
|
done();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var data = JSON.parse(xhr.responseText || "{}");
|
var data = JSON.parse(xhr.responseText || "{}");
|
||||||
|
// Capture current cycle marker so _loadCart can evict a
|
||||||
|
// localStorage cart that belongs to a previous cycle.
|
||||||
|
self._currentCutoffDate = data && data.cutoff_date ? data.cutoff_date : null;
|
||||||
// Clear cart if order is closed, action requests clearance, or cutoff already passed
|
// Clear cart if order is closed, action requests clearance, or cutoff already passed
|
||||||
var shouldClear = false;
|
var shouldClear = false;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
@ -527,12 +587,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
self._currentCutoffDate = null;
|
||||||
console.warn("[groupOrderShop] check-status parse error", e);
|
console.warn("[groupOrderShop] check-status parse error", e);
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.onerror = function () {
|
xhr.onerror = function () {
|
||||||
|
self._currentCutoffDate = null;
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue