Merge branch 'release/v2.0.4' of https://github.com/Worklenz/worklenz into test/row-kanban-board-v1

This commit is contained in:
shancds
2025-07-03 08:10:29 +05:30
40 changed files with 1615 additions and 2942 deletions

View File

@@ -1,78 +0,0 @@
import React, { useEffect } from 'react';
import { Layout, Typography, Card, Space, Alert } from 'antd';
import { useDispatch } from 'react-redux';
import TaskListBoard from '@/components/task-management/task-list-board';
import { AppDispatch } from '@/app/store';
const { Header, Content } = Layout;
const { Title, Paragraph } = Typography;
const TaskManagementDemo: React.FC = () => {
const dispatch = useDispatch<AppDispatch>();
// Mock project ID for demo
const demoProjectId = 'demo-project-123';
useEffect(() => {
// Initialize demo data if needed
// You might want to populate some sample tasks here for demonstration
}, [dispatch]);
return (
<Layout className="min-h-screen bg-gray-50">
<Header className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4">
<Title level={2} className="mb-0 text-gray-800">
Enhanced Task Management System
</Title>
</div>
</Header>
<Content className="max-w-7xl mx-auto px-4 py-6 w-full">
<Space direction="vertical" size="large" className="w-full">
{/* Introduction */}
<Card>
<Title level={3}>Task Management Features</Title>
<Paragraph>
This enhanced task management system provides a comprehensive interface for managing tasks
with the following key features:
</Paragraph>
<ul className="list-disc list-inside space-y-1 text-gray-700">
<li><strong>Dynamic Grouping:</strong> Group tasks by Status, Priority, or Phase</li>
<li><strong>Drag & Drop:</strong> Reorder tasks within groups or move between groups</li>
<li><strong>Multi-select:</strong> Select multiple tasks for bulk operations</li>
<li><strong>Bulk Actions:</strong> Change status, priority, assignees, or delete multiple tasks</li>
<li><strong>Subtasks:</strong> Expandable subtask support with progress tracking</li>
<li><strong>Real-time Updates:</strong> Live updates via WebSocket connections</li>
<li><strong>Rich Task Display:</strong> Progress bars, assignees, labels, due dates, and more</li>
</ul>
</Card>
{/* Usage Instructions */}
<Alert
message="Demo Instructions"
description={
<div>
<p><strong>Grouping:</strong> Use the dropdown to switch between Status, Priority, and Phase grouping.</p>
<p><strong>Drag & Drop:</strong> Click and drag tasks to reorder within groups or move between groups.</p>
<p><strong>Selection:</strong> Click checkboxes to select tasks, then use bulk actions in the blue bar.</p>
<p><strong>Subtasks:</strong> Click the +/- buttons next to task names to expand/collapse subtasks.</p>
</div>
}
type="info"
showIcon
className="mb-4"
/>
{/* Task List Board */}
<TaskListBoard
projectId={demoProjectId}
className="task-management-demo"
/>
</Space>
</Content>
</Layout>
);
};
export default TaskManagementDemo;

View File

