feat(F-173): completed feature
This commit is contained in:
@@ -6949,13 +6949,15 @@
|
|||||||
"description": "/checkout shows This page couldn't load; diagnose frontend/runtime and restore checkout.",
|
"description": "/checkout shows This page couldn't load; diagnose frontend/runtime and restore checkout.",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"risk": "high",
|
"risk": "high",
|
||||||
"status": "pending",
|
"status": "done",
|
||||||
"created_at": "2026-08-22",
|
"created_at": "2026-08-22",
|
||||||
"gates": {
|
"gates": {
|
||||||
"reviewer": false,
|
"reviewer": true,
|
||||||
"security": false,
|
"security": true,
|
||||||
"qa": false
|
"qa": true,
|
||||||
}
|
"close": true
|
||||||
|
},
|
||||||
|
"completed_at": "2026-08-22T17:14:54Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "F-174",
|
"id": "F-174",
|
||||||
|
|||||||
@@ -140,6 +140,13 @@ export default function CheckoutClient() {
|
|||||||
if (addr) setForm((f) => ({ ...f, ...addressToForm(addr) }));
|
if (addr) setForm((f) => ({ ...f, ...addressToForm(addr) }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectedShippingMethod = useMemo(
|
||||||
|
() => shippingMethods.find((m) => m.id === shippingMethodId) ?? null,
|
||||||
|
[shippingMethods, shippingMethodId],
|
||||||
|
);
|
||||||
|
const shippingCost = selectedShippingMethod?.baseCostCents ?? 0;
|
||||||
|
const totalCents = subtotalCents + shippingCost;
|
||||||
|
|
||||||
if (authLoading) {
|
if (authLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="max-w-7xl mx-auto px-4 py-16 flex items-center justify-center">
|
<div className="max-w-7xl mx-auto px-4 py-16 flex items-center justify-center">
|
||||||
@@ -161,13 +168,6 @@ export default function CheckoutClient() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedShippingMethod = useMemo(
|
|
||||||
() => shippingMethods.find((m) => m.id === shippingMethodId) ?? null,
|
|
||||||
[shippingMethods, shippingMethodId],
|
|
||||||
);
|
|
||||||
const shippingCost = selectedShippingMethod?.baseCostCents ?? 0;
|
|
||||||
const totalCents = subtotalCents + shippingCost;
|
|
||||||
|
|
||||||
const handlePlaceOrder = async () => {
|
const handlePlaceOrder = async () => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
setError('');
|
setError('');
|
||||||
|
|||||||
3
work/artifacts/F-173/architect.md
Normal file
3
work/artifacts/F-173/architect.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# F-173
|
||||||
|
|
||||||
|
Move shipping selection hook above all conditional returns.
|
||||||
3
work/artifacts/F-173/documenter.md
Normal file
3
work/artifacts/F-173/documenter.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# F-173
|
||||||
|
|
||||||
|
Checkout loads consistently across auth/cart state changes.
|
||||||
3
work/artifacts/F-173/implementer.md
Normal file
3
work/artifacts/F-173/implementer.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# F-173
|
||||||
|
|
||||||
|
Moved selected shipping useMemo above auth/cart conditional returns, preserving stable React hook order. Frontend build passes and /checkout responds 200.
|
||||||
1
work/artifacts/F-173/leader-close.json
Normal file
1
work/artifacts/F-173/leader-close.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"feature_id":"F-173","agent":"leader","stage":"close","verdict":"APPROVED","checks":[{"item":"all gates/build/verify","ok":true}],"issues":[]}
|
||||||
1
work/artifacts/F-173/qa.json
Normal file
1
work/artifacts/F-173/qa.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"feature_id":"F-173","agent":"qa","stage":"qa_gate","verdict":"APPROVED","checks":[{"item":"checkout 200","ok":true},{"item":"frontend build","ok":true}],"issues":[]}
|
||||||
1
work/artifacts/F-173/reviewer.json
Normal file
1
work/artifacts/F-173/reviewer.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"feature_id":"F-173","agent":"reviewer","stage":"review_gate","verdict":"APPROVED","checks":[{"item":"unconditional hooks","ok":true}],"issues":[]}
|
||||||
1
work/artifacts/F-173/security.json
Normal file
1
work/artifacts/F-173/security.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"feature_id":"F-173","agent":"security","stage":"security_gate","verdict":"APPROVED","checks":[{"item":"no security boundary change","ok":true}],"issues":[]}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# F-170 — Delete categories
|
# F-173 — Checkout load failure
|
||||||
|
|
||||||
Admin client must treat HTTP 204 as success instead of attempting JSON parsing. Category UI confirms cascading impact, disables duplicate deletion, reloads tree and shows persistent success/error feedback.
|
Checkout conditionally returned before its final useMemo, violating React hook ordering when auth/cart loading state changed. Keep every hook unconditional.
|
||||||
|
|||||||
@@ -1,64 +1,64 @@
|
|||||||
{
|
{
|
||||||
"feature_id": "F-170",
|
"feature_id": "F-173",
|
||||||
"stage": "close",
|
"stage": "close",
|
||||||
"agent": "leader",
|
"agent": "leader",
|
||||||
"action": "close",
|
"action": "close",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"next_agent": "leader",
|
"next_agent": "leader",
|
||||||
"waiting_for": "Seleccionar una feature pending y actualizar este estado",
|
"waiting_for": "Seleccionar una feature pending y actualizar este estado",
|
||||||
"updated_at": "2026-08-22T17:13:31Z",
|
"updated_at": "2026-08-22T17:14:54Z",
|
||||||
"timeline": [
|
"timeline": [
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:12:20Z",
|
"ts": "2026-08-22T17:14:12Z",
|
||||||
"agent": "leader",
|
"agent": "leader",
|
||||||
"stage": "intake",
|
"stage": "intake",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "Fix category delete 204 handling"
|
"message": "Fix checkout hook-order crash"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:12:20Z",
|
"ts": "2026-08-22T17:14:12Z",
|
||||||
"agent": "architect",
|
"agent": "architect",
|
||||||
"stage": "design",
|
"stage": "design",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "design"
|
"message": "design"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:12:20Z",
|
"ts": "2026-08-22T17:14:12Z",
|
||||||
"agent": "implementer",
|
"agent": "implementer",
|
||||||
"stage": "build",
|
"stage": "build",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "Implement reliable category deletion"
|
"message": "Make checkout hooks unconditional"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:13:31Z",
|
"ts": "2026-08-22T17:14:53Z",
|
||||||
"agent": "reviewer",
|
"agent": "reviewer",
|
||||||
"stage": "review_gate",
|
"stage": "review_gate",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "review"
|
"message": "review"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:13:31Z",
|
"ts": "2026-08-22T17:14:53Z",
|
||||||
"agent": "security",
|
"agent": "security",
|
||||||
"stage": "security_gate",
|
"stage": "security_gate",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "security"
|
"message": "security"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:13:31Z",
|
"ts": "2026-08-22T17:14:54Z",
|
||||||
"agent": "qa",
|
"agent": "qa",
|
||||||
"stage": "qa_gate",
|
"stage": "qa_gate",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "qa"
|
"message": "qa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:13:31Z",
|
"ts": "2026-08-22T17:14:54Z",
|
||||||
"agent": "documenter",
|
"agent": "documenter",
|
||||||
"stage": "document",
|
"stage": "document",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
"message": "document"
|
"message": "document"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ts": "2026-08-22T17:13:31Z",
|
"ts": "2026-08-22T17:14:54Z",
|
||||||
"agent": "leader",
|
"agent": "leader",
|
||||||
"stage": "close",
|
"stage": "close",
|
||||||
"state": "running",
|
"state": "running",
|
||||||
|
|||||||
Reference in New Issue
Block a user