add use ts hooks

This commit is contained in:
James
2025-12-22 19:29:01 -05:00
parent cc0405cf27
commit cb07206dae
4 changed files with 59 additions and 9 deletions

View File

@@ -43,7 +43,6 @@ interface GraphFilterControlsProps {
filterState: GraphFilterState;
availableCategories: string[];
hasActiveFilter: boolean;
onSearchQueryChange: (query: string) => void;
onCategoriesChange: (categories: string[]) => void;
onStatusesChange: (statuses: string[]) => void;
onNegativeFilterChange: (isNegative: boolean) => void;
@@ -54,7 +53,6 @@ export function GraphFilterControls({
filterState,
availableCategories,
hasActiveFilter,
onSearchQueryChange,
onCategoriesChange,
onStatusesChange,
onNegativeFilterChange,
@@ -62,9 +60,6 @@ export function GraphFilterControls({
}: GraphFilterControlsProps) {
const { selectedCategories, selectedStatuses, isNegativeFilter } = filterState;
// Suppress unused variable warning - onSearchQueryChange is used by parent for search input
void onSearchQueryChange;
const handleCategoryToggle = (category: string) => {
if (selectedCategories.includes(category)) {
onCategoriesChange(selectedCategories.filter((c) => c !== category));
@@ -267,6 +262,12 @@ export function GraphFilterControls({
<div className="flex items-center gap-2">
<button
onClick={() => onNegativeFilterChange(!isNegativeFilter)}
aria-label={
isNegativeFilter
? 'Switch to show matching nodes'
: 'Switch to hide matching nodes'
}
aria-pressed={isNegativeFilter}
className={cn(
'flex items-center gap-1.5 px-2 py-1 rounded text-xs transition-colors',
isNegativeFilter
@@ -289,6 +290,7 @@ export function GraphFilterControls({
<Switch
checked={isNegativeFilter}
onCheckedChange={onNegativeFilterChange}
aria-label="Toggle between show and hide filter modes"
className="h-5 w-9 data-[state=checked]:bg-orange-500"
/>
</div>
@@ -311,6 +313,7 @@ export function GraphFilterControls({
size="sm"
className="h-8 w-8 p-0 text-muted-foreground hover:text-destructive"
onClick={onClearFilters}
aria-label="Clear all filters"
>
<X className="w-4 h-4" />
</Button>

View File

@@ -4,6 +4,7 @@ import {
Background,
BackgroundVariant,
MiniMap,
Panel,
useNodesState,
useEdgesState,
ReactFlowProvider,
@@ -30,6 +31,9 @@ import {
type NodeActionCallbacks,
} from './hooks';
import { cn } from '@/lib/utils';
import { useDebounceValue } from 'usehooks-ts';
import { SearchX } from 'lucide-react';
import { Button } from '@/components/ui/button';
// Define custom node and edge types - using any to avoid React Flow's strict typing
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -71,9 +75,12 @@ function GraphCanvasInner({
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
const [isNegativeFilter, setIsNegativeFilter] = useState(false);
// Debounce search query for performance with large graphs
const [debouncedSearchQuery] = useDebounceValue(searchQuery, 200);
// Combined filter state
const filterState: GraphFilterState = {
searchQuery,
searchQuery: debouncedSearchQuery,
selectedCategories,
selectedStatuses,
isNegativeFilter,
@@ -196,7 +203,6 @@ function GraphCanvasInner({
filterState={filterState}
availableCategories={filterResult.availableCategories}
hasActiveFilter={filterResult.hasActiveFilter}
onSearchQueryChange={onSearchQueryChange}
onCategoriesChange={setSelectedCategories}
onStatusesChange={setSelectedStatuses}
onNegativeFilterChange={setIsNegativeFilter}
@@ -204,6 +210,24 @@ function GraphCanvasInner({
/>
<GraphLegend />
{/* Empty state when all nodes are filtered out */}
{filterResult.hasActiveFilter && filterResult.matchedNodeIds.size === 0 && (
<Panel position="top-center" className="mt-20">
<div className="flex flex-col items-center gap-3 p-6 rounded-lg bg-popover/95 backdrop-blur-sm border border-border shadow-lg text-popover-foreground">
<SearchX className="w-10 h-10 text-muted-foreground" />
<div className="text-center">
<p className="text-sm font-medium">No matching tasks</p>
<p className="text-xs text-muted-foreground mt-1">
Try adjusting your filters or search query
</p>
</div>
<Button variant="outline" size="sm" onClick={handleClearFilters} className="mt-1">
Clear Filters
</Button>
</div>
</Panel>
)}
</ReactFlow>
</div>
);