diff --git a/.replit b/.replit index b0a2b1f..410a32e 100644 --- a/.replit +++ b/.replit @@ -19,7 +19,7 @@ localPort = 35345 externalPort = 3002 [[ports]] -localPort = 36505 +localPort = 40125 externalPort = 3001 [[ports]] diff --git a/client/src/App.tsx b/client/src/App.tsx index aac0f35..eb91637 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -9,6 +9,7 @@ import { Card } from "@/components/ui/card"; // Components import BottomNavigation from './components/BottomNavigation'; import TaskCreationModal from './components/TaskCreationModal'; +import TaskDetailsModal from './components/TaskDetailsModal'; import TasksWithCalendar from './components/TasksWithCalendar'; import CalendarView from './components/CalendarView'; import KanbanBoard from './components/KanbanBoard'; @@ -90,6 +91,8 @@ function App() { const [currentTab, setCurrentTab] = useState('tasks'); const [tasks, setTasks] = useState(initialTasks); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [isTaskDetailsOpen, setIsTaskDetailsOpen] = useState(false); + const [selectedTask, setSelectedTask] = useState(null); const handleCreateTask = (newTask: Partial) => { const task: Task = { @@ -124,6 +127,22 @@ function App() { handleTaskUpdate(taskId, { dueDate: newDate }); }; + const handleTaskClick = (task: Task) => { + setSelectedTask(task); + setIsTaskDetailsOpen(true); + console.log('Task clicked:', task.title); + }; + + const handleTaskDetailsClose = () => { + setIsTaskDetailsOpen(false); + setSelectedTask(null); + }; + + const handleTaskDetailsSave = (updatedTask: Task) => { + handleTaskUpdate(updatedTask.id, updatedTask); + console.log('Task details saved:', updatedTask.title); + }; + const handleCreateFromTemplate = (templateId: string, startDate: Date, projectName: string) => { console.log('Creating project from template:', { templateId, startDate, projectName }); // In a real app, this would create multiple tasks based on the template @@ -145,6 +164,7 @@ function App() { console.log('Date selected:', date.toLocaleDateString())} /> ); @@ -155,6 +175,7 @@ function App() { tasks={tasks} onTaskStatusChange={handleTaskStatusChange} onTaskUpdate={handleTaskUpdate} + onTaskClick={handleTaskClick} /> ); @@ -230,6 +251,14 @@ function App() { onClose={() => setIsCreateModalOpen(false)} onSave={handleCreateTask} /> + + {/* Task Details Modal */} + diff --git a/client/src/components/CalendarView.tsx b/client/src/components/CalendarView.tsx index ed523c0..e04bae5 100644 --- a/client/src/components/CalendarView.tsx +++ b/client/src/components/CalendarView.tsx @@ -10,10 +10,11 @@ import { addDays, format, isSameDay, startOfWeek, subWeeks } from 'date-fns'; interface CalendarViewProps { tasks: Task[]; onTaskDrop?: (taskId: string, newDate: Date) => void; + onTaskClick?: (task: Task) => void; onDateSelect?: (date: Date) => void; } -export default function CalendarView({ tasks, onTaskDrop, onDateSelect }: CalendarViewProps) { +export default function CalendarView({ tasks, onTaskDrop, onTaskClick, onDateSelect }: CalendarViewProps) { const [currentDate, setCurrentDate] = useState(new Date()); const [draggedTask, setDraggedTask] = useState(null); @@ -146,12 +147,19 @@ export default function CalendarView({ tasks, onTaskDrop, onDateSelect }: Calend draggable onDragStart={() => handleDragStart(task.id)} onDragEnd={handleDragEnd} - className={`cursor-move ${draggedTask === task.id ? 'opacity-50' : ''}`} + onClick={(e) => { + // Prevent click when dragging + if (draggedTask === task.id) return; + e.stopPropagation(); + onTaskClick?.(task); + console.log('Calendar task clicked:', task.title); + }} + className={`cursor-pointer hover-elevate ${draggedTask === task.id ? 'opacity-50' : ''}`} data-testid={`calendar-task-${task.id}`} > void; onTaskUpdate?: (taskId: string, updates: Partial) => void; + onTaskClick?: (task: Task) => void; } type ViewMode = 'traditional' | 'weekly' | 'monthly'; -export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate }: KanbanBoardProps) { +export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate, onTaskClick }: KanbanBoardProps) { const [viewMode, setViewMode] = useState('traditional'); const [currentDate, setCurrentDate] = useState(new Date()); const [draggedTask, setDraggedTask] = useState(null); @@ -195,7 +196,8 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate }: console.log(`Timer paused for ${task.title}`); }} onEdit={() => { - console.log(`Edit task ${task.title}`); + onTaskClick?.(task); + console.log(`Task clicked from board: ${task.title}`); }} isDragging={draggedTask === task.id} /> @@ -256,7 +258,8 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate }: console.log(`Timer paused for ${task.title}`); }} onEdit={() => { - console.log(`Edit task ${task.title}`); + onTaskClick?.(task); + console.log(`Task clicked from board: ${task.title}`); }} isDragging={draggedTask === task.id} /> @@ -317,7 +320,8 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate }: console.log(`Timer paused for ${task.title}`); }} onEdit={() => { - console.log(`Edit task ${task.title}`); + onTaskClick?.(task); + console.log(`Task clicked from board: ${task.title}`); }} isDragging={draggedTask === task.id} /> diff --git a/client/src/components/TaskDetailsModal.tsx b/client/src/components/TaskDetailsModal.tsx index 1599327..7aa0563 100644 --- a/client/src/components/TaskDetailsModal.tsx +++ b/client/src/components/TaskDetailsModal.tsx @@ -8,19 +8,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Card } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; import { Clock, Timer, FileText, Plus, Edit2, Save, X, Calendar } from 'lucide-react'; - -interface ProjectTask { - id: string; - title: string; - description?: string; - priority: 'low' | 'medium' | 'high'; - status: 'todo' | 'inProgress' | 'done'; - estimatedHours?: number; - labelId?: string; - notes?: string; - timeTracked?: number; // in minutes - timeEntries?: TimeEntry[]; -} +import { Task, Label } from '@shared/schema'; interface TimeEntry { id: string; @@ -32,9 +20,9 @@ interface TimeEntry { interface TaskDetailsModalProps { isOpen: boolean; onClose: () => void; - task: ProjectTask | null; - onSave: (updatedTask: ProjectTask) => void; - labels?: Array<{ id: string; name: string; color: string }>; + task: Task | null; + onSave: (updatedTask: Task) => void; + labels?: Label[]; } export default function TaskDetailsModal({ @@ -56,13 +44,23 @@ export default function TaskDetailsModal({ const [timeDescription, setTimeDescription] = useState(''); const [activeTimeTab, setActiveTimeTab] = useState('clock'); - // Time entries state + // Time entries state - simulate from timeTracked for display const [timeEntries, setTimeEntries] = useState([]); useEffect(() => { if (task && isOpen) { setNotes(task.notes || ''); - setTimeEntries(task.timeEntries || []); + // Create a single time entry from timeTracked for display purposes + if (task.timeTracked > 0) { + setTimeEntries([{ + id: 'tracked-time', + date: new Date(), + timeSpent: task.timeTracked, + description: 'Previously tracked time' + }]); + } else { + setTimeEntries([]); + } // Reset time input state setHours(0); setMinutes(0); @@ -119,7 +117,6 @@ export default function TaskDetailsModal({ const updatedTask = { ...task, - timeEntries: updatedTimeEntries, timeTracked: updatedTimeTracked }; @@ -137,7 +134,10 @@ export default function TaskDetailsModal({ }; const handleDeleteTimeEntry = (entryId: string) => { - if (!task) return; + if (!task || entryId === 'tracked-time') { + // Don't allow deleting the main tracked time entry + return; + } const entryToDelete = timeEntries.find(entry => entry.id === entryId); if (!entryToDelete) return; @@ -147,7 +147,6 @@ export default function TaskDetailsModal({ const updatedTask = { ...task, - timeEntries: updatedTimeEntries, timeTracked: updatedTimeTracked }; @@ -177,7 +176,7 @@ export default function TaskDetailsModal({ }; const getTotalTrackedTime = () => { - return timeEntries.reduce((total, entry) => total + entry.timeSpent, 0); + return task?.timeTracked || 0; }; const getPriorityColor = (priority: string) => { @@ -231,11 +230,6 @@ export default function TaskDetailsModal({ {task.status} - {task.estimatedHours && ( - - {task.estimatedHours}h estimated - - )} {getTotalTrackedTime() > 0 && ( {formatMinutesToHours(getTotalTrackedTime())} tracked @@ -502,14 +496,16 @@ export default function TaskDetailsModal({

)} - + {entry.id !== 'tracked-time' && ( + + )} ))}