From 482105a94c9f2bf85bb6a9f70f6cd31504846960 Mon Sep 17 00:00:00 2001 From: 777genius Date: Sun, 31 May 2026 02:02:53 +0300 Subject: [PATCH] perf(renderer): memoize kanban task card actions --- .../components/team/kanban/KanbanTaskCard.tsx | 372 +++++++++++------- 1 file changed, 225 insertions(+), 147 deletions(-) diff --git a/src/renderer/components/team/kanban/KanbanTaskCard.tsx b/src/renderer/components/team/kanban/KanbanTaskCard.tsx index 022995d9..c2084c37 100644 --- a/src/renderer/components/team/kanban/KanbanTaskCard.tsx +++ b/src/renderer/components/team/kanban/KanbanTaskCard.tsx @@ -266,6 +266,208 @@ const TaskActionIconButton = ({ ); +interface TaskMetaActionsProps { + taskId: string; + unreadCount: number; + commentCount: number; + pulseKey: number; + canOpenChanges: boolean; + changesNeedAttention: boolean; + onViewChanges?: (taskId: string) => void; + onDeleteTask?: (taskId: string) => void; +} + +const TaskMetaActions = memo(function TaskMetaActions({ + taskId, + unreadCount, + commentCount, + pulseKey, + canOpenChanges, + changesNeedAttention, + onViewChanges, + onDeleteTask, +}: TaskMetaActionsProps): React.JSX.Element { + const { t } = useAppTranslation('team'); + + return ( + <> + {canOpenChanges && onViewChanges ? ( + } + variant="ghost" + className={ + changesNeedAttention + ? 'text-amber-400 hover:bg-amber-500/10 hover:text-amber-300' + : 'text-sky-400 hover:bg-sky-500/10 hover:text-sky-300' + } + onClick={(e) => { + e.stopPropagation(); + onViewChanges(taskId); + }} + /> + ) : null} + + {onDeleteTask ? ( + } + variant="ghost" + className="text-red-400 hover:bg-red-500/10 hover:text-red-300" + onClick={(e) => { + e.stopPropagation(); + onDeleteTask(taskId); + }} + /> + ) : null} + + ); +}); + +interface TaskPrimaryActionsProps { + taskId: string; + columnId: KanbanColumnId; + isReviewManual: boolean; + onRequestReview: (taskId: string) => void; + onApprove: (taskId: string) => void; + onRequestChanges: (taskId: string) => void; + onMoveBackToDone: (taskId: string) => void; + onStartTask: (taskId: string) => void; + onCompleteTask: (taskId: string) => void; + onCancelTask: (taskId: string) => void; +} + +const TaskPrimaryActions = memo(function TaskPrimaryActions({ + taskId, + columnId, + isReviewManual, + onRequestReview, + onApprove, + onRequestChanges, + onMoveBackToDone, + onStartTask, + onCompleteTask, + onCancelTask, +}: TaskPrimaryActionsProps): React.JSX.Element { + const { t } = useAppTranslation('team'); + + return ( +
+ {columnId === 'todo' ? ( + <> + } + className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" + onClick={(e) => { + e.stopPropagation(); + onStartTask(taskId); + }} + /> + } + className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" + onClick={(e) => { + e.stopPropagation(); + onCompleteTask(taskId); + }} + /> + + ) : null} + + {columnId === 'in_progress' ? ( + <> + } + className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" + onClick={(e) => { + e.stopPropagation(); + onCompleteTask(taskId); + }} + /> + + + ) : null} + + {columnId === 'done' ? ( + <> + } + className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" + onClick={(e) => { + e.stopPropagation(); + onApprove(taskId); + }} + /> + } + className="border-violet-500/40 text-violet-400 hover:bg-violet-500/10 hover:text-violet-300" + onClick={(e) => { + e.stopPropagation(); + onRequestReview(taskId); + }} + /> + + ) : null} + + {columnId === 'review' ? ( +
+ {isReviewManual ? ( +
+ {t('kanban.taskCard.manualReview')} +
+ ) : null} +
+ } + className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" + onClick={(e) => { + e.stopPropagation(); + onApprove(taskId); + }} + /> + } + variant="destructive" + className="bg-red-500/90 text-white hover:bg-red-500" + onClick={(e) => { + e.stopPropagation(); + onRequestChanges(taskId); + }} + /> +
+
+ ) : null} + + {columnId === 'approved' ? ( + } + className="border-amber-500/40 text-amber-400 hover:bg-amber-500/10 hover:text-amber-300" + onClick={(e) => { + e.stopPropagation(); + onMoveBackToDone(taskId); + }} + /> + ) : null} +
+ ); +}); + export const KanbanTaskCard = memo( function KanbanTaskCard({ task, @@ -326,48 +528,6 @@ export const KanbanTaskCard = memo( syncCommentPulse({ taskKey: commentPulseTaskKey, comments }); }, [commentCount, commentPulseTaskKey, comments]); - const metaActions = ( - <> - {canOpenChanges ? ( - } - variant="ghost" - className={ - changesNeedAttention - ? 'text-amber-400 hover:bg-amber-500/10 hover:text-amber-300' - : 'text-sky-400 hover:bg-sky-500/10 hover:text-sky-300' - } - onClick={(e) => { - e.stopPropagation(); - onViewChanges!(task.id); - }} - /> - ) : null} - - {onDeleteTask ? ( - } - variant="ghost" - className="text-red-400 hover:bg-red-500/10 hover:text-red-300" - onClick={(e) => { - e.stopPropagation(); - onDeleteTask(task.id); - }} - /> - ) : null} - - ); - return (
-
- {columnId === 'todo' ? ( - <> - } - className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" - onClick={(e) => { - e.stopPropagation(); - onStartTask(task.id); - }} - /> - } - className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" - onClick={(e) => { - e.stopPropagation(); - onCompleteTask(task.id); - }} - /> - - ) : null} + - {columnId === 'in_progress' ? ( - <> - } - className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" - onClick={(e) => { - e.stopPropagation(); - onCompleteTask(task.id); - }} - /> - - - ) : null} - - {columnId === 'done' ? ( - <> - } - className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" - onClick={(e) => { - e.stopPropagation(); - onApprove(task.id); - }} - /> - } - className="border-violet-500/40 text-violet-400 hover:bg-violet-500/10 hover:text-violet-300" - onClick={(e) => { - e.stopPropagation(); - onRequestReview(task.id); - }} - /> - - ) : null} - - {columnId === 'review' ? ( -
- {isReviewManual ? ( -
- {t('kanban.taskCard.manualReview')} -
- ) : null} -
- } - className="border-emerald-500/40 text-emerald-400 hover:bg-emerald-500/10 hover:text-emerald-300" - onClick={(e) => { - e.stopPropagation(); - onApprove(task.id); - }} - /> - } - variant="destructive" - className="bg-red-500/90 text-white hover:bg-red-500" - onClick={(e) => { - e.stopPropagation(); - onRequestChanges(task.id); - }} - /> -
-
- ) : null} - - {columnId === 'approved' ? ( - } - className="border-amber-500/40 text-amber-400 hover:bg-amber-500/10 hover:text-amber-300" - onClick={(e) => { - e.stopPropagation(); - onMoveBackToDone(task.id); - }} - /> - ) : null} +
+
- -
{metaActions}
);