refactor: improve task progress visualization in MemberCard and enhance message filtering

- Updated MemberCard to display task completion progress using a gradient progress bar for better visual feedback.
- Refined sorting logic in MessagesFilterPopover to ensure consistent ordering of sender and recipient options, enhancing usability.
This commit is contained in:
iliya 2026-02-23 12:42:51 +02:00
parent c67e85b203
commit 895fe2c0de
2 changed files with 20 additions and 24 deletions

View file

@ -35,6 +35,8 @@ export const MemberCard = ({
const totalTasks = pending + inProgress + completed; const totalTasks = pending + inProgress + completed;
const completedRatio = totalTasks > 0 ? completed / totalTasks : 0; const completedRatio = totalTasks > 0 ? completed / totalTasks : 0;
const progressPercent = Math.round(completedRatio * 100);
return ( return (
<div className="rounded"> <div className="rounded">
<div <div
@ -116,26 +118,18 @@ export const MemberCard = ({
</button> </button>
</div> </div>
</div> </div>
<div className="rounded-b border-x border-b border-t-0 border-[var(--color-border)] bg-[var(--color-surface)] px-2 py-1"> <div
<div className="flex items-center gap-2"> className="h-0.5 rounded-b bg-[var(--color-border)]"
<div role="progressbar"
className="h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-[var(--color-surface-raised)]" aria-valuenow={completed}
role="progressbar" aria-valuemin={0}
aria-valuenow={completed} aria-valuemax={totalTasks}
aria-valuemin={0} aria-label={`Tasks ${completed}/${totalTasks} completed`}
aria-valuemax={totalTasks} title={`${completed}/${totalTasks} tasks`}
aria-label={`Tasks ${completed}/${totalTasks} completed`} style={{
> background: `linear-gradient(to right, #10b981 ${progressPercent}%, var(--color-border) ${progressPercent}%)`,
<div }}
className="h-full rounded-full bg-emerald-500 transition-all duration-200" />
style={{ width: `${Math.round(completedRatio * 100)}%` }}
/>
</div>
<span className="shrink-0 text-[10px] font-medium tabular-nums text-[var(--color-text-muted)]">
{completed}/{totalTasks}
</span>
</div>
</div>
</div> </div>
); );
}; };

View file

@ -25,7 +25,7 @@ function collectFromOptions(messages: InboxMessage[]): string[] {
for (const m of messages) { for (const m of messages) {
if (m.from?.trim()) set.add(m.from.trim()); if (m.from?.trim()) set.add(m.from.trim());
} }
return Array.from(set).sort(); return Array.from(set).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
} }
function collectToOptions(messages: InboxMessage[]): string[] { function collectToOptions(messages: InboxMessage[]): string[] {
@ -33,7 +33,7 @@ function collectToOptions(messages: InboxMessage[]): string[] {
for (const m of messages) { for (const m of messages) {
if (m.to?.trim()) set.add(m.to.trim()); if (m.to?.trim()) set.add(m.to.trim());
} }
return Array.from(set).sort(); return Array.from(set).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
} }
export const MessagesFilterPopover = ({ export const MessagesFilterPopover = ({
@ -47,10 +47,12 @@ export const MessagesFilterPopover = ({
useEffect(() => { useEffect(() => {
if (open) { if (open) {
setDraft({ const next = {
from: new Set(filter.from), from: new Set(filter.from),
to: new Set(filter.to), to: new Set(filter.to),
}); };
const schedule = (): void => setDraft(next);
queueMicrotask(schedule);
} }
}, [open, filter.from, filter.to]); }, [open, filter.from, filter.to]);