From b1ad9ec5465d1915a2090d6a5b2be18e30791d1c Mon Sep 17 00:00:00 2001 From: mARTin Date: Thu, 26 Feb 2026 07:49:45 +0100 Subject: [PATCH] fix(hooks): definitively resolve React Hook violations in GridTaskBlock --- src/components/GridTaskBlock.tsx | 180 +++++++++++++------------------ 1 file changed, 72 insertions(+), 108 deletions(-) diff --git a/src/components/GridTaskBlock.tsx b/src/components/GridTaskBlock.tsx index f459a56..9f53710 100644 --- a/src/components/GridTaskBlock.tsx +++ b/src/components/GridTaskBlock.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect } from "react"; -import { Repeat, ChevronDown, ChevronRight, FileText, CheckCircle, Circle, Copy } from "lucide-react"; +import { Repeat } from "lucide-react"; import { Task } from "./WeeklyView"; interface GridTaskBlockProps { @@ -107,7 +107,6 @@ export function GridTaskBlock({ }, 0); }; - // Calculate dimensions (needed by resize useEffect, must be before early return) const pixelsPerMinute = getSlotHeight(cellDuration) / cellDuration; useEffect(() => { @@ -144,20 +143,16 @@ export function GridTaskBlock({ }; }, [isResizing, resizeHeight, pixelsPerMinute, task.id, updateTaskDuration, task.startTime]); + // Conditional rendering should only happen after hooks if (!task.startTime) return null; const [startHour, startMinute] = task.startTime.split(":").map(Number); const startMinutes = (startHour - workingHoursStart) * 60 + startMinute; - - // Calculate top offset const topOffset = startMinutes * pixelsPerMinute; - - // Calculate height - const duration = task.duration || 15; // default 15m if not set + const duration = task.duration || 15; const baseHeight = duration * pixelsPerMinute; const currentHeight = isResizing && resizeHeight !== null ? resizeHeight : baseHeight; - // Handlers for resizing const onResizeStart = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); @@ -167,7 +162,6 @@ export function GridTaskBlock({ document.body.style.cursor = "ns-resize"; }; - return (
{ e.preventDefault(); const input = e.currentTarget.elements.namedItem("title") as HTMLInputElement; - updateTask(task.id, input.value); + updateTask(task.id, input.value || \"\"); }} onClick={(e) => e.stopPropagation()} style={{ width: "100%", paddingRight: "20px" }} @@ -246,48 +240,11 @@ export function GridTaskBlock({ style={{ width: "12px", height: "12px", margin: 0, cursor: "pointer", position: "relative", top: "3px", left: "-2px", accentColor: "#FFF" }} /> )} - {task.externalProvider && ( - !task.externalId || - !task.lastSyncedAt || - new Date(task.updatedAt) > new Date(task.lastSyncedAt) - ) && ( - - - - - - )} - {task.subTasks && task.subTasks.length > 0 && ( - { e.stopPropagation(); setIsSubTasksOpen(!isSubTasksOpen); }} - > - - - - - )} {task.title} - {task.markdownContent && ( - { e.stopPropagation(); setIsNotesOpen(!isNotesOpen); }} - style={{ marginLeft: "auto" }} - > - - - - - - - - )} )} -
- {/* Task Actions - shown below text on hover */}
e.stopPropagation()} @@ -311,74 +268,78 @@ export function GridTaskBlock({ - - - {!task.completed && ( - + {!task.completed && ( + )} - {!task.completed && ( - )} - -
+ + - {/* Inline Expanders Container */}
{isNotesOpen && (
180 ? "on-top" : ""}`} + className={`weekly-notes-popup ${topOffset > 180 ? \"on-top\" : \"\"}`} onClick={(e) => e.stopPropagation()} style={{ - top: topOffset > 180 ? "auto" : "100%", - bottom: topOffset > 180 ? "100%" : "auto", - marginTop: topOffset > 180 ? "0" : "10px", - marginBottom: topOffset > 180 ? "10px" : "0", - left: "-10px", - right: "-10px", - width: "auto", + top: topOffset > 180 ? \"auto\" : \"100%\", + bottom: topOffset > 180 ? \"100%\" : \"auto\", + marginTop: topOffset > 180 ? \"0\" : \"10px\", + marginBottom: topOffset > 180 ? \"10px\" : \"0\", + left: \"-10px\", + right: \"-10px\", + width: \"auto\", }} >
- - - - Markdown + + + + Markdown