ui: merge sort/filter bar with status tabs in single header row, remove 'All Tasks' title

This commit is contained in:
lorentz 2026-04-08 22:40:18 +00:00
parent d82b3dc611
commit 98a567e752

View file

@ -788,111 +788,102 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users }
{/* Task list */}
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 flex-wrap gap-2">
<CardTitle className="flex items-center gap-2">
All Tasks
{cardFilter && (
<span className="text-xs font-normal text-muted-foreground">
({visibleTasks.length} shown)
</span>
)}
</CardTitle>
<div className="flex items-center gap-2">
{isPrivileged && visibleTasks.length > 0 && (
<Button
size="sm"
variant="outline"
className="gap-1.5"
onClick={() => setTransferOpen(true)}
>
<ArrowRightLeft className="h-3.5 w-3.5" />
Transfer{cardFilter ? ` ${visibleTasks.length}` : ''}
</Button>
)}
<div className="flex rounded-md border border-border overflow-hidden text-sm">
{(['assigned', 'completed', 'all'] as const).map((f) => (
{/* Sort / filter / status header */}
<div className="px-6 pt-5 pb-3">
<div className="flex items-center justify-between gap-3 flex-wrap">
{/* Sort & column filter controls */}
<div className="flex items-center gap-2 flex-wrap rounded-md border border-border bg-muted/30 px-2 py-1.5 text-xs">
{([
['client', 'Client'],
['date', 'Date'],
['type', 'Type'],
['priority', 'Level'],
] as const).map(([key, label]) => (
<button
key={f}
onClick={() => setFilter(f)}
className={`px-3 py-1.5 capitalize transition-colors ${
filter === f
key={key}
onClick={() => handleSort(key)}
className={`flex items-center gap-1 px-2.5 py-1 rounded transition-colors font-medium ${
sortKey === key
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:bg-muted'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{f}
{label}
{sortKey === key ? (
sortDir === 'asc' ? <ArrowUp className="h-3 w-3" /> : <ArrowDown className="h-3 w-3" />
) : (
<ArrowUpDown className="h-3 w-3 opacity-40" />
)}
</button>
))}
<span className="mx-1 h-4 w-px bg-border" />
<Select value={typeFilter} onValueChange={(v) => setTypeFilter(v as any)}>
<SelectTrigger className="h-7 w-[100px] text-xs border-0 bg-transparent shadow-none px-2">
<Filter className="h-3 w-3 mr-1 opacity-60" />
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Types</SelectItem>
<SelectItem value="Group">Group</SelectItem>
<SelectItem value="Policy">Policy</SelectItem>
<SelectItem value="Client">Client</SelectItem>
</SelectContent>
</Select>
<Select value={priorityFilter} onValueChange={(v) => setPriorityFilter(v as any)}>
<SelectTrigger className="h-7 w-[110px] text-xs border-0 bg-transparent shadow-none px-2">
<Filter className="h-3 w-3 mr-1 opacity-60" />
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Levels</SelectItem>
<SelectItem value="HIGH">High</SelectItem>
<SelectItem value="MEDIUM">Medium</SelectItem>
<SelectItem value="LOW">Low</SelectItem>
</SelectContent>
</Select>
{(typeFilter !== 'all' || priorityFilter !== 'all') && (
<button
onClick={() => { setTypeFilter('all'); setPriorityFilter('all') }}
className="ml-1 flex items-center gap-1 px-2 py-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
>
<X className="h-3 w-3" /> Clear
</button>
)}
</div>
</div>
</CardHeader>
{/* Column sort & filter header */}
<div className="px-6 pb-2">
<div className="flex items-center gap-1 flex-wrap rounded-md border border-border bg-muted/30 px-2 py-1.5 text-xs">
{/* Sort buttons */}
{([
['client', 'Client'],
['date', 'Date'],
['type', 'Type'],
['priority', 'Level'],
] as const).map(([key, label]) => (
<button
key={key}
onClick={() => handleSort(key)}
className={`flex items-center gap-1 px-2.5 py-1 rounded transition-colors font-medium ${
sortKey === key
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{label}
{sortKey === key ? (
sortDir === 'asc' ? <ArrowUp className="h-3 w-3" /> : <ArrowDown className="h-3 w-3" />
) : (
<ArrowUpDown className="h-3 w-3 opacity-40" />
)}
</button>
))}
<span className="mx-1 h-4 w-px bg-border" />
{/* Type filter */}
<Select value={typeFilter} onValueChange={(v) => setTypeFilter(v as any)}>
<SelectTrigger className="h-7 w-[100px] text-xs border-0 bg-transparent shadow-none px-2">
<Filter className="h-3 w-3 mr-1 opacity-60" />
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Types</SelectItem>
<SelectItem value="Group">Group</SelectItem>
<SelectItem value="Policy">Policy</SelectItem>
<SelectItem value="Client">Client</SelectItem>
</SelectContent>
</Select>
{/* Priority filter */}
<Select value={priorityFilter} onValueChange={(v) => setPriorityFilter(v as any)}>
<SelectTrigger className="h-7 w-[110px] text-xs border-0 bg-transparent shadow-none px-2">
<Filter className="h-3 w-3 mr-1 opacity-60" />
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Levels</SelectItem>
<SelectItem value="HIGH">High</SelectItem>
<SelectItem value="MEDIUM">Medium</SelectItem>
<SelectItem value="LOW">Low</SelectItem>
</SelectContent>
</Select>
{(typeFilter !== 'all' || priorityFilter !== 'all') && (
<button
onClick={() => { setTypeFilter('all'); setPriorityFilter('all') }}
className="ml-1 flex items-center gap-1 px-2 py-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
>
<X className="h-3 w-3" /> Clear
</button>
)}
{/* Right side: Transfer + Assigned/Completed/All */}
<div className="flex items-center gap-2">
{isPrivileged && visibleTasks.length > 0 && (
<Button
size="sm"
variant="outline"
className="gap-1.5"
onClick={() => setTransferOpen(true)}
>
<ArrowRightLeft className="h-3.5 w-3.5" />
Transfer{cardFilter ? ` ${visibleTasks.length}` : ''}
</Button>
)}
<div className="flex rounded-md border border-border overflow-hidden text-sm">
{(['assigned', 'completed', 'all'] as const).map((f) => (
<button
key={f}
onClick={() => setFilter(f)}
className={`px-3 py-1.5 capitalize transition-colors ${
filter === f
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:bg-muted'
}`}
>
{f}
</button>
))}
</div>
</div>
</div>
</div>