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' && (
+
+ )}
))}