Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/components/board/KanbanColumn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
onTaskCreate,
onTaskUpdate,
onTaskDelete,
draggedTask

Check failure on line 13 in client/src/components/board/KanbanColumn.jsx

View workflow job for this annotation

GitHub Actions / test-frontend (18.x)

'draggedTask' is defined but never used
}) => {
const [showCreateForm, setShowCreateForm] = useState(false);
const [showMenu, setShowMenu] = useState(false);
Expand Down Expand Up @@ -99,7 +99,7 @@
>
{/* Create Task Form */}
{showCreateForm && (
<div className="mb-4">
<div className="mb-4 animate-in slide-in-from-top duration-200">
<TaskCreateForm
onSubmit={handleCreateTask}
onCancel={() => setShowCreateForm(false)}
Expand Down
142 changes: 89 additions & 53 deletions client/src/components/board/TaskCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import {
} from 'lucide-react';
import { Avatar } from '../common';
import TaskEditModal from '../common/TaskEditModal';
import ConfirmDialog from '../common/ConfirmDialog';

const TaskCard = ({ task, onUpdate, onDelete, isDragging }) => {
const [showMenu, setShowMenu] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const menuRef = useRef(null);

// Close menu when clicking outside
Expand Down Expand Up @@ -48,13 +50,21 @@ const TaskCard = ({ task, onUpdate, onDelete, isDragging }) => {
}
};

const formatDate = (date) => {
const formatDate = (date, taskStatus) => {
if (!date) return null;
const taskDate = new Date(date);
const today = new Date();
const diffTime = taskDate - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

// Don't show overdue messages for completed tasks
if (taskStatus === 'completed') {
return {
text: `Completed ${taskDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`,
color: 'text-green-600'
};
}

if (diffDays < 0) {
return { text: `${Math.abs(diffDays)}d overdue`, color: 'text-red-600' };
} else if (diffDays === 0) {
Expand All @@ -71,20 +81,27 @@ const TaskCard = ({ task, onUpdate, onDelete, isDragging }) => {
}
};

const dueDateInfo = formatDate(task.dueDate);
const dueDateInfo = formatDate(task.dueDate, task.status);

const handleEdit = () => {
setShowEditModal(true);
setShowMenu(false);
};

const handleDelete = () => {
if (window.confirm('Are you sure you want to delete this task?')) {
onDelete(task._id);
}
setShowConfirmDialog(true);
setShowMenu(false);
};

const confirmDelete = () => {
onDelete(task._id);
setShowConfirmDialog(false);
};

const cancelDelete = () => {
setShowConfirmDialog(false);
};

const handleSaveEdit = async (updatedData) => {
try {
await onUpdate(task._id, updatedData);
Expand Down Expand Up @@ -171,58 +188,66 @@ const TaskCard = ({ task, onUpdate, onDelete, isDragging }) => {
)}

{/* Metadata */}
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
{/* Priority */}
{task.priority && (
<span className={`inline-flex items-center px-2 py-1 text-xs font-medium rounded ${getPriorityColor(task.priority)}`}>
<Flag className="h-3 w-3 mr-1" />
{task.priority}
</span>
)}

{/* Due Date */}
{dueDateInfo && (
<span className={`inline-flex items-center text-xs ${dueDateInfo.color}`}>
<Clock className="h-3 w-3 mr-1" />
{dueDateInfo.text}
</span>
)}
</div>
<div className="space-y-2">
{/* Priority and Due Date Row */}
<div className="flex items-center justify-between flex-wrap gap-2">
<div className="flex items-center space-x-2 flex-wrap">
{/* Priority */}
{task.priority && (
<span className={`inline-flex items-center px-2 py-1 text-xs font-medium rounded ${getPriorityColor(task.priority)}`}>
<Flag className="h-3 w-3 mr-1" />
{task.priority}
</span>
)}

<div className="flex items-center space-x-2">
{/* Comments Count */}
{task.commentsCount > 0 && (
<span className="inline-flex items-center text-xs text-gray-500">
<MessageSquare className="h-3 w-3 mr-1" />
{task.commentsCount}
</span>
)}

{/* Attachments Count */}
{task.attachmentsCount > 0 && (
<span className="inline-flex items-center text-xs text-gray-500">
<Paperclip className="h-3 w-3 mr-1" />
{task.attachmentsCount}
</span>
)}
{/* Due Date */}
{dueDateInfo && (
<span className={`inline-flex items-center text-xs ${dueDateInfo.color} whitespace-nowrap`}>
<Clock className="h-3 w-3 mr-1" />
{dueDateInfo.text}
</span>
)}
</div>

{/* Assignee Avatar */}
{task.assignedTo && (
<Avatar
user={task.assignedTo}
size="xs"
showOnlineStatus={false}
/>
)}
{!task.assignedTo && task.assignee && (
<Avatar
user={task.assignee}
size="xs"
showOnlineStatus={false}
/>
)}
<div className="flex-shrink-0">
{task.assignedTo && (
<Avatar
user={task.assignedTo}
size="xs"
showOnlineStatus={false}
/>
)}
{!task.assignedTo && task.assignee && (
<Avatar
user={task.assignee}
size="xs"
showOnlineStatus={false}
/>
)}
</div>
</div>

{/* Comments and Attachments Row */}
{(task.commentsCount > 0 || task.attachmentsCount > 0) && (
<div className="flex items-center space-x-3">
{/* Comments Count */}
{task.commentsCount > 0 && (
<span className="inline-flex items-center text-xs text-gray-500">
<MessageSquare className="h-3 w-3 mr-1" />
{task.commentsCount}
</span>
)}

{/* Attachments Count */}
{task.attachmentsCount > 0 && (
<span className="inline-flex items-center text-xs text-gray-500">
<Paperclip className="h-3 w-3 mr-1" />
{task.attachmentsCount}
</span>
)}
</div>
)}
</div>

{/* Edit Modal */}
Expand All @@ -232,6 +257,17 @@ const TaskCard = ({ task, onUpdate, onDelete, isDragging }) => {
task={task}
onSave={handleSaveEdit}
/>

{/* Delete Confirmation Dialog */}
<ConfirmDialog
isOpen={showConfirmDialog}
onClose={cancelDelete}
onConfirm={confirmDelete}
title="Delete Task"
message={`Are you sure you want to delete "${task.title}"? This action cannot be undone.`}
confirmText="Delete"
type="danger"
/>
</div>
);
};
Expand Down
Loading
Loading