From cccb0e4ca7692e976b458829eafa9d404f6deac7 Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Tue, 18 Aug 2026 18:09:23 -0300 Subject: [PATCH] refactor(vendors): extract trade rejection message to satisfy length gate (SH-249) The loading-aware gate pushed VendorTradeSpecialtiesField to 151 lines, one over the changed-file maintainability cap. Move the rejection-message resolution and the contract explanation to module scope; no behavior change. --- .../vendor-trade-specialties-field.tsx | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/app/(protected)/vendors/_components/vendor-trade-specialties-field.tsx b/src/app/(protected)/vendors/_components/vendor-trade-specialties-field.tsx index ef7b0043..11a7ea4b 100644 --- a/src/app/(protected)/vendors/_components/vendor-trade-specialties-field.tsx +++ b/src/app/(protected)/vendors/_components/vendor-trade-specialties-field.tsx @@ -84,6 +84,22 @@ interface VendorTradeSpecialtiesFieldProps { tradeOptionsLoading?: boolean; } +/** + * SH-249: an empty tradeOptions array means two different things. While the facets + * query is in flight it means "not loaded yet"; only once it settles does it mean + * "vocabulary genuinely unavailable". Failing open during the cold-fetch window let + * free text through the canonical gate permanently, because anything committed there + * is added to knownTradesRef and stays selectable after the real list arrives. + * + * SH-249: why a typed trade was refused. Extracted to module scope so the field + * component stays inside the maintainability gate's function-length cap. + */ +function resolveRejectionMessage(value: string, loading: boolean): string { + return loading + ? "Trades are still loading. Wait for the list, then pick a trade." + : `"${value}" is not in the trades list. Pick a trade from the list.`; +} + export function VendorTradeSpecialtiesField({ control, index, @@ -93,12 +109,6 @@ export function VendorTradeSpecialtiesField({ const [tradeInput, setTradeInput] = useState(""); const [feedbackMessage, setFeedbackMessage] = useState(null); const knownTradesRef = useRef>(new Set()); - // SH-249: an empty tradeOptions array means two different things. While the - // facets query is in flight it means "not loaded yet"; only once it settles - // does it mean "vocabulary genuinely unavailable". Failing open during the - // cold-fetch window let free text through the canonical gate permanently, - // because anything committed there is added to knownTradesRef and stays - // selectable after the real list arrives. const tradeListUnavailable = !tradeOptionsLoading && tradeOptions.length === 0; const gateActive = tradeOptions.length > 0 || tradeOptionsLoading; @@ -125,11 +135,7 @@ export function VendorTradeSpecialtiesField({ } const canonical = matchTradeOption(normalized, selectableTrades); if (gateActive && !canonical) { - setFeedbackMessage( - tradeOptionsLoading - ? "Trades are still loading. Wait for the list, then pick a trade." - : `"${normalized}" is not in the trades list. Pick a trade from the list.`, - ); + setFeedbackMessage(resolveRejectionMessage(normalized, tradeOptionsLoading)); return; } const trade = canonical ?? normalized;