Add label functionality to tasks for better organization and tracking

Update TaskCard and TaskCreationModal components to support task labels, including UI elements for selection and display. Refactor schema imports to use '@shared/schema'.

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/vtpluZY
This commit is contained in:
paul-nothaft
2025-09-11 12:36:38 +00:00
parent 5f866afbb1
commit 4ea8a7c6a1
5 changed files with 55 additions and 9 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ import CalendarView from './components/CalendarView';
import KanbanBoard from './components/KanbanBoard';
import ProjectTemplate from './components/ProjectTemplate';
import ThemeToggle from './components/ThemeToggle';
import { Task } from './components/TaskCard';
import { Task } from '@shared/schema';
import { addDays, subDays } from 'date-fns';
// Mock data for prototype
+17 -4
View File
@@ -19,12 +19,21 @@ export default function TaskCard({ task, onPlay, onPause, onEdit, onStatusChange
const [isTimerRunning, setIsTimerRunning] = useState(task.isTracking);
// Fetch labels to get the label color
const { data: labels = [] } = useQuery<Label[]>({
const { data: labels = [], isLoading: labelsLoading, error: labelsError } = useQuery<Label[]>({
queryKey: ['/api/labels'],
staleTime: 5 * 60 * 1000, // 5 minutes cache
refetchOnWindowFocus: false,
});
// Find the label for this task
const taskLabel = task.labelId ? labels.find(label => label.id === task.labelId) : null;
const taskLabel = task.labelId && labels.length > 0 ? labels.find(label => label.id === task.labelId) : null;
// Debug logging - always log for tasks with labelId
if (task.labelId) {
console.log(`TaskCard Debug - Task: "${task.title}", LabelId: ${task.labelId}, Labels loading: ${labelsLoading}, Labels count: ${labels.length}, Labels error:`, labelsError);
console.log(`TaskCard Debug - Available labels:`, labels.map(l => ({ id: l.id, name: l.name, color: l.color })));
console.log(`TaskCard Debug - Found label:`, taskLabel);
}
const handleToggleTimer = () => {
if (isTimerRunning) {
@@ -68,8 +77,12 @@ export default function TaskCard({ task, onPlay, onPause, onEdit, onStatusChange
>
{taskLabel && (
<div
className="absolute left-0 top-2 bottom-2 w-1 rounded-r-sm"
style={{ backgroundColor: taskLabel.color }}
className="absolute left-0 top-2 bottom-2 w-1 rounded-r-sm z-10"
style={{
backgroundColor: taskLabel.color,
minWidth: '4px'
}}
data-testid={`accent-label-${task.id}`}
/>
)}
<div className="flex items-start justify-between gap-3">
+34 -2
View File
@@ -6,8 +6,9 @@ import { Textarea } from '@/components/ui/textarea';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Calendar } from '@/components/ui/calendar';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { CalendarIcon, Plus } from 'lucide-react';
import { Task } from './TaskCard';
import { CalendarIcon, Plus, Tag } from 'lucide-react';
import { Task, Label } from '@shared/schema';
import { useQuery } from '@tanstack/react-query';
interface TaskCreationModalProps {
isOpen: boolean;
@@ -20,8 +21,14 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
const [description, setDescription] = useState('');
const [priority, setPriority] = useState<'low' | 'medium' | 'high'>('medium');
const [dueDate, setDueDate] = useState<Date | undefined>();
const [labelId, setLabelId] = useState<string | undefined>();
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
// Fetch labels
const { data: labels = [] } = useQuery<Label[]>({
queryKey: ['/api/labels'],
});
const handleSave = () => {
if (!title.trim()) return;
@@ -30,6 +37,7 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
description: description.trim() || undefined,
priority,
dueDate,
labelId,
status: 'todo',
timeTracked: 0,
isTracking: false
@@ -43,6 +51,7 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
setDescription('');
setPriority('medium');
setDueDate(undefined);
setLabelId(undefined);
onClose();
};
@@ -58,6 +67,7 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
setDescription('');
setPriority('medium');
setDueDate(undefined);
setLabelId(undefined);
onClose();
};
@@ -109,6 +119,28 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
</Select>
</div>
<div className="flex-1">
<Select value={labelId || 'none'} onValueChange={(value) => setLabelId(value === 'none' ? undefined : value)}>
<SelectTrigger data-testid="select-task-label">
<SelectValue placeholder="Label" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">No Label</SelectItem>
{labels.map((label) => (
<SelectItem key={label.id} value={label.id}>
<div className="flex items-center gap-2">
<div
className="w-3 h-3 rounded"
style={{ backgroundColor: label.color }}
/>
{label.name}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Popover open={isCalendarOpen} onOpenChange={setIsCalendarOpen}>
<PopoverTrigger asChild>
<Button
+1 -1
View File
@@ -5,7 +5,7 @@ import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Search, Filter, SortAsc, Calendar, ChevronLeft, ChevronRight } from 'lucide-react';
import { Task } from './TaskCard';
import { Task } from '@shared/schema';
import TaskCard from './TaskCard';
import TimeCompletionModal from './TimeCompletionModal';
import { addDays, format, isSameDay, startOfToday } from 'date-fns';
+2 -1
View File
@@ -1,4 +1,5 @@
import TaskCard, { Task } from '../TaskCard';
import TaskCard from '../TaskCard';
import { Task } from '@shared/schema';
const mockTask: Task = {
id: '1',