@@ -1,36 +1,80 @@
import { Input } from 'antd';
import React, { useState } from 'react';
import { Input, Button } from 'antd';
import React, { useRef, useEffect, useState } from 'react';
import { useAppSelector } from '../../../../../../hooks/useAppSelector';
import { colors } from '../../../../../../styles/colors';
import { useTranslation } from 'react-i18next';
const AddSubTaskListRow = () => {
const [isEdit, setIsEdit] = useState<boolean>(false);
interface AddSubTaskListRowProps {
visibleColumns: { key: string; label: string; width: number }[];
taskColumnKey: string;
onAdd: (name: string) => void;
onCancel: () => void;
parentTaskId: string;
}
// localization
const AddSubTaskListRow: React.FC<AddSubTaskListRowProps> = ({
visibleColumns,
taskColumnKey,
onAdd,
onCancel,
}) => {
const [subtaskName, setSubtaskName] = useState('');
const inputRef = useRef<any>(null);
const { t } = useTranslation('task-list-table');
// get data theme data from redux
const themeMode = useAppSelector(state => state.themeReducer.mode);
const customBorderColor = themeMode === 'dark' && ' border-[#303030]';
const customBorderColor = themeMode === 'dark' ? ' border-[#303030]' : '';
useEffect(() => {
inputRef.current?.focus();
}, []);
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && subtaskName.trim()) {
onAdd(subtaskName.trim());
setSubtaskName('');
} else if (e.key === 'Escape') {
onCancel();
}
};
return (
<div className={`border-t ${customBorderColor}`}>
{isEdit ? (
<Input
className="h-12 w-full rounded-none"
style={{ borderColor: colors.skyBlue }}
placeholder={t('addTaskInputPlaceholder')}
onBlur={() => setIsEdit(false)}
/>
) : (
<Input
onFocus={() => setIsEdit(true)}
className="w-[300px] border-none"
value={t('addSubTaskText')}
/>
)}
</div>
<tr className={`add-subtask-row${customBorderColor}`}>
{visibleColumns.map(col => (
<td key={col.key} style={{ padding: 0, background: 'inherit' }}>
{col.key === taskColumnKey ? (
<div style={{ display: 'flex', alignItems: 'center', padding: '4px 0' }}>
<Input
ref={inputRef}
value={subtaskName}
onChange={e => setSubtaskName(e.target.value)}
onKeyDown={handleKeyDown}
onBlur={onCancel}
placeholder={t('enterSubtaskName')}
style={{ width: '100%' }}
autoFocus
/>
<Button
size="small"
type="primary"
style={{ marginLeft: 8 }}
disabled={!subtaskName.trim()}
onClick={() => {
if (subtaskName.trim()) {
onAdd(subtaskName.trim());
setSubtaskName('');
}
}}
>
{t('add')}
</Button>
<Button size="small" style={{ marginLeft: 4 }} onClick={onCancel}>
{t('cancel')}
</Button>
</div>
) : null}
</td>
))}
</tr>
);
};

View File

@@ -1365,6 +1365,7 @@ const TaskListTable: React.FC<TaskListTableProps> = ({ taskList, tableId, active
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
const [editingTaskId, setEditingTaskId] = useState<string | null>(null);
const [editColumnKey, setEditColumnKey] = useState<string | null>(null);
const [showAddSubtaskFor, setShowAddSubtaskFor] = useState<string | null>(null);
const toggleTaskExpansion = (taskId: string) => {
const task = displayTasks.find(t => t.id === taskId);
@@ -1857,14 +1858,38 @@ const TaskListTable: React.FC<TaskListTableProps> = ({ taskList, tableId, active
{renderTaskRow(updatedTask)}
{updatedTask.show_sub_tasks && (
<>
{updatedTask?.sub_tasks?.map(subtask =>
{updatedTask?.sub_tasks?.map(subtask =>
subtask?.id ? renderTaskRow(subtask, true) : null
)}
<tr key={`add-subtask-${updatedTask.id}`}>
<td colSpan={visibleColumns.length + 1}>
<AddTaskListRow groupId={tableId} parentTask={updatedTask.id} />
</td>
</tr>
{showAddSubtaskFor !== updatedTask.id && (
<tr key={`add-subtask-link-${updatedTask.id}`}>
<td colSpan={visibleColumns.length + 1}>
<div
style={{
padding: '8px 16px',
color: '#1677ff',
cursor: 'pointer',
fontWeight: 500,
background: '#f6f8fa'
}}
onClick={() => setShowAddSubtaskFor(updatedTask.id)}
>
+ Add Sub Task
</div>
</td>
</tr>
)}
{showAddSubtaskFor === updatedTask.id && (
<tr key={`add-subtask-input-${updatedTask.id}`}>
<td colSpan={visibleColumns.length + 1}>
<AddTaskListRow
groupId={tableId}
parentTask={updatedTask.id}
onCancel={() => setShowAddSubtaskFor(null)}
/>
</td>
</tr>
)}
</>
)}
</React.Fragment>