Add core components for task management and navigation

This commit introduces the foundational UI components and logic for the task management application, including task creation, calendar views, Kanban boards, and navigation elements.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/yy9YLEW
This commit is contained in:
paul-nothaft
2025-09-11 08:59:20 +00:00
parent 5d959e8ab3
commit 28ad3f1535
88 changed files with 17059 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Clock, Calendar, Play, Pause, MoreHorizontal } from "lucide-react";
import { useState } from "react";
export interface Task {
id: string;
title: string;
description?: string;
status: 'todo' | 'inProgress' | 'done';
priority: 'low' | 'medium' | 'high';
dueDate?: Date;
timeTracked: number; // in minutes
isTracking: boolean;
projectId?: string;
notes?: string;
}
interface TaskCardProps {
task: Task;
onPlay?: () => void;
onPause?: () => void;
onEdit?: () => void;
onStatusChange?: (status: Task['status']) => void;
isDragging?: boolean;
}
export default function TaskCard({ task, onPlay, onPause, onEdit, onStatusChange, isDragging }: TaskCardProps) {
const [isTimerRunning, setIsTimerRunning] = useState(task.isTracking);
const handleToggleTimer = () => {
if (isTimerRunning) {
onPause?.();
console.log(`Timer paused for task: ${task.title}`);
} else {
onPlay?.();
console.log(`Timer started for task: ${task.title}`);
}
setIsTimerRunning(!isTimerRunning);
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'bg-destructive text-destructive-foreground';
case 'medium': return 'bg-yellow-500 text-white';
default: return 'bg-muted text-muted-foreground';
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'done': return 'bg-green-500 text-white';
case 'inProgress': return 'bg-primary text-primary-foreground';
default: return 'bg-muted text-muted-foreground';
}
};
const formatTime = (minutes: number) => {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return `${hours}h ${mins}m`;
};
return (
<Card
className={`p-4 hover-elevate active-elevate-2 transition-all duration-200 ${
isDragging ? 'rotate-1 scale-105 shadow-lg' : ''
}`}
data-testid={`card-task-${task.id}`}
>
<div className="flex items-start justify-between gap-3">
<div className="flex-1 min-w-0">
<h3 className="font-medium text-sm leading-tight truncate" data-testid={`text-task-title-${task.id}`}>
{task.title}
</h3>
{task.description && (
<p className="text-xs text-muted-foreground mt-1 line-clamp-2" data-testid={`text-task-description-${task.id}`}>
{task.description}
</p>
)}
<div className="flex items-center gap-2 mt-3 flex-wrap">
<Badge
variant="secondary"
className={`text-xs ${getPriorityColor(task.priority)}`}
data-testid={`badge-priority-${task.id}`}
>
{task.priority}
</Badge>
<Badge
variant="outline"
className={`text-xs ${getStatusColor(task.status)}`}
data-testid={`badge-status-${task.id}`}
>
{task.status.replace(/([A-Z])/g, ' $1').toLowerCase()}
</Badge>
{task.dueDate && (
<div className="flex items-center gap-1 text-xs text-muted-foreground">
<Calendar className="w-3 h-3" />
<span data-testid={`text-due-date-${task.id}`}>
{task.dueDate.toLocaleDateString()}
</span>
</div>
)}
</div>
{task.timeTracked > 0 && (
<div className="flex items-center gap-1 mt-2 text-xs text-muted-foreground">
<Clock className="w-3 h-3" />
<span data-testid={`text-time-tracked-${task.id}`}>
{formatTime(task.timeTracked)}
</span>
</div>
)}
</div>
<div className="flex flex-col gap-2">
<Button
size="icon"
variant="ghost"
onClick={(e) => {
e.stopPropagation();
onEdit?.();
console.log(`Edit task: ${task.title}`);
}}
data-testid={`button-edit-${task.id}`}
className="w-6 h-6"
>
<MoreHorizontal className="w-3 h-3" />
</Button>
<Button
size="icon"
variant={isTimerRunning ? "default" : "outline"}
onClick={handleToggleTimer}
data-testid={`button-timer-${task.id}`}
className="w-6 h-6"
>
{isTimerRunning ? <Pause className="w-3 h-3" /> : <Play className="w-3 h-3" />}
</Button>
</div>
</div>
</Card>
);
}