refactor(board-view): extract BoardHeader component

This commit is contained in:
Kacper
2025-12-15 23:36:49 +01:00
parent 2880314931
commit 091c6b2737
2 changed files with 116 additions and 70 deletions

View File

@@ -63,6 +63,7 @@ import { FeatureSuggestionsDialog } from "./feature-suggestions-dialog";
import { BoardBackgroundModal } from "@/components/dialogs/board-background-modal";
import { AddFeatureDialog } from "./board-view/AddFeatureDialog";
import { EditFeatureDialog } from "./board-view/EditFeatureDialog";
import { BoardHeader } from "./board-view/BoardHeader";
import {
Plus,
RefreshCw,
@@ -1718,76 +1719,17 @@ export function BoardView() {
data-testid="board-view"
>
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
<div>
<h1 className="text-xl font-bold">Kanban Board</h1>
<p className="text-sm text-muted-foreground">{currentProject.name}</p>
</div>
<div className="flex gap-2 items-center">
{/* Concurrency Slider - only show after mount to prevent hydration issues */}
{isMounted && (
<div
className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-secondary border border-border"
data-testid="concurrency-slider-container"
>
<Users className="w-4 h-4 text-muted-foreground" />
<Slider
value={[maxConcurrency]}
onValueChange={(value) => setMaxConcurrency(value[0])}
min={1}
max={10}
step={1}
className="w-20"
data-testid="concurrency-slider"
/>
<span
className="text-sm text-muted-foreground min-w-[2ch] text-center"
data-testid="concurrency-value"
>
{maxConcurrency}
</span>
</div>
)}
{/* Auto Mode Toggle - only show after mount to prevent hydration issues */}
{isMounted && (
<>
{autoMode.isRunning ? (
<Button
variant="destructive"
size="sm"
onClick={() => autoMode.stop()}
data-testid="stop-auto-mode"
>
<StopCircle className="w-4 h-4 mr-2" />
Stop Auto Mode
</Button>
) : (
<Button
variant="secondary"
size="sm"
onClick={() => autoMode.start()}
data-testid="start-auto-mode"
>
<Play className="w-4 h-4 mr-2" />
Auto Mode
</Button>
)}
</>
)}
<HotkeyButton
size="sm"
onClick={() => setShowAddDialog(true)}
hotkey={shortcuts.addFeature}
hotkeyActive={false}
data-testid="add-feature-button"
>
<Plus className="w-4 h-4 mr-2" />
Add Feature
</HotkeyButton>
</div>
</div>
<BoardHeader
projectName={currentProject.name}
maxConcurrency={maxConcurrency}
onConcurrencyChange={setMaxConcurrency}
isAutoModeRunning={autoMode.isRunning}
onStartAutoMode={() => autoMode.start()}
onStopAutoMode={() => autoMode.stop()}
onAddFeature={() => setShowAddDialog(true)}
addFeatureShortcut={shortcuts.addFeature}
isMounted={isMounted}
/>
{/* Main Content Area */}
<div className="flex-1 flex flex-col overflow-hidden">

View File

@@ -0,0 +1,104 @@
"use client";
import { Button } from "@/components/ui/button";
import { HotkeyButton } from "@/components/ui/hotkey-button";
import { Slider } from "@/components/ui/slider";
import { Play, StopCircle, Plus, Users } from "lucide-react";
import { KeyboardShortcut } from "@/hooks/use-keyboard-shortcuts";
interface BoardHeaderProps {
projectName: string;
maxConcurrency: number;
onConcurrencyChange: (value: number) => void;
isAutoModeRunning: boolean;
onStartAutoMode: () => void;
onStopAutoMode: () => void;
onAddFeature: () => void;
addFeatureShortcut: KeyboardShortcut;
isMounted: boolean;
}
export function BoardHeader({
projectName,
maxConcurrency,
onConcurrencyChange,
isAutoModeRunning,
onStartAutoMode,
onStopAutoMode,
onAddFeature,
addFeatureShortcut,
isMounted,
}: BoardHeaderProps) {
return (
<div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
<div>
<h1 className="text-xl font-bold">Kanban Board</h1>
<p className="text-sm text-muted-foreground">{projectName}</p>
</div>
<div className="flex gap-2 items-center">
{/* Concurrency Slider - only show after mount to prevent hydration issues */}
{isMounted && (
<div
className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-secondary border border-border"
data-testid="concurrency-slider-container"
>
<Users className="w-4 h-4 text-muted-foreground" />
<Slider
value={[maxConcurrency]}
onValueChange={(value) => onConcurrencyChange(value[0])}
min={1}
max={10}
step={1}
className="w-20"
data-testid="concurrency-slider"
/>
<span
className="text-sm text-muted-foreground min-w-[2ch] text-center"
data-testid="concurrency-value"
>
{maxConcurrency}
</span>
</div>
)}
{/* Auto Mode Toggle - only show after mount to prevent hydration issues */}
{isMounted && (
<>
{isAutoModeRunning ? (
<Button
variant="destructive"
size="sm"
onClick={onStopAutoMode}
data-testid="stop-auto-mode"
>
<StopCircle className="w-4 h-4 mr-2" />
Stop Auto Mode
</Button>
) : (
<Button
variant="secondary"
size="sm"
onClick={onStartAutoMode}
data-testid="start-auto-mode"
>
<Play className="w-4 h-4 mr-2" />
Auto Mode
</Button>
)}
</>
)}
<HotkeyButton
size="sm"
onClick={onAddFeature}
hotkey={addFeatureShortcut}
hotkeyActive={false}
data-testid="add-feature-button"
>
<Plus className="w-4 h-4 mr-2" />
Add Feature
</HotkeyButton>
</div>
</div>
);
}