chore(frontend): real ESLint instead of a placeholder; fix hook order (FLAW-19)

eslint.config.js with the recommended JS/TypeScript rules and the React
hooks rules; npm run lint runs ESLint and is part of the image build.
Errors fail the build, the 24 exhaustive-deps findings stay warnings.

It found two real bugs: TaskDetailModal and ManageColumnsModal called
their hooks after 'if (!isOpen) return null', so the hook count changed
whenever the dialog opened or closed. Both are now a thin wrapper plus a
content component. Also removes unused imports and dead code (old task
list share handlers, an unused realtime status, an unused rollup).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 15:02:52 +01:00
1 parent 6b56a33500
commit 4c50d15915
19 files changed
+1360 -140

No files matched your search

@@ -18,15 +18,11 @@ import {
ExternalLink,
Sparkles,
ArrowLeft,
CheckSquare,
PanelLeftClose,
PanelLeftOpen,
ChevronLeft,
Radio,
Clock,
AlertTriangle,
Loader2,
Lock,
} from 'lucide-react';
import {
Document,
@@ -44,10 +40,6 @@ import {
api,
onWebSocketMessage,
triggerHaptic,
onRealtimeStatus,
isRealtimeConnected,
getRealtimeTransport,
RealtimeTransport,
} from '../../lib/api';
import { UnifiedShareModal } from '../../components/UnifiedShareModal';
import { FormSheet } from '../../components/FormSheet';
@@ -347,7 +339,6 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
const [newFolderCoords, setNewFolderCoords] = useState<{ x: number; y: number } | null>(null);
const [newFolderParentPath, setNewFolderParentPath] = useState<string>('');
const [newFolderName, setNewFolderName] = useState<string>('');
const [newFolderIsShared, setNewFolderIsShared] = useState<boolean>(false);
const handleOpenNewFolder = (parentPath = '', e?: React.MouseEvent) => {
setNewFolderParentPath(parentPath);
@@ -381,19 +372,9 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
});
}, []);
// Realtime connection status for top header indicator (identical to SharedListsView)
const [realtimeConnected, setRealtimeConnected] = useState<boolean>(() => isRealtimeConnected());
const [realtimeTransport, setRealtimeTransport] = useState<RealtimeTransport>(() => getRealtimeTransport());
const [docSaveStatus, setDocSaveStatus] = useState<DocSaveStatus>('saved');
const [docSaveError, setDocSaveError] = useState<string | null>(null);
useEffect(() => {
const unsub = onRealtimeStatus((connected, transport) => {
setRealtimeConnected(connected);
setRealtimeTransport(transport);
});
return unsub;
}, []);
// Drag and Drop state
const [draggingDocId, setDraggingDocId] = useState<string | null>(null);
@@ -960,22 +941,6 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
return map;
}, [allRollupTodos]);
const rollupTodosByNote = useMemo(() => {
const map = new Map<
string,
{ doc: Document; items: { doc: Document; blockId: string; content: string; checked: boolean }[] }
>();
for (const item of allRollupTodos) {
const existing = map.get(item.doc.id);
if (existing) {
existing.items.push(item);
} else {
map.set(item.doc.id, { doc: item.doc, items: [item] });
}
}
return map;
}, [allRollupTodos]);
const totalOpenTodos = useMemo(() => {
let count = 0;
for (const d of docs) {