fix: resolve subtasks tab not updating on Linux (#794)
* auto-claude: subtask-2-1 - Fix the selectedTask update logic in App.tsx * auto-claude: subtask-2-2 - Add console logging for debugging state updates * refactor: gate debug logs behind DEBUG flag and optimize deep comparison - Import debugLog from shared utils to gate console.log statements - Debug logs only emit when DEBUG=true (via npm run dev:debug) - Replace full task object comparison with specific field checks - Only compare subtasks array and status field for better performance - Add clear reason logging for what changed Addresses code review feedback about verbose production logs and expensive JSON.stringify comparisons. * refactor: optimize debug logging and expand task field comparison - Export isDebugEnabled from debug-logger for performance gating - Guard expensive debugLog computations (Date, map, stringify) with isDebugEnabled() - Add title, description, metadata to field comparisons - TaskDetailModal now refreshes when these fields are edited in TaskEditDialog This prevents performance overhead from debug log argument construction when debug mode is disabled and ensures modal updates for all task edits. * fix: complete task field comparisons and simplify debug logging Add missing comparisons for executionProgress, qaReport, reviewReason, and logs to prevent stale UI. Remove redundant isDebugEnabled() checks since debugLog() guards internally. Consolidate debug logging with early-return pattern for better readability. * refactor: remove unused isDebugEnabled import --------- Co-authored-by: StillKnotKnown <stillknotknown@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Download, RefreshCw, AlertCircle } from 'lucide-react';
|
import { Download, RefreshCw, AlertCircle } from 'lucide-react';
|
||||||
|
import { debugLog } from '../shared/utils/debug-logger';
|
||||||
import {
|
import {
|
||||||
DndContext,
|
DndContext,
|
||||||
DragOverlay,
|
DragOverlay,
|
||||||
@@ -439,13 +440,84 @@ export function App() {
|
|||||||
|
|
||||||
// Update selected task when tasks change (for real-time updates)
|
// Update selected task when tasks change (for real-time updates)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedTask) {
|
if (!selectedTask) {
|
||||||
const updatedTask = tasks.find(
|
debugLog('[App] No selected task to update');
|
||||||
(t) => t.id === selectedTask.id || t.specId === selectedTask.specId
|
return;
|
||||||
);
|
}
|
||||||
if (updatedTask && updatedTask !== selectedTask) {
|
|
||||||
setSelectedTask(updatedTask);
|
const updatedTask = tasks.find(
|
||||||
}
|
(t) => t.id === selectedTask.id || t.specId === selectedTask.specId
|
||||||
|
);
|
||||||
|
|
||||||
|
debugLog('[App] Task lookup result', {
|
||||||
|
found: !!updatedTask,
|
||||||
|
updatedTaskId: updatedTask?.id,
|
||||||
|
selectedTaskId: selectedTask.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updatedTask) {
|
||||||
|
debugLog('[App] Updated task not found in tasks array');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare all mutable fields that affect UI state
|
||||||
|
const subtasksChanged =
|
||||||
|
JSON.stringify(selectedTask.subtasks || []) !==
|
||||||
|
JSON.stringify(updatedTask.subtasks || []);
|
||||||
|
const statusChanged = selectedTask.status !== updatedTask.status;
|
||||||
|
const titleChanged = selectedTask.title !== updatedTask.title;
|
||||||
|
const descriptionChanged = selectedTask.description !== updatedTask.description;
|
||||||
|
const metadataChanged =
|
||||||
|
JSON.stringify(selectedTask.metadata || {}) !==
|
||||||
|
JSON.stringify(updatedTask.metadata || {});
|
||||||
|
const executionProgressChanged =
|
||||||
|
JSON.stringify(selectedTask.executionProgress || {}) !==
|
||||||
|
JSON.stringify(updatedTask.executionProgress || {});
|
||||||
|
const qaReportChanged =
|
||||||
|
JSON.stringify(selectedTask.qaReport || {}) !==
|
||||||
|
JSON.stringify(updatedTask.qaReport || {});
|
||||||
|
const reviewReasonChanged = selectedTask.reviewReason !== updatedTask.reviewReason;
|
||||||
|
const logsChanged =
|
||||||
|
JSON.stringify(selectedTask.logs || []) !==
|
||||||
|
JSON.stringify(updatedTask.logs || []);
|
||||||
|
|
||||||
|
const hasChanged =
|
||||||
|
subtasksChanged || statusChanged || titleChanged || descriptionChanged ||
|
||||||
|
metadataChanged || executionProgressChanged || qaReportChanged ||
|
||||||
|
reviewReasonChanged || logsChanged;
|
||||||
|
|
||||||
|
debugLog('[App] Task comparison', {
|
||||||
|
hasChanged,
|
||||||
|
changes: {
|
||||||
|
subtasks: subtasksChanged,
|
||||||
|
status: statusChanged,
|
||||||
|
title: titleChanged,
|
||||||
|
description: descriptionChanged,
|
||||||
|
metadata: metadataChanged,
|
||||||
|
executionProgress: executionProgressChanged,
|
||||||
|
qaReport: qaReportChanged,
|
||||||
|
reviewReason: reviewReasonChanged,
|
||||||
|
logs: logsChanged,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasChanged) {
|
||||||
|
const reasons = [];
|
||||||
|
if (subtasksChanged) reasons.push('Subtasks');
|
||||||
|
if (statusChanged) reasons.push('Status');
|
||||||
|
if (titleChanged) reasons.push('Title');
|
||||||
|
if (descriptionChanged) reasons.push('Description');
|
||||||
|
if (metadataChanged) reasons.push('Metadata');
|
||||||
|
if (executionProgressChanged) reasons.push('ExecutionProgress');
|
||||||
|
if (qaReportChanged) reasons.push('QAReport');
|
||||||
|
if (reviewReasonChanged) reasons.push('ReviewReason');
|
||||||
|
if (logsChanged) reasons.push('Logs');
|
||||||
|
|
||||||
|
debugLog('[App] Updating selectedTask', {
|
||||||
|
taskId: updatedTask.id,
|
||||||
|
reason: reasons.join(', '),
|
||||||
|
});
|
||||||
|
setSelectedTask(updatedTask);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- Intentionally omit selectedTask object to prevent infinite re-render loop
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- Intentionally omit selectedTask object to prevent infinite re-render loop
|
||||||
}, [tasks, selectedTask?.id, selectedTask?.specId]);
|
}, [tasks, selectedTask?.id, selectedTask?.specId]);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Only logs when DEBUG=true in environment
|
* Only logs when DEBUG=true in environment
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const isDebugEnabled = (): boolean => {
|
export const isDebugEnabled = (): boolean => {
|
||||||
if (typeof process !== 'undefined' && process.env) {
|
if (typeof process !== 'undefined' && process.env) {
|
||||||
return process.env.DEBUG === 'true';
|
return process.env.DEBUG === 'true';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user