feat: redesign task filter bar UI
This commit is contained in:
parent
b399992dc5
commit
ec9cf347ec
1 changed files with 52 additions and 54 deletions
|
|
@ -1,8 +1,7 @@
|
||||||
import { Filter, RefreshCw, Search } from 'lucide-react'
|
import { RefreshCw, Search } from 'lucide-react'
|
||||||
import { TASK_STATUS_LABELS, TASK_STATUS_OPTIONS, type TaskStatus } from '../types/task'
|
import { TASK_STATUS_LABELS, TASK_STATUS_OPTIONS, type TaskStatus } from '../types/task'
|
||||||
import type { Project } from '../services/projectService'
|
import type { Project } from '../services/projectService'
|
||||||
import type { Category } from '../types/category'
|
import type { Category } from '../types/category'
|
||||||
import { MultiSelectChips } from './MultiSelectChips'
|
|
||||||
import { CategoryIcon } from './CategoryIcon'
|
import { CategoryIcon } from './CategoryIcon'
|
||||||
import { MultiSelectDropdown } from './MultiSelectDropdown'
|
import { MultiSelectDropdown } from './MultiSelectDropdown'
|
||||||
import type { TaskFilters } from './taskFilters'
|
import type { TaskFilters } from './taskFilters'
|
||||||
|
|
@ -68,39 +67,65 @@ export function TaskFilterBar({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
{projects.length > 0 && (
|
{projects.length > 0 && (
|
||||||
<div className="flex items-center gap-2">
|
<MultiSelectDropdown
|
||||||
<Filter size={16} className="shrink-0 text-text-muted" />
|
label="Projetos"
|
||||||
<span className="text-[10px] font-black uppercase tracking-widest text-text-muted whitespace-nowrap">Projetos</span>
|
allLabel="Todos os projetos"
|
||||||
<MultiSelectChips
|
|
||||||
options={projects.map((p) => ({
|
options={projects.map((p) => ({
|
||||||
value: p.id,
|
value: p.id,
|
||||||
label: p.name,
|
label: p.name,
|
||||||
color: p.color ?? undefined,
|
|
||||||
icon: p.icon ? <CategoryIcon iconName={p.icon} size={14} /> : undefined,
|
icon: p.icon ? <CategoryIcon iconName={p.icon} size={14} /> : undefined,
|
||||||
}))}
|
}))}
|
||||||
selected={filters.projectIds}
|
selected={filters.projectIds}
|
||||||
onChange={(projectIds) => update({ projectIds })}
|
onChange={(projectIds) => update({ projectIds })}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<MultiSelectDropdown
|
||||||
<Filter size={16} className="shrink-0 text-text-muted" />
|
label="Status"
|
||||||
<span className="text-[10px] font-black uppercase tracking-widest text-text-muted whitespace-nowrap">Status</span>
|
allLabel="Todos os status"
|
||||||
<MultiSelectChips
|
|
||||||
options={TASK_STATUS_OPTIONS.map((s) => ({
|
options={TASK_STATUS_OPTIONS.map((s) => ({
|
||||||
value: s,
|
value: s,
|
||||||
label: TASK_STATUS_LABELS[s],
|
label: TASK_STATUS_LABELS[s],
|
||||||
color: STATUS_CHIP_COLORS[s],
|
icon: (
|
||||||
|
<div
|
||||||
|
className="h-3 w-3 shrink-0 rounded-full"
|
||||||
|
style={{ backgroundColor: STATUS_CHIP_COLORS[s] }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
}))}
|
}))}
|
||||||
selected={filters.statuses}
|
selected={filters.statuses}
|
||||||
onChange={(statuses) => update({ statuses })}
|
onChange={(statuses) => update({ statuses })}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
{categories.length > 0 && (
|
||||||
|
<MultiSelectDropdown
|
||||||
|
label="Categorias"
|
||||||
|
allLabel="Todas as categorias"
|
||||||
|
options={categories.map((c) => ({
|
||||||
|
value: c.id,
|
||||||
|
label: c.name,
|
||||||
|
icon: <CategoryIcon iconName={c.icon} size={14} />,
|
||||||
|
}))}
|
||||||
|
selected={filters.categoryIds}
|
||||||
|
onChange={(categoryIds) => update({ categoryIds })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{assignees.length > 0 && (
|
{assignees.length > 0 && (
|
||||||
<div className="grid gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
<MultiSelectDropdown
|
||||||
|
label="Funcionário"
|
||||||
|
allLabel="Todos os funcionários"
|
||||||
|
options={assignees.map((assignee) => ({
|
||||||
|
value: assignee.id,
|
||||||
|
label: assignee.name,
|
||||||
|
icon: <UserAvatar name={assignee.name} photo={assignee.photo} size="sm" />,
|
||||||
|
}))}
|
||||||
|
selected={filters.userIds}
|
||||||
|
onChange={(userIds) => update({ userIds })}
|
||||||
|
/>
|
||||||
{currentUserId !== undefined && (
|
{currentUserId !== undefined && (
|
||||||
<label className="flex cursor-pointer items-center gap-2 self-start">
|
<label className="flex cursor-pointer items-center gap-2 self-start">
|
||||||
<input
|
<input
|
||||||
|
|
@ -116,36 +141,9 @@ export function TaskFilterBar({
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
<MultiSelectDropdown
|
|
||||||
label="Funcionario"
|
|
||||||
allLabel="Todos os funcionarios"
|
|
||||||
options={assignees.map((assignee) => ({
|
|
||||||
value: assignee.id,
|
|
||||||
label: assignee.name,
|
|
||||||
icon: <UserAvatar name={assignee.name} photo={assignee.photo} size="sm" />,
|
|
||||||
}))}
|
|
||||||
selected={filters.userIds}
|
|
||||||
onChange={(userIds) => update({ userIds })}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{categories.length > 0 && (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Filter size={16} className="shrink-0 text-text-muted" />
|
|
||||||
<span className="text-[10px] font-black uppercase tracking-widest text-text-muted whitespace-nowrap">Categorias</span>
|
|
||||||
<MultiSelectChips
|
|
||||||
options={categories.map((c) => ({
|
|
||||||
value: c.id,
|
|
||||||
label: c.name,
|
|
||||||
color: c.color,
|
|
||||||
icon: <CategoryIcon iconName={c.icon} size={14} />,
|
|
||||||
}))}
|
|
||||||
selected={filters.categoryIds}
|
|
||||||
onChange={(categoryIds) => update({ categoryIds })}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue