Skip to content

Commit c967c89

Browse files
refactor: decompose KanbanBoard God Component into focused hooks (#147)
* refactor: seal useRepositorySearch API — replace raw setters with semantic actions (#143) - Replace setSearchQuery with updateSearch - Replace setSelectedRepos([]) with clearSelection - Replace setIsAdding(true/false) with startAdding/finishAdding - Replace setAddError with setAddingError/clearError - All actions wrapped in useCallback for referential stability - startAdding() combines setIsAdding(true) + clearError() atomically * refactor: extract useKanbanDnD hook from KanbanBoard (#141, #145) - Extract ~195 lines of DnD logic into src/hooks/board/useKanbanDnD.ts - Move forgivingCollisionDetection, sensors, drag handlers, grid calculations - Column sub-handlers (handleNewRowDrop, handleColumnInsertDrop, handleColumnSwap) included - handleDragEnd split into column/card branches per #145 - History state stays in KanbanBoard via pushCardHistory/pushColumnHistory callbacks - All handlers wrapped in useCallback with correct dependency arrays * refactor: extract useKanbanUndo hook from KanbanBoard (#142) - Extract history/columnHistory state, handleUndo, Z-key useEffect - Hook is self-contained: keyboard shortcut registered internally - Returns pushCardHistory/pushColumnHistory for useKanbanDnD consumption - KanbanBoard no longer imports setStatusLists/setRepoCards directly - Removes ~73 lines from KanbanBoard * refactor: extract useCommentState hook from KanbanBoard (#144) - Extract comments state + 3 CRUD handlers into useCommentState hook - Preserve optimistic updates, Sentry error tracking, and toast feedback - KanbanBoard now imports zero direct server actions - Removed unused imports: useState, useCallback, Sentry, toast, CommentColor - KanbanBoard reduced from 825 → 295 lines (completes decomposition) * fix: address CodeRabbit review — error handling + Z-key modifier guard - useCommentState: convert unreachable try/catch to result.success pattern (server actions return ActionResult, never throw) - useCommentState: add rollback + toast on all 3 handlers failure path - useCommentState: remove duplicate Sentry reporting (server already reports) - useKanbanUndo: guard Z-key handler with !ctrlKey && !metaKey && !altKey to avoid conflict with browser Ctrl+Z undo * refactor: replace dynamic import with static for updateStatusListPosition Address CodeRabbit nitpick: align updateStatusListPosition import with other board actions that are already statically imported.
1 parent 4e3451a commit c967c89

File tree

7 files changed

+932
-620
lines changed

7 files changed

+932
-620
lines changed

src/components/Board/AddRepositoryCombobox.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,17 @@ export const AddRepositoryCombobox = memo(function AddRepositoryCombobox({
106106
// --- Extracted hooks ---
107107
const {
108108
searchQuery,
109-
setSearchQuery,
109+
updateSearch,
110110
deferredSearchQuery,
111111
selectedRepos,
112-
setSelectedRepos,
113112
isAdding,
114-
setIsAdding,
113+
startAdding,
114+
finishAdding,
115115
addError,
116-
setAddError,
116+
setAddingError,
117117
toggleRepoSelection,
118118
removeSelectedRepo,
119+
clearSelection,
119120
} = useRepositorySearch()
120121

121122
const { currentUser, filteredOrganizations, isLoadingOrgs, organizations } =
@@ -253,8 +254,7 @@ export const AddRepositoryCombobox = memo(function AddRepositoryCombobox({
253254
if (selectedRepos.length === 0) return
254255

255256
try {
256-
setIsAdding(true)
257-
setAddError(null)
257+
startAdding()
258258

259259
// Server Action: Add repositories to board with duplicate detection
260260
const result = await addRepositoriesToBoard(
@@ -264,7 +264,9 @@ export const AddRepositoryCombobox = memo(function AddRepositoryCombobox({
264264
)
265265

266266
if (!result.success) {
267-
setAddError(result.errors?.join(', ') || 'Failed to add repositories')
267+
setAddingError(
268+
result.errors?.join(', ') || 'Failed to add repositories',
269+
)
268270
return
269271
}
270272

@@ -287,8 +289,8 @@ export const AddRepositoryCombobox = memo(function AddRepositoryCombobox({
287289
)
288290

289291
// Success: clear selection and close combobox
290-
setSelectedRepos([])
291-
setSearchQuery('')
292+
clearSelection()
293+
updateSearch('')
292294
// Close combobox (supports both controlled and uncontrolled modes)
293295
if (onOpenChange) {
294296
onOpenChange(false)
@@ -304,11 +306,11 @@ export const AddRepositoryCombobox = memo(function AddRepositoryCombobox({
304306
onQuickNoteFocus()
305307
}, 100)
306308
} catch (err) {
307-
setAddError(
309+
setAddingError(
308310
err instanceof Error ? err.message : 'Error adding repositories',
309311
)
310312
} finally {
311-
setIsAdding(false)
313+
finishAdding()
312314
}
313315
}
314316

@@ -367,7 +369,7 @@ export const AddRepositoryCombobox = memo(function AddRepositoryCombobox({
367369
ref={searchInputRef}
368370
type="text"
369371
value={searchQuery}
370-
onChange={(e) => setSearchQuery(e.target.value)}
372+
onChange={(e) => updateSearch(e.target.value)}
371373
placeholder="Search repositories..."
372374
className="border-input bg-background text-foreground focus:border-primary focus:ring-primary w-full rounded-md border px-3 py-2 text-sm focus:ring-1 focus:outline-none"
373375
aria-label="Search repositories"

0 commit comments

Comments
 (0)