diff --git a/TaskTracker.Web/src/components/TaskDetailPanel.tsx b/TaskTracker.Web/src/components/TaskDetailPanel.tsx index 5798255..ee769c3 100644 --- a/TaskTracker.Web/src/components/TaskDetailPanel.tsx +++ b/TaskTracker.Web/src/components/TaskDetailPanel.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useRef, useCallback } from 'react' +import { motion } from 'framer-motion' import { X, Loader2 } from 'lucide-react' import { WorkTaskStatus } from '../types/index.ts' import { @@ -40,9 +41,6 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp const completeTask = useCompleteTask() const abandonTask = useAbandonTask() - // Slide-in animation state - const [visible, setVisible] = useState(false) - // Inline editing states const [editingTitle, setEditingTitle] = useState(false) const [titleValue, setTitleValue] = useState('') @@ -58,11 +56,6 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp const categoryInputRef = useRef(null) const estimateInputRef = useRef(null) - // Trigger slide-in - useEffect(() => { - requestAnimationFrame(() => setVisible(true)) - }, []) - // Escape key handler const handleEscape = useCallback( (e: KeyboardEvent) => { @@ -75,10 +68,10 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp setEditingEstimate(false) return } - handleClose() + onClose() } }, - [editingTitle, editingDesc, editingCategory, editingEstimate] // eslint-disable-line react-hooks/exhaustive-deps + [editingTitle, editingDesc, editingCategory, editingEstimate, onClose] ) useEffect(() => { @@ -100,11 +93,6 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp if (editingEstimate) estimateInputRef.current?.focus() }, [editingEstimate]) - function handleClose() { - setVisible(false) - setTimeout(onClose, 200) // wait for slide-out animation - } - // --- Save handlers --- function saveTitle() { if (task && titleValue.trim() && titleValue.trim() !== task.title) { @@ -153,22 +141,25 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp return ( <> {/* Overlay */} -
{/* Panel */} -
{isLoading || !task ? (
- +
) : ( <> @@ -176,47 +167,47 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp
{/* Header */}
- {/* Close button */} -
+ {/* Title row with close button inline */} +
+
+ {editingTitle ? ( + setTitleValue(e.target.value)} + onBlur={saveTitle} + onKeyDown={(e) => { + if (e.key === 'Enter') saveTitle() + if (e.key === 'Escape') setEditingTitle(false) + }} + className="w-full bg-[var(--color-page)] text-xl font-semibold text-[var(--color-text-primary)] px-3 py-2 rounded border border-[var(--color-accent)] outline-none" + /> + ) : ( +

{ + setTitleValue(task.title) + setEditingTitle(true) + }} + > + {task.title} +

+ )} +
- {/* Title */} - {editingTitle ? ( - setTitleValue(e.target.value)} - onBlur={saveTitle} - onKeyDown={(e) => { - if (e.key === 'Enter') saveTitle() - if (e.key === 'Escape') setEditingTitle(false) - }} - className="w-full bg-[#0f1117] text-lg font-semibold text-white px-3 py-2 rounded border border-indigo-500 outline-none" - /> - ) : ( -

{ - setTitleValue(task.title) - setEditingTitle(true) - }} - > - {task.title} -

- )} - {/* Status badge + Category */}
{statusConfig && ( ) : ( { setCategoryValue(task.category ?? '') setEditingCategory(true) @@ -254,11 +245,11 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp
-
+
{/* Description */}
-

+

Description

{editingDesc ? ( @@ -274,13 +265,13 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp } }} rows={4} - className="w-full bg-[#0f1117] text-sm text-white px-3 py-2 rounded border border-indigo-500 outline-none resize-none" + className="w-full bg-[var(--color-page)] text-sm text-[var(--color-text-primary)] px-3 py-2 rounded border border-[var(--color-accent)] outline-none resize-none" placeholder="Add a description..." /> ) : (

{ setDescValue(task.description ?? '') @@ -292,23 +283,23 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp )}

-
+
{/* Time */}
-

+

Time

- Elapsed - + Elapsed + {task.startedAt ? formatElapsed(task.startedAt, task.completedAt) : '--'}
- Estimate + Estimate {editingEstimate ? ( ) : ( { setEstimateValue(task.estimatedMinutes?.toString() ?? '') setEditingEstimate(true) @@ -342,7 +333,9 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp
= 100 ? 'bg-rose-500' : 'bg-indigo-500' + progressPercent >= 100 + ? 'bg-rose-500' + : 'bg-gradient-to-r from-[var(--color-accent)] to-[var(--color-accent-end)]' }`} style={{ width: `${Math.min(progressPercent, 100)}%` }} /> @@ -350,14 +343,14 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp )}
-
+
{/* Subtasks */}
-
+
{/* Notes */}
@@ -367,13 +360,13 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp {/* Action buttons - fixed at bottom */} {task.status !== WorkTaskStatus.Completed && task.status !== WorkTaskStatus.Abandoned && ( -
+
{task.status === WorkTaskStatus.Pending && ( <> @@ -418,7 +411,7 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp @@ -442,7 +435,7 @@ export default function TaskDetailPanel({ taskId, onClose }: TaskDetailPanelProp )} )} -
+ ) }