mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
refactor(board-view): extract BoardSearchBar component
This commit is contained in:
@@ -64,6 +64,7 @@ import { BoardBackgroundModal } from "@/components/dialogs/board-background-moda
|
|||||||
import { AddFeatureDialog } from "./board-view/AddFeatureDialog";
|
import { AddFeatureDialog } from "./board-view/AddFeatureDialog";
|
||||||
import { EditFeatureDialog } from "./board-view/EditFeatureDialog";
|
import { EditFeatureDialog } from "./board-view/EditFeatureDialog";
|
||||||
import { BoardHeader } from "./board-view/BoardHeader";
|
import { BoardHeader } from "./board-view/BoardHeader";
|
||||||
|
import { BoardSearchBar } from "./board-view/BoardSearchBar";
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
@@ -285,7 +286,6 @@ export function BoardView() {
|
|||||||
const startNextFeaturesRef = useRef<() => void>(() => {});
|
const startNextFeaturesRef = useRef<() => void>(() => {});
|
||||||
|
|
||||||
// Ref for search input to enable keyboard shortcut focus
|
// Ref for search input to enable keyboard shortcut focus
|
||||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
// Keyboard shortcuts for this view
|
// Keyboard shortcuts for this view
|
||||||
const boardShortcuts: KeyboardShortcut[] = useMemo(() => {
|
const boardShortcuts: KeyboardShortcut[] = useMemo(() => {
|
||||||
@@ -300,11 +300,6 @@ export function BoardView() {
|
|||||||
action: () => startNextFeaturesRef.current(),
|
action: () => startNextFeaturesRef.current(),
|
||||||
description: "Start next features from backlog",
|
description: "Start next features from backlog",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: "/",
|
|
||||||
action: () => searchInputRef.current?.focus(),
|
|
||||||
description: "Focus search input",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add shortcuts for in-progress cards (1-9 and 0 for 10th)
|
// Add shortcuts for in-progress cards (1-9 and 0 for 10th)
|
||||||
@@ -1735,51 +1730,13 @@ export function BoardView() {
|
|||||||
<div className="flex-1 flex flex-col overflow-hidden">
|
<div className="flex-1 flex flex-col overflow-hidden">
|
||||||
{/* Search Bar Row */}
|
{/* Search Bar Row */}
|
||||||
<div className="px-4 pt-4 pb-2 flex items-center justify-between">
|
<div className="px-4 pt-4 pb-2 flex items-center justify-between">
|
||||||
<div className="relative max-w-md flex-1 flex items-center gap-2">
|
<BoardSearchBar
|
||||||
<div className="relative flex-1">
|
searchQuery={searchQuery}
|
||||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground pointer-events-none" />
|
onSearchChange={setSearchQuery}
|
||||||
<Input
|
isCreatingSpec={isCreatingSpec}
|
||||||
ref={searchInputRef}
|
creatingSpecProjectPath={creatingSpecProjectPath}
|
||||||
type="text"
|
currentProjectPath={currentProject?.path}
|
||||||
placeholder="Search features by keyword..."
|
/>
|
||||||
value={searchQuery}
|
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
|
||||||
className="pl-9 pr-12 border-border"
|
|
||||||
data-testid="kanban-search-input"
|
|
||||||
/>
|
|
||||||
{searchQuery ? (
|
|
||||||
<button
|
|
||||||
onClick={() => setSearchQuery("")}
|
|
||||||
className="absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded-sm hover:bg-accent text-muted-foreground hover:text-foreground transition-colors"
|
|
||||||
data-testid="kanban-search-clear"
|
|
||||||
aria-label="Clear search"
|
|
||||||
>
|
|
||||||
<X className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<span
|
|
||||||
className="absolute right-2 top-1/2 -translate-y-1/2 px-1.5 py-0.5 text-[10px] font-mono rounded bg-brand-500/10 border border-brand-500/30 text-brand-400/70"
|
|
||||||
data-testid="kanban-search-hotkey"
|
|
||||||
>
|
|
||||||
/
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* Spec Creation Loading Badge */}
|
|
||||||
{isCreatingSpec &&
|
|
||||||
currentProject?.path === creatingSpecProjectPath && (
|
|
||||||
<div
|
|
||||||
className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-brand-500/10 border border-brand-500/20 shrink-0"
|
|
||||||
title="Creating App Specification"
|
|
||||||
data-testid="spec-creation-badge"
|
|
||||||
>
|
|
||||||
<Loader2 className="w-3 h-3 animate-spin text-brand-500 shrink-0" />
|
|
||||||
<span className="text-xs font-medium text-brand-500 whitespace-nowrap">
|
|
||||||
Creating spec
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Board Background & Detail Level Controls */}
|
{/* Board Background & Detail Level Controls */}
|
||||||
{isMounted && (
|
{isMounted && (
|
||||||
|
|||||||
89
apps/app/src/components/views/board-view/BoardSearchBar.tsx
Normal file
89
apps/app/src/components/views/board-view/BoardSearchBar.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useRef, useEffect } from "react";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Search, X, Loader2 } from "lucide-react";
|
||||||
|
|
||||||
|
interface BoardSearchBarProps {
|
||||||
|
searchQuery: string;
|
||||||
|
onSearchChange: (query: string) => void;
|
||||||
|
isCreatingSpec: boolean;
|
||||||
|
creatingSpecProjectPath?: string;
|
||||||
|
currentProjectPath?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BoardSearchBar({
|
||||||
|
searchQuery,
|
||||||
|
onSearchChange,
|
||||||
|
isCreatingSpec,
|
||||||
|
creatingSpecProjectPath,
|
||||||
|
currentProjectPath,
|
||||||
|
}: BoardSearchBarProps) {
|
||||||
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
// Focus search input when "/" is pressed
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
// Only focus if not typing in an input/textarea
|
||||||
|
if (
|
||||||
|
e.key === "/" &&
|
||||||
|
!(e.target instanceof HTMLInputElement) &&
|
||||||
|
!(e.target instanceof HTMLTextAreaElement)
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
searchInputRef.current?.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative max-w-md flex-1 flex items-center gap-2">
|
||||||
|
<div className="relative flex-1">
|
||||||
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground pointer-events-none" />
|
||||||
|
<Input
|
||||||
|
ref={searchInputRef}
|
||||||
|
type="text"
|
||||||
|
placeholder="Search features by keyword..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => onSearchChange(e.target.value)}
|
||||||
|
className="pl-9 pr-12 border-border"
|
||||||
|
data-testid="kanban-search-input"
|
||||||
|
/>
|
||||||
|
{searchQuery ? (
|
||||||
|
<button
|
||||||
|
onClick={() => onSearchChange("")}
|
||||||
|
className="absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded-sm hover:bg-accent text-muted-foreground hover:text-foreground transition-colors"
|
||||||
|
data-testid="kanban-search-clear"
|
||||||
|
aria-label="Clear search"
|
||||||
|
>
|
||||||
|
<X className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span
|
||||||
|
className="absolute right-2 top-1/2 -translate-y-1/2 px-1.5 py-0.5 text-[10px] font-mono rounded bg-brand-500/10 border border-brand-500/30 text-brand-400/70"
|
||||||
|
data-testid="kanban-search-hotkey"
|
||||||
|
>
|
||||||
|
/
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{/* Spec Creation Loading Badge */}
|
||||||
|
{isCreatingSpec &&
|
||||||
|
currentProjectPath === creatingSpecProjectPath && (
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-brand-500/10 border border-brand-500/20 shrink-0"
|
||||||
|
title="Creating App Specification"
|
||||||
|
data-testid="spec-creation-badge"
|
||||||
|
>
|
||||||
|
<Loader2 className="w-3 h-3 animate-spin text-brand-500 shrink-0" />
|
||||||
|
<span className="text-xs font-medium text-brand-500 whitespace-nowrap">
|
||||||
|
Creating spec
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user