Add quick ways to complete and delete tasks
Implement a checkbox for marking tasks as done and a delete button directly on the TaskCard component, enhancing user interaction. 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/nn5bWKE
This commit is contained in:
@@ -14,6 +14,10 @@ run = ["npm", "run", "start"]
|
|||||||
localPort = 5000
|
localPort = 5000
|
||||||
externalPort = 80
|
externalPort = 80
|
||||||
|
|
||||||
|
[[ports]]
|
||||||
|
localPort = 33679
|
||||||
|
externalPort = 4200
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 35345
|
localPort = 35345
|
||||||
externalPort = 3002
|
externalPort = 3002
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Clock, Calendar, Play, Pause, MoreHorizontal, Edit, Trash2, Timer, CheckCircle } from "lucide-react";
|
import { Clock, Calendar, Play, Pause, MoreHorizontal, Edit, Trash2, Timer, CheckCircle, X } from "lucide-react";
|
||||||
import { Task, Label } from '@shared/schema';
|
import { Task, Label } from '@shared/schema';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
|
||||||
interface TaskCardProps {
|
interface TaskCardProps {
|
||||||
task: Task;
|
task: Task;
|
||||||
@@ -74,25 +75,44 @@ export default function TaskCard({ task, onStartTimer, onStopTimer, onEdit, onDe
|
|||||||
return `${hours}h ${mins}m`;
|
return `${hours}h ${mins}m`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleComplete = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (task.status === 'done') {
|
||||||
|
onStatusChange?.('todo');
|
||||||
|
} else {
|
||||||
|
onStatusChange?.('done');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className={`p-4 hover-elevate active-elevate-2 transition-all duration-200 relative cursor-pointer ${
|
className={`p-4 hover-elevate active-elevate-2 transition-all duration-200 relative ${
|
||||||
isDragging ? 'rotate-1 scale-105 shadow-lg' : ''
|
isDragging ? 'rotate-1 scale-105 shadow-lg' : ''
|
||||||
}`}
|
} ${task.status === 'done' ? 'opacity-60' : ''}`}
|
||||||
style={taskLabel ? {
|
style={taskLabel ? {
|
||||||
borderColor: taskLabel.color,
|
borderColor: taskLabel.color,
|
||||||
borderWidth: '2px',
|
borderWidth: '2px',
|
||||||
borderStyle: 'solid'
|
borderStyle: 'solid'
|
||||||
} : {}}
|
} : {}}
|
||||||
data-testid={`card-task-${task.id}`}
|
data-testid={`card-task-${task.id}`}
|
||||||
onClick={() => {
|
|
||||||
onEdit?.();
|
|
||||||
console.log(`Task card clicked: ${task.title}`);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="flex-1 min-w-0">
|
{/* Checkbox for quick completion */}
|
||||||
<h3 className="font-medium text-sm leading-tight truncate" data-testid={`text-task-title-${task.id}`}>
|
<Checkbox
|
||||||
|
checked={task.status === 'done'}
|
||||||
|
onClick={handleToggleComplete}
|
||||||
|
data-testid={`checkbox-complete-${task.id}`}
|
||||||
|
className="mt-0.5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="flex-1 min-w-0 cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
onEdit?.();
|
||||||
|
console.log(`Task card clicked: ${task.title}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3 className={`font-medium text-sm leading-tight truncate ${task.status === 'done' ? 'line-through' : ''}`} data-testid={`text-task-title-${task.id}`}>
|
||||||
{task.title}
|
{task.title}
|
||||||
</h3>
|
</h3>
|
||||||
{task.description && (
|
{task.description && (
|
||||||
@@ -142,7 +162,23 @@ export default function TaskCard({ task, onStartTimer, onStopTimer, onEdit, onDe
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex items-start gap-1">
|
||||||
|
{/* Quick delete button */}
|
||||||
|
{onDelete && (
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onDelete();
|
||||||
|
}}
|
||||||
|
data-testid={`button-delete-${task.id}`}
|
||||||
|
className="w-6 h-6 text-muted-foreground hover:text-destructive"
|
||||||
|
>
|
||||||
|
<X className="w-3 h-3" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -255,19 +291,6 @@ export default function TaskCard({ task, onStartTimer, onStopTimer, onEdit, onDe
|
|||||||
)}
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant={task.isTracking ? "default" : "outline"}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleToggleTimer();
|
|
||||||
}}
|
|
||||||
data-testid={`button-timer-${task.id}`}
|
|
||||||
className="w-6 h-6"
|
|
||||||
>
|
|
||||||
{task.isTracking ? <Pause className="w-3 h-3" /> : <Play className="w-3 h-3" />}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user