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.
This commit is contained in:
@@ -109,7 +109,7 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static getQuery(userId: string, options: ParsedQs) {
|
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 { searchQuery, sortField } = TasksControllerV2.toPaginationOptions(options, searchField);
|
||||||
|
|
||||||
const isSubTasks = !!options.parent_task;
|
const isSubTasks = !!options.parent_task;
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ interface AddSubtaskRowProps {
|
|||||||
onSubtaskAdded: (rowId: string) => void;
|
onSubtaskAdded: (rowId: string) => void;
|
||||||
rowId: string; // Unique identifier for this add subtask row
|
rowId: string; // Unique identifier for this add subtask row
|
||||||
autoFocus?: boolean; // Whether this row should auto-focus on mount
|
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<AddSubtaskRowProps> = memo(({
|
const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
||||||
@@ -43,7 +45,9 @@ const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
|||||||
visibleColumns,
|
visibleColumns,
|
||||||
onSubtaskAdded,
|
onSubtaskAdded,
|
||||||
rowId,
|
rowId,
|
||||||
autoFocus = false
|
autoFocus = false,
|
||||||
|
isActive = true,
|
||||||
|
onActivate
|
||||||
}) => {
|
}) => {
|
||||||
const [isAdding, setIsAdding] = useState(autoFocus);
|
const [isAdding, setIsAdding] = useState(autoFocus);
|
||||||
const [subtaskName, setSubtaskName] = useState('');
|
const [subtaskName, setSubtaskName] = useState('');
|
||||||
@@ -127,32 +131,40 @@ const AddSubtaskRow: React.FC<AddSubtaskRowProps> = memo(({
|
|||||||
<div className="w-4" />
|
<div className="w-4" />
|
||||||
<div className="w-2" />
|
<div className="w-2" />
|
||||||
|
|
||||||
{!isAdding ? (
|
{isActive ? (
|
||||||
<button
|
!isAdding ? (
|
||||||
onClick={() => setIsAdding(true)}
|
<button
|
||||||
className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors h-full"
|
onClick={() => {
|
||||||
>
|
onActivate?.(rowId);
|
||||||
<PlusOutlined className="text-xs" />
|
setIsAdding(true);
|
||||||
{t('addSubTaskText')}
|
}}
|
||||||
</button>
|
className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors h-full"
|
||||||
|
>
|
||||||
|
<PlusOutlined className="text-xs" />
|
||||||
|
{t('addSubTaskText')}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
value={subtaskName}
|
||||||
|
onChange={(e) => 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
|
||||||
|
/>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<Input
|
// Empty space when not active
|
||||||
ref={inputRef}
|
<div className="h-full" />
|
||||||
value={subtaskName}
|
|
||||||
onChange={(e) => 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
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -208,6 +220,10 @@ const TaskRowWithSubtasks: React.FC<TaskRowWithSubtasksProps> = memo(({
|
|||||||
});
|
});
|
||||||
}, [taskId]);
|
}, [taskId]);
|
||||||
|
|
||||||
|
const handleRowActivate = useCallback((rowId: string) => {
|
||||||
|
setActiveRowId(rowId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -250,18 +266,25 @@ const TaskRowWithSubtasks: React.FC<TaskRowWithSubtasksProps> = memo(({
|
|||||||
{!isLoadingSubtasks && (
|
{!isLoadingSubtasks && (
|
||||||
<>
|
<>
|
||||||
{/* Render all add subtask rows */}
|
{/* Render all add subtask rows */}
|
||||||
{addSubtaskRows.map((rowId, index) => (
|
{addSubtaskRows.map((rowId, index) => {
|
||||||
<div key={rowId} className="bg-gray-50 dark:bg-gray-800/50 border-l-2 border-blue-200 dark:border-blue-700">
|
const isLastRow = index === addSubtaskRows.length - 1;
|
||||||
<AddSubtaskRow
|
const isRowActive = activeRowId === null ? isLastRow : activeRowId === rowId;
|
||||||
parentTaskId={taskId}
|
|
||||||
projectId={projectId}
|
return (
|
||||||
visibleColumns={visibleColumns}
|
<div key={rowId} className="bg-gray-50 dark:bg-gray-800/50 border-l-2 border-blue-200 dark:border-blue-700">
|
||||||
onSubtaskAdded={handleSubtaskAdded}
|
<AddSubtaskRow
|
||||||
rowId={rowId}
|
parentTaskId={taskId}
|
||||||
autoFocus={index === addSubtaskRows.length - 1} // Auto-focus the latest row
|
projectId={projectId}
|
||||||
/>
|
visibleColumns={visibleColumns}
|
||||||
</div>
|
onSubtaskAdded={handleSubtaskAdded}
|
||||||
))}
|
rowId={rowId}
|
||||||
|
autoFocus={isLastRow && activeRowId === rowId} // Auto-focus the latest row when it becomes active
|
||||||
|
isActive={isRowActive}
|
||||||
|
onActivate={handleRowActivate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -660,7 +660,7 @@ const SearchFilter: React.FC<{
|
|||||||
type="text"
|
type="text"
|
||||||
value={localValue}
|
value={localValue}
|
||||||
onChange={e => setLocalValue(e.target.value)}
|
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 ${
|
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
|
isDarkMode
|
||||||
? 'bg-gray-700 text-gray-100 placeholder-gray-400 border-gray-600'
|
? 'bg-gray-700 text-gray-100 placeholder-gray-400 border-gray-600'
|
||||||
@@ -919,10 +919,10 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({ position, cla
|
|||||||
useFilterDataLoader();
|
useFilterDataLoader();
|
||||||
|
|
||||||
// Get search value from Redux based on position
|
// 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 kanbanSearch = useAppSelector(state => state.enhancedKanbanReducer?.search || '');
|
||||||
|
|
||||||
const searchValue = position === 'board' ? kanbanSearch : taskReducerSearch;
|
const searchValue = position === 'board' ? kanbanSearch : taskManagementSearch;
|
||||||
|
|
||||||
// Local state for filter sections
|
// Local state for filter sections
|
||||||
const [filterSections, setFilterSections] = useState<FilterSection[]>([]);
|
const [filterSections, setFilterSections] = useState<FilterSection[]>([]);
|
||||||
@@ -1001,8 +1001,8 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({ position, cla
|
|||||||
// Debounced search change function
|
// Debounced search change function
|
||||||
debouncedSearchChangeRef.current = createDebouncedFunction(
|
debouncedSearchChangeRef.current = createDebouncedFunction(
|
||||||
(projectId: string, value: string) => {
|
(projectId: string, value: string) => {
|
||||||
// Always use taskReducer search for list view since that's what we read from
|
// Use taskManagement search for list view
|
||||||
dispatch(setSearch(value));
|
dispatch(setTaskManagementSearch(value));
|
||||||
|
|
||||||
// Trigger task refetch with new search value
|
// Trigger task refetch with new search value
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
@@ -1142,6 +1142,7 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({ position, cla
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Use debounced search for list view
|
// Use debounced search for list view
|
||||||
|
dispatch(setTaskManagementSearch(value));
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
debouncedSearchChangeRef.current?.(projectId, value);
|
debouncedSearchChangeRef.current?.(projectId, value);
|
||||||
}
|
}
|
||||||
@@ -1177,8 +1178,8 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({ position, cla
|
|||||||
|
|
||||||
// Prepare all Redux actions to be dispatched together
|
// Prepare all Redux actions to be dispatched together
|
||||||
const reduxUpdates = () => {
|
const reduxUpdates = () => {
|
||||||
// Clear search - always use taskReducer for list view
|
// Clear search - use taskManagementSearch for list view
|
||||||
dispatch(setSearch(''));
|
dispatch(setTaskManagementSearch(''));
|
||||||
|
|
||||||
// Clear label filters
|
// Clear label filters
|
||||||
const clearedLabels = currentTaskLabels.map(label => ({
|
const clearedLabels = currentTaskLabels.map(label => ({
|
||||||
@@ -1249,7 +1250,7 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({ position, cla
|
|||||||
<SearchFilter
|
<SearchFilter
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
placeholder="Search tasks..."
|
placeholder="Search tasks by name or key..."
|
||||||
themeClasses={themeClasses}
|
themeClasses={themeClasses}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -227,8 +227,8 @@ export const fetchTasksV3 = createAsyncThunk(
|
|||||||
// Get selected priorities from taskReducer
|
// Get selected priorities from taskReducer
|
||||||
const selectedPriorities = state.taskReducer.priorities.join(' ');
|
const selectedPriorities = state.taskReducer.priorities.join(' ');
|
||||||
|
|
||||||
// Get search value from taskReducer
|
// Get search value from taskManagement slice
|
||||||
const searchValue = state.taskReducer.search || '';
|
const searchValue = state.taskManagement.search || '';
|
||||||
|
|
||||||
// Get archived state from task management slice
|
// Get archived state from task management slice
|
||||||
const archivedState = state.taskManagement.archived;
|
const archivedState = state.taskManagement.archived;
|
||||||
|
|||||||
Reference in New Issue
Block a user