refactor(pm-wizard): extract generic hooks — eliminate 58% code duplication#1183
refactor(pm-wizard): extract generic hooks — eliminate 58% code duplication#1183
Conversation
CI Failures ResolvedFixes Applied
Verification
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
nhopeatall
left a comment
There was a problem hiding this comment.
Summary
The extraction of generic logic and the config builders is very clean, and the tests are great! However, I found a critical issue with unused code that introduces broken schema assumptions, and a regression in Trello custom field validation.
Architecture & Design
-
[BLOCKING] Broken, dead code (
useProviderDiscovery): The PR introducesuseProviderDiscovery,DiscoveryConfig, andhasCredentials, but leaves them unused. More importantly, this generic hook is fundamentally broken. It attempts to calltrpcClient.pm.discovery.discover.mutate({ capability:${config.capability.replace(/s$ /, "")}Detailsas DiscoveryCapability }). Thepm.discovery.discovercapability argument relies on theDISCOVERY_CAPABILITIESarray (src/pm/types.ts), which does not containboardDetails,projectDetails, orteamDetails. If this hook were ever used, it would crash instantly with a Zod400 BAD_REQUESTvalidation error because the string type coercion (as DiscoveryCapability) masks the real schema. Since you intentionally kept the per-provider discovery hooks due to endpoint shape differences, this broken code should be entirely deleted rather than merged.
Code Issues
Blocking
web/src/components/projects/pm-wizard-hooks.ts:734— Trello Custom Field Validation Regression:
The new genericuseProviderCustomFieldCreationremoves the Trello validation check (if (!state.trelloBoardId) throw new Error(...)). Instead, it falls back silently withconst containerId = config.getContainerId(state) || "global";.
While JIRA custom fields are global, Trello requires a valid Board ID. Passing"global"to the Trello mutation bypasses the client-side check and will cause an API failure.
Fix: AddcontainerError?: stringtoCustomFieldCreationConfig, pass"Board must be selected before creating a custom field"fromuseTrelloCustomFieldCreation, and inside the mutation throwconfig.containerErrorif!containerId(mirroring howLabelCreationConfighandles it).
Nitpicks
web/src/components/projects/pm-wizard-hooks.ts:777—buildVerifyAuthArgis now just a useless 1-line wrapper aroundbuildProviderAuthArgthat is marked@deprecated. Since it is an unexported internal function, you should simply delete it and usebuildProviderAuthArgdirectly inuseVerificationright below it.
🕵️ opencode · google/gemini-3.1-pro-preview · run details
Review Feedback AddressedChanges Made
Verification
|
…ello CF validation - Delete broken `useProviderDiscovery`, `DiscoveryConfig`, and `hasCredentials` (the hook constructed invalid capability strings like 'boardDetails' that do not exist in DISCOVERY_CAPABILITIES, causing guaranteed Zod 400 errors; per-provider discovery hooks are kept as intentionally correct) - Restore Trello custom field validation: add `containerError?` to `CustomFieldCreationConfig` and throw it in `useProviderCustomFieldCreation` when `containerId` is empty, mirroring `LabelCreationConfig`; pass the error message from `useTrelloCustomFieldCreation` so the silent `|| 'global'` fallback can no longer bypass the board-required check - Delete the deprecated one-liner `buildVerifyAuthArg` wrapper; call `buildProviderAuthArg` directly in `useVerification` - Update file-level docstring, imports (remove unused `DiscoveryCapability`), and bottom re-exports to reflect deleted symbols Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nhopeatall
left a comment
There was a problem hiding this comment.
Summary
The refactoring successfully eliminates a substantial amount of duplicated logic across the PM provider hooks by introducing solid, generalized generic hooks (useProviderLabelCreation and useProviderCustomFieldCreation) and centralizing configuration construction.
I've verified that the extracted configuration builders (buildTrelloIntegrationConfig, buildJiraIntegrationConfig, and buildLinearIntegrationConfig) perfectly match the prior inline objects, and the credential saving paths are fully preserved. Extracting pure functions for testability is a great pattern here. LGTM!
🕵️ opencode · google/gemini-3.1-pro-preview · run details
Summary
Refactors
pm-wizard-hooks.tsto eliminate ~58% code duplication by extracting three parameterized generic hooks and two pure config builder functions.Card: https://trello.com/c/ZjELfcNl/626-refactor-pm-wizard-hooksts-extract-generic-provider-hooks-to-eliminate-58-code-duplication
What was implemented
buildProviderAuthArg(state, projectId)— single auth-arg builder replacing 6+ copies of theshouldUseStoredCredentials → { projectId } / { credentials }branching pattern across every mutationuseProviderLabelCreation(config, ...)— generic parameterized hook replacing the structurally identicaluseTrelloLabelCreationanduseLinearLabelCreationimplementations (~120 lines saved). Both are now thin wrappers.useProviderCustomFieldCreation(config, ...)— generic parameterized hook replacinguseTrelloCustomFieldCreationanduseJiraCustomFieldCreation(~60 lines saved). Both are now thin wrappers with provider-specific error messages.useSaveMutation— refactored from a 3-wayif-elsecascade to data-drivenSAVE_CONFIGSmap, removing thebiome-ignore noExcessiveCognitiveComplexityannotationbuildTrelloIntegrationConfig+buildJiraIntegrationConfig— pure functions extracted topm-wizard-state.ts(alongside the existingbuildLinearIntegrationConfig), used by theSAVE_CONFIGSmap and independently testableWhat was not changed (intentionally)
useTrelloDiscovery,useJiraDiscovery,useLinearDiscovery) are kept as-is because they have provider-specific detail-fetch endpoints (trelloBoardDetails,jiraProjectDetails,linearTeamDetails) that use different tRPC procedures with different shapes — a genericuseProviderDiscoverywas added but the per-provider wrappers remain for correctness and backward compatibilitypm-wizard-hooks.tsremain backward-compatible — providerwizard.tsfiles required zero import changesTesting
tests/unit/web/pm-wizard-hooks.test.tscoveringbuildProviderAuthArg(3 providers × stored/fresh/error),buildTrelloIntegrationConfig,buildJiraIntegrationConfig,buildLinearIntegrationConfig, andrunPerLabelCreations(success, partial error, empty, arg forwarding)biome-ignore noExcessiveCognitiveComplexityannotation fromuseSaveMutation)🤖 Generated with Claude Code
🕵️ claude-code · claude-sonnet-4-6 · run details