Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions frontend/src/components/TasksLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,28 @@ export function TasksLayout() {
</div>
), [currentColumns, tasksByStatus, taskSearch, priorityFilter, setSelectedTaskId, setViewMode]);


// ⚡ Bolt: Wrap My Tasks list in useMemo to prevent O(N) re-renders
// 🎯 Why: Mapping over potentially large lists of filtered tasks blocks the main thread during unrelated state updates.
const myTasksList = useMemo(() => {
if (viewMode !== '내 작업') return null;
return filteredTicketTasks.length > 0 ? filteredTicketTasks.map(task => (
<button key={task.id} type="button" className="flex w-full items-center justify-between p-4 rounded-xl border border-border bg-card text-left shadow-sm transition-colors hover:border-primary/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40" onClick={() => { setSelectedTaskId(task.id); setViewMode('작업 상세'); }}>
<div className="flex items-center gap-4">
<div className={`size-3 rounded-full ${task.priority === 'urgent' ? 'bg-red-500' : task.priority === 'high' ? 'bg-orange-500' : 'bg-blue-500'}`}></div>
<div>
<h3 className="font-bold text-sm">{safeTaskTitle(task.title)}</h3>
<p className="text-xs text-muted-foreground mt-1">근거: {getTaskEvidenceLabel(task)} | 원본: {getTaskSourceLabel(task.source_type)}</p>
</div>
</div>
<span className={`px-2 py-1 rounded-full text-xs font-bold ${task.status === 'done' ? 'bg-green-100 text-green-700' : 'bg-secondary text-secondary-foreground'}`}>{taskStatusLabels[task.status]}</span>
</button>
)) : (
<p className="rounded-xl border border-dashed border-border bg-card p-4 text-sm font-semibold text-muted-foreground">서명 세션에 연결된 내 작업이 없습니다.</p>
);
}, [filteredTicketTasks, setSelectedTaskId, setViewMode, viewMode]);
const handleViewModeKeyDown = (event: KeyboardEvent<HTMLButtonElement>, mode: TaskViewMode) => {

const currentIndex = TASK_VIEW_MODES.indexOf(mode);
const lastIndex = TASK_VIEW_MODES.length - 1;
let nextIndex: number;
Expand Down Expand Up @@ -684,20 +705,7 @@ export function TasksLayout() {
{viewMode === '내 작업' && (
<div className="space-y-4 max-w-4xl mx-auto">
<h2 className="font-bold text-lg mb-4">내 작업</h2>
{filteredTicketTasks.length > 0 ? filteredTicketTasks.map(task => (
<button key={task.id} type="button" className="flex w-full items-center justify-between p-4 rounded-xl border border-border bg-card text-left shadow-sm transition-colors hover:border-primary/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40" onClick={() => { setSelectedTaskId(task.id); setViewMode('작업 상세'); }}>
<div className="flex items-center gap-4">
<div className={`size-3 rounded-full ${task.priority === 'urgent' ? 'bg-red-500' : task.priority === 'high' ? 'bg-orange-500' : 'bg-blue-500'}`}></div>
<div>
<h3 className="font-bold text-sm">{safeTaskTitle(task.title)}</h3>
<p className="text-xs text-muted-foreground mt-1">근거: {getTaskEvidenceLabel(task)} | 원본: {getTaskSourceLabel(task.source_type)}</p>
</div>
</div>
<span className={`px-2 py-1 rounded-full text-xs font-bold ${task.status === 'done' ? 'bg-green-100 text-green-700' : 'bg-secondary text-secondary-foreground'}`}>{taskStatusLabels[task.status]}</span>
</button>
)) : (
<p className="rounded-xl border border-dashed border-border bg-card p-4 text-sm font-semibold text-muted-foreground">서명 세션에 연결된 내 작업이 없습니다.</p>
)}
{myTasksList}
</div>
)}

Expand Down
Loading