90 lines
3 KiB
TypeScript
90 lines
3 KiB
TypeScript
import { useMemo } from 'react';
|
|
|
|
import { Badge } from '@renderer/components/ui/badge';
|
|
import {
|
|
KANBAN_COLUMN_DISPLAY,
|
|
REVIEW_STATE_DISPLAY,
|
|
TASK_STATUS_LABELS,
|
|
TASK_STATUS_STYLES,
|
|
} from '@renderer/utils/memberHelpers';
|
|
import { formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
|
|
import {
|
|
getTeamTaskWorkflowColumn,
|
|
isTeamTaskNeedsFixActionable,
|
|
} from '@shared/utils/teamTaskState';
|
|
|
|
import type { TeamTaskWithKanban } from '@shared/types';
|
|
|
|
interface MemberTasksTabProps {
|
|
tasks: TeamTaskWithKanban[];
|
|
onTaskClick?: (task: TeamTaskWithKanban) => void;
|
|
}
|
|
|
|
const STATUS_ORDER: Record<string, number> = {
|
|
in_progress: 0,
|
|
pending: 1,
|
|
completed: 2,
|
|
};
|
|
|
|
export const MemberTasksTab = ({ tasks, onTaskClick }: MemberTasksTabProps): React.JSX.Element => {
|
|
const visibleTasks = useMemo(
|
|
() =>
|
|
tasks
|
|
.filter((t) => t.status !== 'deleted')
|
|
.sort((a, b) => (STATUS_ORDER[a.status] ?? 3) - (STATUS_ORDER[b.status] ?? 3)),
|
|
[tasks]
|
|
);
|
|
|
|
if (visibleTasks.length === 0) {
|
|
return (
|
|
<div className="rounded-md border border-[var(--color-border)] px-4 py-6 text-center text-sm text-[var(--color-text-muted)]">
|
|
No tasks assigned to this member
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-h-[320px] overflow-y-auto">
|
|
<div className="flex flex-col gap-1">
|
|
{visibleTasks.map((task) => {
|
|
const col = getTeamTaskWorkflowColumn(task);
|
|
const style =
|
|
col && KANBAN_COLUMN_DISPLAY[col]
|
|
? { bg: KANBAN_COLUMN_DISPLAY[col].bg, text: KANBAN_COLUMN_DISPLAY[col].text }
|
|
: TASK_STATUS_STYLES[task.status];
|
|
const label =
|
|
col && KANBAN_COLUMN_DISPLAY[col]
|
|
? KANBAN_COLUMN_DISPLAY[col].label
|
|
: TASK_STATUS_LABELS[task.status];
|
|
return (
|
|
<button
|
|
type="button"
|
|
key={task.id}
|
|
className="flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-left hover:bg-[var(--color-surface-raised)]"
|
|
onClick={() => onTaskClick?.(task)}
|
|
>
|
|
<Badge variant="secondary" className="shrink-0 px-1.5 py-0 text-[10px] font-normal">
|
|
{formatTaskDisplayLabel(task)}
|
|
</Badge>
|
|
<span className="min-w-0 flex-1 truncate text-sm text-[var(--color-text)]">
|
|
{task.subject}
|
|
</span>
|
|
<span
|
|
className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${style.bg} ${style.text}`}
|
|
>
|
|
{label}
|
|
</span>
|
|
{isTeamTaskNeedsFixActionable(task) ? (
|
|
<span
|
|
className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${REVIEW_STATE_DISPLAY.needsFix.bg} ${REVIEW_STATE_DISPLAY.needsFix.text}`}
|
|
>
|
|
{REVIEW_STATE_DISPLAY.needsFix.label}
|
|
</span>
|
|
) : null}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|