fix(orders/new): 첫 클릭에도 재고 초과 경고 즉시 표시 — setCart 콜백 안 warned 변수 제거
Deploy momo-erp / deploy (push) Successful in 1m58s
Deploy momo-erp / deploy (push) Successful in 1m58s
원인: - addManyToCart 가 setCart 함수형 업데이트 안에서 외부 변수 warned 에 값 세팅 - React 18 batched updates 로 콜백 실행이 한 박자 늦어 if(warned) 가 false → 첫 클릭 경고 누락 - 두 번째 클릭 때 이미 콜백이 실행돼 warned 가 true 보여 경고 표시 — 사용자가 본 현상 수정: - 함수형 업데이터 진입 전에 cart 를 동기적으로 읽어 newQty/limit 비교 - 초과면 Swal 띄우고 return — setCart 호출 자체를 안 함 (장바구니 변경 없음) - 통과 시에만 setCart 로 카트 갱신 - updateQty, setQty 도 동일 패턴(stale-closure 차단도 함수형 업데이터 밖에서) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -126,22 +126,13 @@ function ItemsBrowse() {
|
||||
const isDelivery = item.REQUIRES_DELIVERY === "Y";
|
||||
const effStock = isDelivery ? Number.MAX_SAFE_INTEGER : stock;
|
||||
const limit = unlimitedQty || maxQ <= 0 ? effStock : Math.min(effStock, maxQ);
|
||||
let toastTitle = "";
|
||||
let warned = false;
|
||||
setCart((c) => {
|
||||
const found = c.find((x) => x.item.OBJID === item.OBJID);
|
||||
const newQty = (found?.qty ?? 0) + qty;
|
||||
if (newQty > limit) {
|
||||
warned = true;
|
||||
return c;
|
||||
}
|
||||
toastTitle = found
|
||||
? `수량 +${qty} → ${newQty}개`
|
||||
: `장바구니에 추가됨: ${item.ITEM_NAME} (${qty}개)`;
|
||||
if (found) return c.map((x) => x.item.OBJID === item.OBJID ? { ...x, qty: newQty } : x);
|
||||
return [...c, { item, qty }];
|
||||
});
|
||||
if (warned) {
|
||||
|
||||
// setCart 함수형 업데이트 안에서 외부 변수에 warned 세팅하면 비동기 타이밍 때문에
|
||||
// 첫 클릭에는 if(warned) 체크가 한 박자 늦게 동작. 동기 체크로 변경.
|
||||
const found = cart.find((x) => x.item.OBJID === item.OBJID);
|
||||
const newQty = (found?.qty ?? 0) + qty;
|
||||
|
||||
if (newQty > limit) {
|
||||
const isStockLimit = maxQ <= 0 || stock <= maxQ;
|
||||
Swal.fire({
|
||||
icon: "warning",
|
||||
@@ -152,60 +143,54 @@ function ItemsBrowse() {
|
||||
confirmButtonColor: "#0f766e",
|
||||
confirmButtonText: "확인",
|
||||
});
|
||||
return;
|
||||
return; // 차단 — 장바구니 변경 없음
|
||||
}
|
||||
|
||||
setCart((c) => {
|
||||
const f = c.find((x) => x.item.OBJID === item.OBJID);
|
||||
if (f) return c.map((x) => x.item.OBJID === item.OBJID ? { ...x, qty: newQty } : x);
|
||||
return [...c, { item, qty }];
|
||||
});
|
||||
Swal.fire({
|
||||
toast: true, position: "top-end", icon: "success",
|
||||
title: toastTitle,
|
||||
title: found ? `수량 +${qty} → ${newQty}개` : `장바구니에 추가됨: ${item.ITEM_NAME} (${qty}개)`,
|
||||
showConfirmButton: false, timer: 1000, timerProgressBar: true,
|
||||
});
|
||||
};
|
||||
|
||||
const updateQty = (objid: string, delta: number) => {
|
||||
let warnLimit = -1;
|
||||
let warnIsStock = false;
|
||||
setCart((c) =>
|
||||
c.map((x) => {
|
||||
if (x.item.OBJID !== objid) return x;
|
||||
const newQty = x.qty + delta;
|
||||
if (newQty <= 0) return x;
|
||||
const stock = Number(x.item.STOCK_QTY);
|
||||
const maxQ = Number(x.item.MAX_ORDER_QTY ?? 0);
|
||||
const isDelivery = x.item.REQUIRES_DELIVERY === "Y";
|
||||
const effStock = isDelivery ? Number.MAX_SAFE_INTEGER : stock;
|
||||
const limit = unlimitedQty || maxQ <= 0 ? effStock : Math.min(effStock, maxQ);
|
||||
if (newQty > limit) {
|
||||
warnLimit = limit;
|
||||
warnIsStock = maxQ <= 0 || stock <= maxQ;
|
||||
return x;
|
||||
}
|
||||
return { ...x, qty: newQty };
|
||||
})
|
||||
);
|
||||
if (warnLimit >= 0) toastLimit(warnLimit, warnIsStock);
|
||||
const target = cart.find((x) => x.item.OBJID === objid);
|
||||
if (!target) return;
|
||||
const newQty = target.qty + delta;
|
||||
if (newQty <= 0) return;
|
||||
const stock = Number(target.item.STOCK_QTY);
|
||||
const maxQ = Number(target.item.MAX_ORDER_QTY ?? 0);
|
||||
const isDelivery = target.item.REQUIRES_DELIVERY === "Y";
|
||||
const effStock = isDelivery ? Number.MAX_SAFE_INTEGER : stock;
|
||||
const limit = unlimitedQty || maxQ <= 0 ? effStock : Math.min(effStock, maxQ);
|
||||
if (newQty > limit) {
|
||||
toastLimit(limit, maxQ <= 0 || stock <= maxQ);
|
||||
return;
|
||||
}
|
||||
setCart((c) => c.map((x) => x.item.OBJID === objid ? { ...x, qty: newQty } : x));
|
||||
};
|
||||
|
||||
const setQty = (objid: string, value: number) => {
|
||||
let warnLimit = -1;
|
||||
let warnIsStock = false;
|
||||
setCart((c) =>
|
||||
c.map((x) => {
|
||||
if (x.item.OBJID !== objid) return x;
|
||||
const stock = Number(x.item.STOCK_QTY);
|
||||
const maxQ = Number(x.item.MAX_ORDER_QTY ?? 0);
|
||||
const isDelivery = x.item.REQUIRES_DELIVERY === "Y";
|
||||
const effStock = isDelivery ? Number.MAX_SAFE_INTEGER : stock;
|
||||
const limit = unlimitedQty || maxQ <= 0 ? effStock : Math.min(effStock, maxQ);
|
||||
const requested = Math.floor(value || 0);
|
||||
if (requested > limit) {
|
||||
warnLimit = limit;
|
||||
warnIsStock = maxQ <= 0 || stock <= maxQ;
|
||||
}
|
||||
const clamped = Math.max(1, Math.min(limit, requested));
|
||||
return { ...x, qty: clamped };
|
||||
})
|
||||
);
|
||||
if (warnLimit >= 0) toastLimit(warnLimit, warnIsStock);
|
||||
const target = cart.find((x) => x.item.OBJID === objid);
|
||||
if (!target) return;
|
||||
const stock = Number(target.item.STOCK_QTY);
|
||||
const maxQ = Number(target.item.MAX_ORDER_QTY ?? 0);
|
||||
const isDelivery = target.item.REQUIRES_DELIVERY === "Y";
|
||||
const effStock = isDelivery ? Number.MAX_SAFE_INTEGER : stock;
|
||||
const limit = unlimitedQty || maxQ <= 0 ? effStock : Math.min(effStock, maxQ);
|
||||
const requested = Math.floor(value || 0);
|
||||
if (requested > limit) {
|
||||
toastLimit(limit, maxQ <= 0 || stock <= maxQ);
|
||||
// 차단 — 기존 수량 유지
|
||||
return;
|
||||
}
|
||||
const clamped = Math.max(1, requested);
|
||||
setCart((c) => c.map((x) => x.item.OBJID === objid ? { ...x, qty: clamped } : x));
|
||||
};
|
||||
|
||||
const toastLimit = (limit: number, isStockLimit: boolean) => {
|
||||
|
||||
Reference in New Issue
Block a user