- Added 'onDelete' and 'onNotes' support to TaskItem. - Implemented hover actions (Delete and Notes icons) for tasks. - Added Notes Modal for editing task markdown content. - Simplified navigation animation to remove blank flash (single-phase slide-in). - Fixed syntax error in updateTask function. - Updated styles for modal and task actions.
198 lines
6.0 KiB
TypeScript
198 lines
6.0 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Task } from '../types/task';
|
|
import TaskCard from './TaskCard';
|
|
|
|
interface CalendarGridProps {
|
|
tasks: Task[];
|
|
onTaskToggle: (id: string) => void;
|
|
onTaskEdit: (task: Task) => void;
|
|
onTaskDelete: (id: string) => void;
|
|
onAddTask: (date: Date) => void;
|
|
currentDate: Date;
|
|
onTaskMove?: (taskId: string, newDate: Date) => void;
|
|
}
|
|
|
|
const CalendarGrid: React.FC<CalendarGridProps> = ({
|
|
tasks,
|
|
onTaskToggle,
|
|
onTaskEdit,
|
|
onTaskDelete,
|
|
onAddTask,
|
|
currentDate,
|
|
onTaskMove
|
|
}) => {
|
|
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
|
|
|
// Generate days for the current week
|
|
const generateWeekDays = (startDate: Date) => {
|
|
const days = [];
|
|
const start = new Date(startDate);
|
|
start.setDate(start.getDate() - startDate.getDay()); // Start from Sunday
|
|
|
|
for (let i = 0; i < 7; i++) {
|
|
const day = new Date(start);
|
|
day.setDate(start.getDate() + i);
|
|
days.push(day);
|
|
}
|
|
return days;
|
|
};
|
|
|
|
const weekDays = generateWeekDays(currentDate);
|
|
|
|
// Group tasks by date
|
|
const groupTasksByDate = (tasks: Task[]) => {
|
|
const grouped: Record<string, Task[]> = {};
|
|
tasks.forEach(task => {
|
|
const dateKey = task.date.toISOString().split('T')[0];
|
|
if (!grouped[dateKey]) {
|
|
grouped[dateKey] = [];
|
|
}
|
|
grouped[dateKey].push(task);
|
|
});
|
|
return grouped;
|
|
};
|
|
|
|
const groupedTasks = groupTasksByDate(tasks);
|
|
|
|
const handleDateClick = (date: Date) => {
|
|
setSelectedDate(date);
|
|
onAddTask(date);
|
|
};
|
|
|
|
const formatDate = (date: Date) => {
|
|
return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });
|
|
};
|
|
|
|
const isToday = (date: Date) => {
|
|
const today = new Date();
|
|
return date.toDateString() === today.toDateString();
|
|
};
|
|
|
|
// Handle drag start for task cards
|
|
const handleDragStart = (e: React.DragEvent, taskId: string, sourceDay: string) => {
|
|
e.dataTransfer.setData('taskId', taskId);
|
|
e.dataTransfer.setData('sourceDay', sourceDay);
|
|
};
|
|
|
|
// Handle drop on a day
|
|
const handleDrop = (e: React.DragEvent, targetDay: string) => {
|
|
e.preventDefault();
|
|
const taskId = e.dataTransfer.getData('taskId');
|
|
const sourceDay = e.dataTransfer.getData('sourceDay');
|
|
|
|
if (taskId && sourceDay !== targetDay && onTaskMove) {
|
|
// Convert targetDay string back to date
|
|
const targetDate = new Date(targetDay);
|
|
onTaskMove(taskId, targetDate);
|
|
}
|
|
};
|
|
|
|
// Allow drops on day cells
|
|
const handleDragOver = (e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white rounded-lg shadow overflow-hidden">
|
|
{/* Week Navigation */}
|
|
<div className="flex justify-between items-center p-4 border-b">
|
|
<button
|
|
className="px-3 py-1 bg-gray-100 rounded hover:bg-gray-200"
|
|
onClick={() => {
|
|
const prevWeek = new Date(currentDate);
|
|
prevWeek.setDate(prevWeek.getDate() - 7);
|
|
// This would typically update the parent state
|
|
}}
|
|
>
|
|
Previous
|
|
</button>
|
|
|
|
<h2 className="text-lg font-semibold">
|
|
{weekDays[0].toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}
|
|
</h2>
|
|
|
|
<button
|
|
className="px-3 py-1 bg-gray-100 rounded hover:bg-gray-200"
|
|
onClick={() => {
|
|
const nextWeek = new Date(currentDate);
|
|
nextWeek.setDate(nextWeek.getDate() + 7);
|
|
// This would typically update the parent state
|
|
}}
|
|
>
|
|
Next
|
|
</button>
|
|
</div>
|
|
|
|
{/* Day Headers */}
|
|
<div className="grid grid-cols-7 gap-0 border-b">
|
|
{weekDays.map((day, index) => (
|
|
<div
|
|
key={index}
|
|
className={`p-2 text-center ${
|
|
isToday(day) ? 'bg-blue-50 text-blue-700 font-bold' : ''
|
|
}`}
|
|
>
|
|
<div className="text-sm text-gray-500">
|
|
{day.toLocaleDateString('en-US', { weekday: 'short' })}
|
|
</div>
|
|
<div className={`text-lg ${isToday(day) ? 'bg-blue-500 text-white rounded-full w-8 h-8 flex items-center justify-center mx-auto' : ''}`}>
|
|
{day.getDate()}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Calendar Grid */}
|
|
<div className="grid grid-cols-7 gap-0">
|
|
{weekDays.map((day, dayIndex) => {
|
|
const dayKey = day.toISOString().split('T')[0];
|
|
const dayTasks = groupedTasks[dayKey] || [];
|
|
|
|
return (
|
|
<div
|
|
key={dayIndex}
|
|
className="border-r border-b min-h-32"
|
|
onClick={() => handleDateClick(day)}
|
|
onDrop={(e) => handleDrop(e, dayKey)}
|
|
onDragOver={handleDragOver}
|
|
>
|
|
<div className="p-2 text-right">
|
|
<span className={`inline-block w-6 h-6 text-center rounded-full ${
|
|
isToday(day) ? 'bg-blue-500 text-white' : ''
|
|
}`}>
|
|
{day.getDate()}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="p-2 space-y-1">
|
|
{dayTasks.map(task => (
|
|
<TaskCard
|
|
key={task.id}
|
|
task={task}
|
|
onToggleComplete={onTaskToggle}
|
|
onEdit={onTaskEdit}
|
|
onDelete={onTaskDelete}
|
|
draggable={true}
|
|
onDragStart={(e) => handleDragStart(e, task.id, dayKey)}
|
|
/>
|
|
))}
|
|
|
|
<button
|
|
className="text-xs text-gray-400 hover:text-gray-600 w-full text-left p-1"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleDateClick(day);
|
|
}}
|
|
>
|
|
+ Add task
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CalendarGrid; |