Add planned end date for projects and improve task interaction
Adds a 'plannedEndDate' field to projects, making it editable. Enhances task interaction by allowing direct clicking on tasks in various views to open details and report time. Also, includes a helper function to ensure date selections respect the current day. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/P2PZNJ9
This commit is contained in:
@@ -18,6 +18,10 @@ externalPort = 80
|
||||
localPort = 35345
|
||||
externalPort = 3002
|
||||
|
||||
[[ports]]
|
||||
localPort = 38369
|
||||
externalPort = 3001
|
||||
|
||||
[[ports]]
|
||||
localPort = 41353
|
||||
externalPort = 3000
|
||||
|
||||
@@ -13,6 +13,12 @@ import { Label } from '@shared/schema';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiRequest } from '@/lib/queryClient';
|
||||
|
||||
// Helper function to get today's date at start of day for proper date comparison
|
||||
const getStartOfToday = () => {
|
||||
const today = new Date();
|
||||
return new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
};
|
||||
|
||||
interface ProjectTask {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -31,6 +37,7 @@ interface Project {
|
||||
tasks: ProjectTask[];
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
plannedEndDate?: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
@@ -94,6 +101,8 @@ export default function ProjectTemplate({
|
||||
const [isEditProjectOpen, setIsEditProjectOpen] = useState(false);
|
||||
const [newProjectName, setNewProjectName] = useState('');
|
||||
const [newProjectDescription, setNewProjectDescription] = useState('');
|
||||
const [newPlannedEndDate, setNewPlannedEndDate] = useState<Date | undefined>();
|
||||
const [isPlannedEndDateCalendarOpen, setIsPlannedEndDateCalendarOpen] = useState(false);
|
||||
const [isRestartProjectOpen, setIsRestartProjectOpen] = useState(false);
|
||||
const [restartingProject, setRestartingProject] = useState<Project | null>(null);
|
||||
const [newEndDate, setNewEndDate] = useState<Date | undefined>();
|
||||
@@ -162,12 +171,14 @@ export default function ProjectTemplate({
|
||||
description: newProjectDescription.trim() || undefined,
|
||||
status: 'planning',
|
||||
tasks: projectTasks,
|
||||
plannedEndDate: newPlannedEndDate,
|
||||
createdAt: new Date()
|
||||
};
|
||||
|
||||
setProjects([...projects, newProject]);
|
||||
setNewProjectName('');
|
||||
setNewProjectDescription('');
|
||||
setNewPlannedEndDate(undefined);
|
||||
setProjectTasks([]);
|
||||
setIsCreateProjectOpen(false);
|
||||
console.log('Created new project:', newProject.name, 'with', projectTasks.length, 'tasks');
|
||||
@@ -650,7 +661,7 @@ export default function ProjectTemplate({
|
||||
setNewEndDate(date);
|
||||
setIsEndDateCalendarOpen(false);
|
||||
}}
|
||||
disabled={(date) => date < new Date()}
|
||||
disabled={(date) => date < getStartOfToday()}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
@@ -700,6 +711,9 @@ export default function ProjectTemplate({
|
||||
{/* Create Project Dialog */}
|
||||
<Dialog open={isCreateProjectOpen} onOpenChange={() => {
|
||||
setIsCreateProjectOpen(false);
|
||||
setNewProjectName('');
|
||||
setNewProjectDescription('');
|
||||
setNewPlannedEndDate(undefined);
|
||||
setProjectTasks([]);
|
||||
setTaskForm({});
|
||||
setIsEditingTask(false);
|
||||
@@ -731,6 +745,46 @@ export default function ProjectTemplate({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Planned End Date */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Planned End Date (Optional)</label>
|
||||
<Popover open={isPlannedEndDateCalendarOpen} onOpenChange={setIsPlannedEndDateCalendarOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full md:w-80 justify-start text-left"
|
||||
data-testid="button-select-planned-end-date"
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{newPlannedEndDate ? newPlannedEndDate.toLocaleDateString() : "Select planned end date"}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={newPlannedEndDate}
|
||||
onSelect={(date) => {
|
||||
setNewPlannedEndDate(date);
|
||||
setIsPlannedEndDateCalendarOpen(false);
|
||||
}}
|
||||
disabled={(date) => date < getStartOfToday()}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{newPlannedEndDate && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setNewPlannedEndDate(undefined)}
|
||||
className="text-xs text-muted-foreground hover:text-destructive"
|
||||
data-testid="button-clear-create-planned-end-date"
|
||||
>
|
||||
Clear date
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tasks Section */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -980,6 +1034,48 @@ export default function ProjectTemplate({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Planned End Date */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Planned End Date (Optional)</label>
|
||||
<Popover open={isPlannedEndDateCalendarOpen} onOpenChange={setIsPlannedEndDateCalendarOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full md:w-80 justify-start text-left"
|
||||
data-testid="button-edit-planned-end-date"
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{selectedProject.plannedEndDate
|
||||
? selectedProject.plannedEndDate.toLocaleDateString()
|
||||
: "Select planned end date"}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={selectedProject.plannedEndDate}
|
||||
onSelect={(date) => {
|
||||
setSelectedProject({ ...selectedProject, plannedEndDate: date });
|
||||
setIsPlannedEndDateCalendarOpen(false);
|
||||
}}
|
||||
disabled={(date) => date < getStartOfToday()}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{selectedProject.plannedEndDate && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setSelectedProject({ ...selectedProject, plannedEndDate: undefined })}
|
||||
className="text-xs text-muted-foreground hover:text-destructive"
|
||||
data-testid="button-clear-planned-end-date"
|
||||
>
|
||||
Clear date
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tasks Section */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user