feat(invoice-inbox): loading state + review fixes + env-driven config (#416)
* feat(invoice-inbox): make Bedrock model + max_tokens env-overridable Read BEDROCK_MODEL_ID and BEDROCK_MAX_TOKENS from process.env so ops can swap models or raise token caps without a code deploy. Defaults align with production: eu.anthropic.claude-sonnet-4-6 in eu-north-1, 8192 tokens (up from 4096 — enough headroom for 20+ line-item invoices that were still occasionally truncating mid-JSON). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(invoice-inbox): guard onFieldsUpdated, tighten env parsing, hoist afterAll Three fixes from Greptile review on #416: 1. Stale-closure bug in onFieldsUpdated. setSelected used an unconditional spread, so a PATCH that completed after the user had navigated to a different item would silently overwrite the new selection's extracted_data with the old item's payload. Guard with prev?.id === selected.id (capturing selected.id outside the updater so it's stable through both setSelected and setItems). 2. BEDROCK_MAX_TOKENS env parser used `Number(...) || 8192`, which silently treats a deliberate `0` the same as unset. Use Number.isFinite(n) && n > 0 so an invalid configuration is not masked by the fallback. 3. Hoist `afterAll` import in the test file to the top alongside other vitest imports. Was working via ES-module hoisting but reads weird to humans and linters. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
9c13586b13
commit
8a1c7f0714
@@ -452,10 +452,18 @@ export default function InvoiceInboxWorkspace(_props: WorkspaceComponentProps) {
|
||||
onAttach={() => setAttachOpen(true)}
|
||||
isDeleting={isDeleting}
|
||||
onFieldsUpdated={(nextData) => {
|
||||
setSelected((prev) => (prev ? { ...prev, extracted_data: nextData } : prev))
|
||||
// Guard against stale closure: if the user navigated to a
|
||||
// different item between sending the PATCH and the response
|
||||
// arriving, the captured `selected` is no longer the
|
||||
// currently-selected one. Without the id check we'd write
|
||||
// item A's payload onto item B's row.
|
||||
const targetId = selected.id
|
||||
setSelected((prev) =>
|
||||
prev?.id === targetId ? { ...prev, extracted_data: nextData } : prev
|
||||
)
|
||||
setItems((prev) =>
|
||||
prev.map((it) =>
|
||||
it.id === selected.id ? { ...it, extracted_data: nextData } : it
|
||||
it.id === targetId ? { ...it, extracted_data: nextData } : it
|
||||
)
|
||||
)
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user