ui: single header bar with sort left, filters + status tabs right
This commit is contained in:
parent
98a567e752
commit
fd878f7701
1 changed files with 90 additions and 87 deletions
|
|
@ -790,99 +790,102 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users }
|
|||
<Card>
|
||||
{/* 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]) => (
|
||||
<div className="flex items-center gap-2 flex-wrap rounded-md border border-border bg-muted/30 px-3 py-2 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>
|
||||
))}
|
||||
|
||||
{/* Spacer pushes right side */}
|
||||
<span className="flex-1" />
|
||||
|
||||
{/* 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="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>
|
||||
)}
|
||||
|
||||
<span className="mx-1 h-4 w-px bg-border" />
|
||||
|
||||
{/* Transfer button */}
|
||||
{isPrivileged && visibleTasks.length > 0 && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="gap-1.5 h-7 text-xs"
|
||||
onClick={() => setTransferOpen(true)}
|
||||
>
|
||||
<ArrowRightLeft className="h-3 w-3" />
|
||||
Transfer{cardFilter ? ` ${visibleTasks.length}` : ''}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Assigned / Completed / All */}
|
||||
<div className="flex rounded-md border border-border overflow-hidden text-xs">
|
||||
{(['assigned', 'completed', 'all'] as const).map((f) => (
|
||||
<button
|
||||
key={key}
|
||||
onClick={() => handleSort(key)}
|
||||
className={`flex items-center gap-1 px-2.5 py-1 rounded transition-colors font-medium ${
|
||||
sortKey === key
|
||||
key={f}
|
||||
onClick={() => setFilter(f)}
|
||||
className={`px-2.5 py-1 capitalize transition-colors ${
|
||||
filter === f
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
||||
: 'text-muted-foreground hover:bg-muted'
|
||||
}`}
|
||||
>
|
||||
{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" />
|
||||
)}
|
||||
{f}
|
||||
</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>
|
||||
|
||||
{/* 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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue