Repository navigation
fix(dashboard): stop QuotaCutoffModal drafts resetting on parent re-render (#7889) - #7921
diegosouzapw wants to merge 1 commit into
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Thanks for the fast root-cause and fix — I verified it locally and it correctly reproduces and resolves #7889 (checked out the branch, ran the new One thing worth flagging before merge: PR #7909 (@hydraxman) was opened ~55 minutes before this one and fixes the exact same bug in the exact same two files ( Since #7909 is from an external contributor and was submitted first, I'd suggest merging that one and closing this one as subsumed, to keep the credit with the earlier external fix rather than my own duplicate. Happy to go the other way if there's a reason to prefer this implementation instead — let me know. |
|
Closing this as subsumed by #7909 (just merged). @hydraxman opened an equivalent, also-tested fix for the exact same #7889 bug ~55 min earlier — same two files, both CI-green. Since this is my own duplicate of an earlier external contribution, the credit rightly goes to their PR (the project's whole focus is growing the contributor base over anything else). My ref-based approach worked equally well, but theirs landing first is the right outcome. Thanks anyway to everyone who looked at it. |
Summary
Closes #7889
Provider Quota → "Edit cutoffs": the numeric cutoff input reset to the
provider default (or cleared) while the operator was still typing. Reported
against Codex but not specific to it — it hits any connection whose quota
data refreshes often enough during editing.
Root cause:
QuotaCutoffModal'suseEffectthat seeds the localdraftsstate was keyed on[isOpen, windows, current].windows(array)and
current(object) are props whose identity changes on every parentre-render — most directly,
ProviderLimits/index.tsxrebuilds thewindowsarray with a fresh
.map()on every render, and the same happens wheneverthe parent's quota-polling/refresh state updates. React compares hook deps
by reference, so any unrelated parent re-render re-ran the effect mid-edit
and overwrote whatever the operator was typing with the last persisted
value.
Fix: reseed
draftsonly on the real "opened against a (possibly new)connection" signal instead.
QuotaCutoffModalnow takes aconnectionIdprop (the connection's stable id, already used elsewhere by the parent for
saveQuotaWindowThresholds/visibleQuotaDatalookups) and the seedingeffect depends on
[isOpen, connectionId].windows/currentare stillread for the actual seed values, but via refs kept fresh on every render —
so the effect always sees the latest data without re-running just because
those props got new array/object identities.
Root cause / fix
src/app/(dashboard)/dashboard/usage/components/ProviderLimits/QuotaCutoffModal.tsx— added
connectionIdprop, switched the seeding effect's dependencyarray from
[isOpen, windows, current]to[isOpen, connectionId],reading
windows/currentthrough refs updated every render.src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx— threads
connectionId={cutoffModalConn.id}into<QuotaCutoffModal>.Test plan (TDD, Hard Rule #18)
tests/unit/ui/issue-7889-quota-cutoff-input-persists.test.tsx:then re-rendering the parent with brand-new (but content-equivalent)
windows/currentreferences for the same connection, snapped theinput back to the persisted value ("5") — reproducing the report exactly.
connection (
connectionIdchanges) still correctly reseeds the draftsto that connection's persisted values.
node scripts/check/check-file-size.mjs— OK, no✗node scripts/check/check-complexity.mjs— 2081 violations (baseline 2130), no regressioneslint --no-config-lookup --config eslint.sonarjs.config.mjson changed files — cleannpm run typecheck:core— cleaneslint --suppressions-location config/quality/eslint-suppressions.jsonon changed files — exit 0npm run test:vitest:ui— new test passes standalone and combined; 17 pre-existing failures in unrelated files (qdrant-config-card,runtime-page-client, etc.) reproduced only under full-suite load and pass cleanly in isolation — not touched by this change