From 8f5de8f1a128411cae0ca2071d638a0d298d0d46 Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Wed, 9 Jul 2025 17:11:15 +0530 Subject: [PATCH] refactor(task-management): update search handling and improve task filtering - Modified search handling to utilize the taskManagement slice for consistent state management across components. - Enhanced placeholder text in search filters for better user guidance. - Updated task fetching logic to ensure accurate search value retrieval from the correct state slice. --- .../src/controllers/tasks-controller-v2.ts | 2 +- .../task-list-v2/TaskRowWithSubtasks.tsx | 99 ++++++++++++------- .../task-management/improved-task-filters.tsx | 17 ++-- .../task-management/task-management.slice.ts | 4 +- 4 files changed, 73 insertions(+), 49 deletions(-) diff --git a/worklenz-backend/src/controllers/tasks-controller-v2.ts b/worklenz-backend/src/controllers/tasks-controller-v2.ts index 3c17c974..27df13e7 100644 --- a/worklenz-backend/src/controllers/tasks-controller-v2.ts +++ b/worklenz-backend/src/controllers/tasks-controller-v2.ts @@ -109,7 +109,7 @@ export default class TasksControllerV2 extends TasksControllerBase { } private static getQuery(userId: string, options: ParsedQs) { - const searchField = options.search ? "t.name" : "sort_order"; + const searchField = options.search ? ["t.name", "CONCAT((SELECT key FROM projects WHERE id = t.project_id), '-', task_no)"] : "sort_order"; const { searchQuery, sortField } = TasksControllerV2.toPaginationOptions(options, searchField); const isSubTasks = !!options.parent_task; diff --git a/worklenz-frontend/src/components/task-list-v2/TaskRowWithSubtasks.tsx b/worklenz-frontend/src/components/task-list-v2/TaskRowWithSubtasks.tsx index b9a511e6..e38c0b72 100644 --- a/worklenz-frontend/src/components/task-list-v2/TaskRowWithSubtasks.tsx +++ b/worklenz-frontend/src/components/task-list-v2/TaskRowWithSubtasks.tsx @@ -35,6 +35,8 @@ interface AddSubtaskRowProps { onSubtaskAdded: (rowId: string) => void; rowId: string; // Unique identifier for this add subtask row autoFocus?: boolean; // Whether this row should auto-focus on mount + isActive?: boolean; // Whether this row should show the input/button + onActivate?: (rowId: string) => void; // Callback when row becomes active } const AddSubtaskRow: React.FC = memo(({ @@ -43,7 +45,9 @@ const AddSubtaskRow: React.FC = memo(({ visibleColumns, onSubtaskAdded, rowId, - autoFocus = false + autoFocus = false, + isActive = true, + onActivate }) => { const [isAdding, setIsAdding] = useState(autoFocus); const [subtaskName, setSubtaskName] = useState(''); @@ -127,32 +131,40 @@ const AddSubtaskRow: React.FC = memo(({
- {!isAdding ? ( - + {isActive ? ( + !isAdding ? ( + + ) : ( + setSubtaskName(e.target.value)} + onPressEnter={handleAddSubtask} + onBlur={handleCancel} + onKeyDown={handleKeyDown} + placeholder="Type subtask name and press Enter to save" + className="w-full h-full border-none shadow-none bg-transparent" + style={{ + height: '100%', + minHeight: '32px', + padding: '0', + fontSize: '14px' + }} + autoFocus + /> + ) ) : ( - setSubtaskName(e.target.value)} - onPressEnter={handleAddSubtask} - onBlur={handleCancel} - onKeyDown={handleKeyDown} - placeholder="Type subtask name and press Enter to save" - className="w-full h-full border-none shadow-none bg-transparent" - style={{ - height: '100%', - minHeight: '32px', - padding: '0', - fontSize: '14px' - }} - autoFocus - /> + // Empty space when not active +
)}
@@ -208,6 +220,10 @@ const TaskRowWithSubtasks: React.FC = memo(({ }); }, [taskId]); + const handleRowActivate = useCallback((rowId: string) => { + setActiveRowId(rowId); + }, []); + if (!task) { return null; } @@ -250,18 +266,25 @@ const TaskRowWithSubtasks: React.FC = memo(({ {!isLoadingSubtasks && ( <> {/* Render all add subtask rows */} - {addSubtaskRows.map((rowId, index) => ( -
- -
- ))} + {addSubtaskRows.map((rowId, index) => { + const isLastRow = index === addSubtaskRows.length - 1; + const isRowActive = activeRowId === null ? isLastRow : activeRowId === rowId; + + return ( +
+ +
+ ); + })} )} diff --git a/worklenz-frontend/src/components/task-management/improved-task-filters.tsx b/worklenz-frontend/src/components/task-management/improved-task-filters.tsx index 807d1280..b14d27e3 100644 --- a/worklenz-frontend/src/components/task-management/improved-task-filters.tsx +++ b/worklenz-frontend/src/components/task-management/improved-task-filters.tsx @@ -660,7 +660,7 @@ const SearchFilter: React.FC<{ type="text" value={localValue} onChange={e => setLocalValue(e.target.value)} - placeholder={placeholder || t('searchTasks')} + placeholder={placeholder || t('searchTasks') || 'Search tasks by name or key...'} className={`w-full pr-4 pl-8 py-1 rounded border focus:outline-none focus:ring-2 focus:ring-gray-500 transition-colors duration-150 ${ isDarkMode ? 'bg-gray-700 text-gray-100 placeholder-gray-400 border-gray-600' @@ -919,10 +919,10 @@ const ImprovedTaskFilters: React.FC = ({ position, cla useFilterDataLoader(); // Get search value from Redux based on position - const taskReducerSearch = useAppSelector(state => state.taskReducer?.search || ''); + const taskManagementSearch = useAppSelector(state => state.taskManagement?.search || ''); const kanbanSearch = useAppSelector(state => state.enhancedKanbanReducer?.search || ''); - const searchValue = position === 'board' ? kanbanSearch : taskReducerSearch; + const searchValue = position === 'board' ? kanbanSearch : taskManagementSearch; // Local state for filter sections const [filterSections, setFilterSections] = useState([]); @@ -1001,8 +1001,8 @@ const ImprovedTaskFilters: React.FC = ({ position, cla // Debounced search change function debouncedSearchChangeRef.current = createDebouncedFunction( (projectId: string, value: string) => { - // Always use taskReducer search for list view since that's what we read from - dispatch(setSearch(value)); + // Use taskManagement search for list view + dispatch(setTaskManagementSearch(value)); // Trigger task refetch with new search value dispatch(fetchTasksV3(projectId)); @@ -1142,6 +1142,7 @@ const ImprovedTaskFilters: React.FC = ({ position, cla } } else { // Use debounced search for list view + dispatch(setTaskManagementSearch(value)); if (projectId) { debouncedSearchChangeRef.current?.(projectId, value); } @@ -1177,8 +1178,8 @@ const ImprovedTaskFilters: React.FC = ({ position, cla // Prepare all Redux actions to be dispatched together const reduxUpdates = () => { - // Clear search - always use taskReducer for list view - dispatch(setSearch('')); + // Clear search - use taskManagementSearch for list view + dispatch(setTaskManagementSearch('')); // Clear label filters const clearedLabels = currentTaskLabels.map(label => ({ @@ -1249,7 +1250,7 @@ const ImprovedTaskFilters: React.FC = ({ position, cla diff --git a/worklenz-frontend/src/features/task-management/task-management.slice.ts b/worklenz-frontend/src/features/task-management/task-management.slice.ts index c10805e1..ef2f34f9 100644 --- a/worklenz-frontend/src/features/task-management/task-management.slice.ts +++ b/worklenz-frontend/src/features/task-management/task-management.slice.ts @@ -227,8 +227,8 @@ export const fetchTasksV3 = createAsyncThunk( // Get selected priorities from taskReducer const selectedPriorities = state.taskReducer.priorities.join(' '); - // Get search value from taskReducer - const searchValue = state.taskReducer.search || ''; + // Get search value from taskManagement slice + const searchValue = state.taskManagement.search || ''; // Get archived state from task management slice const archivedState = state.taskManagement.archived;