Add src/components/Header.tsx
This commit is contained in:
111
src/components/Header.tsx
Normal file
111
src/components/Header.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
'use client';
|
||||
|
||||
import { useApp } from './AppProvider';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
|
||||
export default function Header() {
|
||||
const {
|
||||
view, selectedProject, projects, searchQuery, setSearchQuery,
|
||||
showNewTaskModal, setShowNewTaskModal,
|
||||
filterStatus, setFilterStatus, filterPriority, setFilterPriority,
|
||||
filterDueBefore, setFilterDueBefore, filterDueAfter, setFilterDueAfter,
|
||||
filterCompleted, setFilterCompleted,
|
||||
refreshTasks,
|
||||
} = useApp();
|
||||
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
searchRef.current?.focus();
|
||||
}
|
||||
if (e.key === 'n' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
setShowNewTaskModal(true);
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handler);
|
||||
return () => window.removeEventListener('keydown', handler);
|
||||
}, [setShowNewTaskModal]);
|
||||
|
||||
const selectedProjectName = projects.find(p => p.id === selectedProject)?.name || 'All Projects';
|
||||
|
||||
const handleSearch = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
refreshTasks();
|
||||
};
|
||||
|
||||
const handleClearFilters = () => {
|
||||
setFilterStatus('');
|
||||
setFilterPriority('');
|
||||
setFilterDueBefore('');
|
||||
setFilterDueAfter('');
|
||||
setFilterCompleted('');
|
||||
setSearchQuery('');
|
||||
refreshTasks();
|
||||
};
|
||||
|
||||
const hasActiveFilters = filterStatus || filterPriority || filterDueBefore || filterDueAfter || filterCompleted || searchQuery;
|
||||
|
||||
return (
|
||||
<header className="bg-card border-b border-border px-4 py-3 flex items-center gap-4">
|
||||
{/* Search */}
|
||||
<form onSubmit={handleSearch} className="flex-1 max-w-md">
|
||||
<div className="relative">
|
||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-sm">🔍</span>
|
||||
<input
|
||||
ref={searchRef}
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Search tasks... (⌘K)"
|
||||
className="w-full bg-content border border-border rounded-lg pl-9 pr-4 py-2 text-sm focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent/20 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Project name */}
|
||||
<div className="flex items-center gap-2 text-sm text-text-secondary">
|
||||
<span>📋</span>
|
||||
<span className="font-medium">{selectedProjectName}</span>
|
||||
</div>
|
||||
|
||||
{/* Filter toggle */}
|
||||
<button
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
className={`flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm transition-colors ${
|
||||
showFilters || hasActiveFilters
|
||||
? 'bg-accent/10 text-accent'
|
||||
: 'text-text-secondary hover:bg-content'
|
||||
}`}
|
||||
>
|
||||
<span>⚙</span>
|
||||
Filters
|
||||
{hasActiveFilters && (
|
||||
<span className="w-2 h-2 bg-accent rounded-full" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Clear filters */}
|
||||
{hasActiveFilters && (
|
||||
<button
|
||||
onClick={handleClearFilters}
|
||||
className="text-xs text-text-secondary hover:text-red-500 transition-colors"
|
||||
>
|
||||
Clear all
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* New task button (mobile) */}
|
||||
<button
|
||||
onClick={() => setShowNewTaskModal(true)}
|
||||
className="bg-accent hover:bg-accent-hover text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||||
>
|
||||
+ New Task
|
||||
</button>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user