import { useState, useEffect } from "react"; // Mock API that takes longer to process some invoice payments async function apiPayInvoice(id) { const delay = id === 101 ? 600 : 200; await new Promise(r => setTimeout(r, delay)); if (id === 101) { throw new Error("Card declined. Insufficient funds."); } return { success: true }; } const INVOICES = [ { id: 101, client: "Acme Corp", amount: "450.00" }, { id: 102, client: "Stark Industries", amount: "1,250.00" }, { id: 103, client: "Wayne Enterprises", amount: "800.00" } ]; export default function App() { // BUG: Tracking invoice payment statuses using a single shared state object. // When multiple invoice payments run concurrently, they read/write to the // same shared activePayment state. If a fast payment (Invoice #102) succeeds // and a slow payment (Invoice #101) subsequently fails, the failure updates // the shared state, wiping out the success of Invoice #102 and showing // the failure status on the wrong invoice! // // The fix is to track payment statuses per-invoice (e.g. by using a dictionary // of statuses: {[id]: 'processing' | 'success' | 'error'} or mapping state locally). const [activePayment, setActivePayment] = useState({ id: null, status: "idle", error: "" }); useEffect(() => { const b = document.body; b.style.background = "#fafafa"; b.style.margin = "0"; return () => { b.style.background = ""; b.style.margin = ""; }; }, []); const handlePay = async (id) => { setActivePayment({ id, status: "processing", error: "" }); try { await apiPayInvoice(id); setActivePayment({ id, status: "success", error: "" }); } catch (err) { setActivePayment({ id, status: "error", error: err.message }); } }; return ( <div style={styles.page}> <div style={styles.card}> <h1 style={styles.h1}>Invoices</h1> <p style={styles.sub}>Manage and settle outstanding corporate client invoices.</p> <div style={styles.list}> {INVOICES.map((inv) => { const isProcessing = activePayment.id === inv.id && activePayment.status === "processing"; const hasError = activePayment.id === inv.id && activePayment.status === "error"; const isSuccess = activePayment.id === inv.id && activePayment.status === "success"; return ( <div key={inv.id} style={styles.row}> <div style={styles.info}> <div style={styles.client}> {inv.client} <span style={styles.id}>#{inv.id}</span> </div> <div style={styles.amount}>${inv.amount}</div> </div> <div style={styles.actions}> {isSuccess ? ( <span data-testid={`status-paid-${inv.id}`} style={styles.paidStatus}>✓ Paid</span> ) : isProcessing ? ( <span data-testid={`status-loading-${inv.id}`} style={styles.loading}>Processing...</span> ) : ( <button data-testid={`pay-btn-${inv.id}`} onClick={() => handlePay(inv.id)} style={styles.payBtn} > Pay Now </button> )} </div> {hasError && ( <div data-testid={`error-${inv.id}`} style={styles.errorRow}> Error: {activePayment.error} </div> )} </div> ); })} </div> </div> </div> ); } const styles = { page: { display: "flex", justifyContent: "center", padding: "40px 20px", fontFamily: "system-ui, sans-serif" }, card: { background: "#fff", borderRadius: 12, padding: "24px", width: "100%", maxWidth: 480, boxShadow: "0 4px 6px -1px rgba(0,0,0,0.1)" }, h1: { margin: "0 0 6px", fontSize: 20, fontWeight: 600, color: "#111827" }, sub: { margin: "0 0 20px", fontSize: 14, color: "#6B7280" }, list: { display: "flex", flexDirection: "column", gap: 16 }, row: { position: "relative", display: "flex", flexWrap: "wrap", justifyContent: "space-between", alignItems: "center", padding: "16px", border: "1px solid #E5E7EB", borderRadius: 8, background: "#FAFBFB" }, info: { display: "flex", flexDirection: "column", gap: 4 }, client: { fontSize: 15, fontWeight: 600, color: "#111827" }, id: { fontSize: 12, color: "#9CA3AF", marginLeft: 6, fontWeight: 400 }, amount: { fontSize: 14, color: "#374151", fontWeight: 500 }, actions: { display: "flex", alignItems: "center" }, payBtn: { background: "#4F46E5", color: "#fff", border: "none", padding: "8px 16px", borderRadius: 6, fontSize: 13, fontWeight: 500, cursor: "pointer", transition: "background-color 0.2s" }, paidStatus: { color: "#10B981", fontSize: 14, fontWeight: 600 }, loading: { color: "#6B7280", fontSize: 13, fontStyle: "italic" }, errorRow: { width: "100%", marginTop: 8, fontSize: 12, color: "#EF4444", fontWeight: 500 } };
Click "Run checks" to verify your solution.