diff --git a/client/src/App.tsx b/client/src/App.tsx
index d79034b..aac0f35 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -29,6 +29,7 @@ const initialTasks: Task[] = [
timeTracked: 0,
isTracking: false,
projectId: 'project-1',
+ notes: null,
labelId: '2e8c3aa0-278f-4220-b2c5-aa109b4279b7' // Urgent label
},
{
@@ -41,16 +42,20 @@ const initialTasks: Task[] = [
timeTracked: 120,
isTracking: true,
projectId: 'project-2',
+ notes: null,
labelId: 'cb44bed1-8ba3-43fe-9498-bb28e483ed1f' // Work label
},
{
id: '3',
title: 'Team standup meeting',
+ description: null,
status: 'todo',
priority: 'low',
- dueDate: undefined, // Make unscheduled
+ dueDate: null,
timeTracked: 0,
isTracking: false,
+ projectId: null,
+ notes: null,
labelId: '274f0ba4-a133-471a-bbe9-8189aa3b0106' // Personal label
},
{
@@ -59,21 +64,25 @@ const initialTasks: Task[] = [
description: 'Users unable to login with special characters in password',
status: 'todo',
priority: 'high',
- dueDate: undefined, // Make unscheduled
+ dueDate: null,
timeTracked: 30,
isTracking: false,
projectId: 'project-2',
+ notes: null,
labelId: 'c5eccac9-7919-4eb1-bb50-badacad6b1c1' // Study label
},
{
id: '5',
title: 'Deploy new features',
+ description: null,
status: 'done',
priority: 'medium',
dueDate: subDays(new Date(), 2),
timeTracked: 120,
isTracking: false,
- projectId: 'project-1'
+ projectId: 'project-1',
+ notes: null,
+ labelId: null
}
];
@@ -86,14 +95,15 @@ function App() {
const task: Task = {
id: Date.now().toString(),
title: newTask.title || '',
- description: newTask.description,
+ description: newTask.description || null,
status: 'todo',
priority: newTask.priority || 'medium',
- dueDate: newTask.dueDate,
+ dueDate: newTask.dueDate || null,
timeTracked: 0,
isTracking: false,
- projectId: newTask.projectId,
- labelId: newTask.labelId
+ projectId: newTask.projectId || null,
+ notes: newTask.notes || null,
+ labelId: newTask.labelId || null
};
setTasks(prev => [...prev, task]);
console.log('Task created:', task);
@@ -155,37 +165,6 @@ function App() {
/>
);
- case 'settings':
- return (
-
-
-
Settings
-
-
-
-
-
Dark Mode
-
Toggle between light and dark themes
-
-
-
-
-
-
-
-
About TaskFlow
-
- A personal task management app with calendar scheduling, kanban boards, and project templates.
-
-
- Version 1.0.0 • Built with React and Tailwind CSS
-
-
-
-
-
- );
-
default:
return (
diff --git a/client/src/components/CalendarView.tsx b/client/src/components/CalendarView.tsx
index dac863d..ed523c0 100644
--- a/client/src/components/CalendarView.tsx
+++ b/client/src/components/CalendarView.tsx
@@ -24,10 +24,10 @@ export default function CalendarView({ tasks, onTaskDrop, onDateSelect }: Calend
refetchOnWindowFocus: false,
});
- // Get 6 weeks: 1 previous week (grayed out) + current week + 4 future weeks
- const currentWeekStart = startOfWeek(currentDate);
+ // Get 5 weeks: 1 previous week (grayed out) + current week + 3 future weeks
+ const currentWeekStart = startOfWeek(currentDate, { weekStartsOn: 1 }); // Monday = 1
const startDate = subWeeks(currentWeekStart, 1); // Start from 1 week before current week
- const dates = Array.from({ length: 42 }, (_, i) => addDays(startDate, i));
+ const dates = Array.from({ length: 35 }, (_, i) => addDays(startDate, i));
const getTasksForDate = (date: Date) => {
return tasks.filter(task =>
@@ -69,7 +69,7 @@ export default function CalendarView({ tasks, onTaskDrop, onDateSelect }: Calend
- 6-Week Calendar View
+ 5-Week Calendar View
diff --git a/client/src/components/KanbanBoard.tsx b/client/src/components/KanbanBoard.tsx
index 0ed76a4..02efa4a 100644
--- a/client/src/components/KanbanBoard.tsx
+++ b/client/src/components/KanbanBoard.tsx
@@ -39,8 +39,8 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate }:
let filteredTasks = tasks.filter(task => task.status === status);
if (viewMode === 'weekly') {
- const weekStart = startOfWeek(currentDate);
- const weekEnd = endOfWeek(currentDate);
+ const weekStart = startOfWeek(currentDate, { weekStartsOn: 1 }); // Monday = 1
+ const weekEnd = endOfWeek(currentDate, { weekStartsOn: 1 });
filteredTasks = filteredTasks.filter(task =>
task.dueDate && isWithinInterval(task.dueDate, { start: weekStart, end: weekEnd })
);
@@ -87,7 +87,7 @@ export default function KanbanBoard({ tasks, onTaskStatusChange, onTaskUpdate }:
const getPeriodTitle = () => {
if (viewMode === 'weekly') {
- return `Week of ${format(startOfWeek(currentDate), 'MMM d')}`;
+ return `Week of ${format(startOfWeek(currentDate, { weekStartsOn: 1 }), 'MMM d')}`;
} else if (viewMode === 'monthly') {
return format(currentDate, 'MMMM yyyy');
}
diff --git a/client/src/components/ProjectTemplate.tsx b/client/src/components/ProjectTemplate.tsx
index 7616a90..1d9fa54 100644
--- a/client/src/components/ProjectTemplate.tsx
+++ b/client/src/components/ProjectTemplate.tsx
@@ -9,7 +9,6 @@ import { Calendar } from '@/components/ui/calendar';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Plus, Calendar as CalendarIcon, Clock, Copy, Play, Tag, Edit, Trash2, Palette } from 'lucide-react';
-import { Task } from './TaskCard';
import { Label } from '@shared/schema';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiRequest } from '@/lib/queryClient';
@@ -95,6 +94,12 @@ export default function ProjectTemplate({
const [isEditProjectOpen, setIsEditProjectOpen] = useState(false);
const [newProjectName, setNewProjectName] = useState('');
const [newProjectDescription, setNewProjectDescription] = useState('');
+ const [isRestartProjectOpen, setIsRestartProjectOpen] = useState(false);
+ const [restartingProject, setRestartingProject] = useState
(null);
+ const [newEndDate, setNewEndDate] = useState();
+ const [clearHistory, setClearHistory] = useState(false);
+ const [isEndDateCalendarOpen, setIsEndDateCalendarOpen] = useState(false);
+ const [closedProjectsFilter, setClosedProjectsFilter] = useState('');
// Label management state
const [isLabelDialogOpen, setIsLabelDialogOpen] = useState(false);
@@ -197,6 +202,43 @@ export default function ProjectTemplate({
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
};
+
+ const handleRestartProject = (project: Project) => {
+ setRestartingProject(project);
+ setIsRestartProjectOpen(true);
+ };
+
+ const handleConfirmRestart = () => {
+ if (restartingProject && newEndDate) {
+ const restartedProject: Project = {
+ ...restartingProject,
+ status: 'planning',
+ startDate: new Date(),
+ endDate: newEndDate,
+ tasks: clearHistory ? [] : restartingProject.tasks.map(task => ({ ...task, status: 'todo' as const }))
+ };
+
+ setProjects(projects.map(p =>
+ p.id === restartingProject.id ? restartedProject : p
+ ));
+
+ setIsRestartProjectOpen(false);
+ setRestartingProject(null);
+ setNewEndDate(undefined);
+ setClearHistory(false);
+ console.log('Project restarted:', restartedProject.name, 'Clear history:', clearHistory);
+ }
+ };
+
+ const getFilteredClosedProjects = () => {
+ const closedProjects = projects.filter(project => project.status === 'finished');
+ if (!closedProjectsFilter) return closedProjects;
+
+ return closedProjects.filter(project =>
+ project.name.toLowerCase().includes(closedProjectsFilter.toLowerCase()) ||
+ (project.description && project.description.toLowerCase().includes(closedProjectsFilter.toLowerCase()))
+ );
+ };
// Label management functions
const handleSaveLabel = () => {
@@ -378,8 +420,165 @@ export default function ProjectTemplate({
);
})}
+
+ {/* Closed Projects Section */}
+
+
+
Closed Projects
+ setClosedProjectsFilter(e.target.value)}
+ className="max-w-xs"
+ data-testid="input-filter-closed-projects"
+ />
+
+
+
+ {getFilteredClosedProjects().map((project) => (
+
+
+
+
+
+ {project.name}
+
+ {project.description && (
+
+ {project.description}
+
+ )}
+
+
+
+ Finished
+
+
+
+
+
+
+ {getTotalEstimatedHours(project)}h
+
+
+
+ {project.tasks.length} tasks
+
+
+
+
+ {project.endDate && (
+
+ Finished: {project.endDate.toLocaleDateString()}
+
+ )}
+
+
+
+
+ ))}
+
+ {getFilteredClosedProjects().length === 0 && (
+
+ {closedProjectsFilter ? 'No closed projects match your filter' : 'No closed projects yet'}
+
+ )}
+
+
+ {/* Restart Project Dialog */}
+
+
{/* Create Project Dialog */}