feat(task-filters): enhance ImprovedTaskFilters for Kanban integration

- Updated useFilterData to support both board and list views, utilizing enhanced Kanban state for filtering.
- Integrated new selectors for Kanban-specific filters including priorities, labels, and assignees.
- Refactored handleSelectionChange and handleSearchChange to accommodate Kanban logic, ensuring proper dispatch of actions based on the selected view.
- Improved overall filter functionality and user experience in task management.
This commit is contained in:
shancds
2025-06-25 17:10:38 +05:30
parent 3c7cacc46f
commit 208d1ad5d4

View File

@@ -45,6 +45,17 @@ import { getTeamMembers } from '@/features/team-members/team-members.slice';
import { ITaskPriority } from '@/types/tasks/taskPriority.types'; import { ITaskPriority } from '@/types/tasks/taskPriority.types';
import { ITaskListColumn } from '@/types/tasks/taskList.types'; import { ITaskListColumn } from '@/types/tasks/taskList.types';
import { IGroupBy } from '@/features/tasks/tasks.slice'; import { IGroupBy } from '@/features/tasks/tasks.slice';
// --- Enhanced Kanban imports ---
import {
setGroupBy as setKanbanGroupBy,
setSearch as setKanbanSearch,
setArchived as setKanbanArchived,
setTaskAssignees as setKanbanTaskAssignees,
setLabels as setKanbanLabels,
setPriorities as setKanbanPriorities,
setMembers as setKanbanMembers,
fetchEnhancedKanbanGroups,
} from '@/features/enhanced-kanban/enhanced-kanban.slice';
// Optimized selectors with proper transformation logic // Optimized selectors with proper transformation logic
const selectFilterData = createSelector( const selectFilterData = createSelector(
@@ -109,7 +120,7 @@ interface ImprovedTaskFiltersProps {
} }
// Get real filter data from Redux state // Get real filter data from Redux state
const useFilterData = (): FilterSection[] => { const useFilterData = (position: 'board' | 'list'): FilterSection[] => {
const { t } = useTranslation('task-list-filters'); const { t } = useTranslation('task-list-filters');
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { projectView } = useTabSearchParam(); const { projectView } = useTabSearchParam();
@@ -117,80 +128,145 @@ const useFilterData = (): FilterSection[] => {
// Use optimized selector to get all filter data at once // Use optimized selector to get all filter data at once
const filterData = useAppSelector(selectFilterData); const filterData = useAppSelector(selectFilterData);
const currentGrouping = useAppSelector(selectCurrentGrouping); const currentGrouping = useAppSelector(selectCurrentGrouping);
// Enhanced Kanban selectors
const kanbanState = useAppSelector((state: RootState) => state.enhancedKanbanReducer);
const kanbanProject = useAppSelector((state: RootState) => state.projectReducer.project);
// Determine which state to use
const isBoard = position === 'board';
const tab = searchParams.get('tab'); const tab = searchParams.get('tab');
const currentProjectView = tab === 'tasks-list' ? 'list' : 'kanban'; const currentProjectView = tab === 'tasks-list' ? 'list' : 'kanban';
return useMemo(() => { return useMemo(() => {
const currentPriorities = currentProjectView === 'list' ? filterData.taskPriorities : filterData.boardPriorities; if (isBoard) {
const currentLabels = currentProjectView === 'list' ? filterData.taskLabels : filterData.boardLabels; // Use enhanced kanban state
const currentAssignees = currentProjectView === 'list' ? filterData.taskAssignees : filterData.boardAssignees; const currentPriorities = kanbanState.priorities || [];
const groupByValue = currentGrouping || 'status'; const currentLabels = kanbanState.labels || [];
const currentAssignees = kanbanState.taskAssignees || [];
return [ const groupByValue = kanbanState.groupBy || 'status';
{ return [
id: 'priority', {
label: 'Priority', id: 'priority',
options: filterData.priorities.map((p: any) => ({ label: 'Priority',
value: p.id, options: (kanbanProject?.priorities || []).map((p: any) => ({
label: p.name, value: p.id,
color: p.color_code, label: p.name,
})), color: p.color_code,
selectedValues: filterData.selectedPriorities, })),
multiSelect: true, selectedValues: currentPriorities,
searchable: false, multiSelect: true,
icon: FlagOutlined, searchable: false,
}, icon: FlagOutlined,
{ },
id: 'assignees', {
label: t('membersText'), id: 'assignees',
icon: TeamOutlined, label: t('membersText'),
multiSelect: true, icon: TeamOutlined,
searchable: true, multiSelect: true,
selectedValues: currentAssignees.filter((m: any) => m.selected && m.id).map((m: any) => m.id || ''), searchable: true,
options: currentAssignees.map((assignee: any) => ({ selectedValues: currentAssignees.filter((m: any) => m.selected && m.id).map((m: any) => m.id || ''),
id: assignee.id || '', options: currentAssignees.map((assignee: any) => ({
label: assignee.name || '', id: assignee.id || '',
value: assignee.id || '', label: assignee.name || '',
avatar: assignee.avatar_url, value: assignee.id || '',
selected: assignee.selected, avatar: assignee.avatar_url,
})), selected: assignee.selected,
}, })),
{ },
id: 'labels', {
label: t('labelsText'), id: 'labels',
icon: TagOutlined, label: t('labelsText'),
multiSelect: true, icon: TagOutlined,
searchable: true, multiSelect: true,
selectedValues: currentLabels.filter((l: any) => l.selected && l.id).map((l: any) => l.id || ''), searchable: true,
options: currentLabels.map((label: any) => ({ selectedValues: currentLabels.filter((l: any) => l.selected && l.id).map((l: any) => l.id || ''),
id: label.id || '', options: currentLabels.map((label: any) => ({
label: label.name || '', id: label.id || '',
value: label.id || '', label: label.name || '',
color: label.color_code, value: label.id || '',
selected: label.selected, color: label.color_code,
})), selected: label.selected,
}, })),
{ },
id: 'groupBy', {
label: t('groupByText'), id: 'groupBy',
icon: GroupOutlined, label: t('groupByText'),
multiSelect: false, icon: GroupOutlined,
searchable: false, multiSelect: false,
selectedValues: [groupByValue], searchable: false,
options: [ selectedValues: [groupByValue],
{ id: 'status', label: t('statusText'), value: 'status' }, options: [
{ id: 'priority', label: t('priorityText'), value: 'priority' }, { id: 'status', label: t('statusText'), value: 'status' },
{ id: 'phase', label: filterData.project?.phase_label || t('phaseText'), value: 'phase' }, { id: 'priority', label: t('priorityText'), value: 'priority' },
], { id: 'phase', label: kanbanProject?.phase_label || t('phaseText'), value: 'phase' },
}, ],
]; },
}, [ ];
filterData, } else {
currentProjectView, // Use task management/board state
t, const currentPriorities = currentProjectView === 'list' ? filterData.taskPriorities : filterData.boardPriorities;
currentGrouping const currentLabels = currentProjectView === 'list' ? filterData.taskLabels : filterData.boardLabels;
]); const currentAssignees = currentProjectView === 'list' ? filterData.taskAssignees : filterData.boardAssignees;
const groupByValue = currentGrouping || 'status';
return [
{
id: 'priority',
label: 'Priority',
options: filterData.priorities.map((p: any) => ({
value: p.id,
label: p.name,
color: p.color_code,
})),
selectedValues: filterData.selectedPriorities,
multiSelect: true,
searchable: false,
icon: FlagOutlined,
},
{
id: 'assignees',
label: t('membersText'),
icon: TeamOutlined,
multiSelect: true,
searchable: true,
selectedValues: currentAssignees.filter((m: any) => m.selected && m.id).map((m: any) => m.id || ''),
options: currentAssignees.map((assignee: any) => ({
id: assignee.id || '',
label: assignee.name || '',
value: assignee.id || '',
avatar: assignee.avatar_url,
selected: assignee.selected,
})),
},
{
id: 'labels',
label: t('labelsText'),
icon: TagOutlined,
multiSelect: true,
searchable: true,
selectedValues: currentLabels.filter((l: any) => l.selected && l.id).map((l: any) => l.id || ''),
options: currentLabels.map((label: any) => ({
id: label.id || '',
label: label.name || '',
value: label.id || '',
color: label.color_code,
selected: label.selected,
})),
},
{
id: 'groupBy',
label: t('groupByText'),
icon: GroupOutlined,
multiSelect: false,
searchable: false,
selectedValues: [groupByValue],
options: [
{ id: 'status', label: t('statusText'), value: 'status' },
{ id: 'priority', label: t('priorityText'), value: 'priority' },
{ id: 'phase', label: filterData.project?.phase_label || t('phaseText'), value: 'phase' },
],
},
];
}
}, [isBoard, kanbanState, kanbanProject, filterData, currentProjectView, t, currentGrouping]);
}; };
// Filter Dropdown Component // Filter Dropdown Component
@@ -629,7 +705,7 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({
const [activeFiltersCount, setActiveFiltersCount] = useState(0); const [activeFiltersCount, setActiveFiltersCount] = useState(0);
// Get real filter data // Get real filter data
const filterSectionsData = useFilterData(); const filterSectionsData = useFilterData(position);
// Check if data is loaded - memoize this computation // Check if data is loaded - memoize this computation
const isDataLoaded = useMemo(() => { const isDataLoaded = useMemo(() => {
@@ -686,67 +762,76 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({
const handleSelectionChange = useCallback((sectionId: string, values: string[]) => { const handleSelectionChange = useCallback((sectionId: string, values: string[]) => {
if (!projectId) return; if (!projectId) return;
if (position === 'board') {
// Prevent clearing all group by options // Enhanced Kanban logic
if (sectionId === 'groupBy' && values.length === 0) { if (sectionId === 'groupBy' && values.length > 0) {
return; // Do nothing dispatch(setKanbanGroupBy(values[0] as any));
dispatch(fetchEnhancedKanbanGroups(projectId));
return;
}
if (sectionId === 'priority') {
dispatch(setKanbanPriorities(values));
dispatch(fetchEnhancedKanbanGroups(projectId));
return;
}
if (sectionId === 'assignees') {
dispatch(setKanbanTaskAssignees(
// Map to {id, selected, ...}
values.map(id => ({ id, selected: true }))
));
dispatch(fetchEnhancedKanbanGroups(projectId));
return;
}
if (sectionId === 'labels') {
dispatch(setKanbanLabels(
values.map(id => ({ id, selected: true }))
));
dispatch(fetchEnhancedKanbanGroups(projectId));
return;
}
} else {
// ... existing list logic ...
if (sectionId === 'groupBy' && values.length > 0) {
dispatch(setCurrentGrouping(values[0] as 'status' | 'priority' | 'phase'));
dispatch(fetchTasksV3(projectId));
return;
}
if (sectionId === 'priority') {
dispatch(setSelectedPriorities(values));
dispatch(fetchTasksV3(projectId));
return;
}
if (sectionId === 'assignees') {
const updatedAssignees = currentTaskAssignees.map(member => ({
...member,
selected: values.includes(member.id || '')
}));
dispatch(setMembers(updatedAssignees));
dispatch(fetchTasksV3(projectId));
return;
}
if (sectionId === 'labels') {
const updatedLabels = currentTaskLabels.map(label => ({
...label,
selected: values.includes(label.id || '')
}));
dispatch(setLabels(updatedLabels));
dispatch(fetchTasksV3(projectId));
return;
}
} }
}, [dispatch, projectId, position, currentTaskAssignees, currentTaskLabels]);
// Update local state first
setFilterSections(prev => prev.map(section =>
section.id === sectionId
? { ...section, selectedValues: values }
: section
));
// Use task management slices for groupBy
if (sectionId === 'groupBy' && values.length > 0) {
dispatch(setCurrentGrouping(values[0] as 'status' | 'priority' | 'phase'));
dispatch(fetchTasksV3(projectId));
return;
}
// Handle priorities
if (sectionId === 'priority') {
console.log('Priority selection changed:', { sectionId, values, projectId });
dispatch(setSelectedPriorities(values));
dispatch(fetchTasksV3(projectId));
return;
}
// Handle assignees (members)
if (sectionId === 'assignees') {
// Update selected property for each assignee
const updatedAssignees = currentTaskAssignees.map(member => ({
...member,
selected: values.includes(member.id || '')
}));
dispatch(setMembers(updatedAssignees));
dispatch(fetchTasksV3(projectId));
return;
}
// Handle labels
if (sectionId === 'labels') {
// Update selected property for each label
const updatedLabels = currentTaskLabels.map(label => ({
...label,
selected: values.includes(label.id || '')
}));
dispatch(setLabels(updatedLabels));
dispatch(fetchTasksV3(projectId));
return;
}
}, [dispatch, projectId, currentTaskAssignees, currentTaskLabels]);
const handleSearchChange = useCallback((value: string) => { const handleSearchChange = useCallback((value: string) => {
setSearchValue(value); setSearchValue(value);
if (position === 'board') {
// Log the search change for now dispatch(setKanbanSearch(value));
console.log('Search change:', value, { projectView, projectId }); dispatch(fetchEnhancedKanbanGroups(projectId));
} else {
// TODO: Implement proper search dispatch // ... existing logic ...
}, [projectView, projectId]); // TODO: Implement proper search dispatch for list
}
}, [dispatch, projectId, position]);
const clearAllFilters = useCallback(() => { const clearAllFilters = useCallback(() => {
// TODO: Implement clear all filters // TODO: Implement clear all filters
@@ -757,9 +842,13 @@ const ImprovedTaskFilters: React.FC<ImprovedTaskFiltersProps> = ({
const toggleArchived = useCallback(() => { const toggleArchived = useCallback(() => {
setShowArchived(!showArchived); setShowArchived(!showArchived);
// TODO: Implement proper archived toggle if (position === 'board') {
console.log('Toggle archived:', !showArchived); dispatch(setKanbanArchived(!showArchived));
}, [showArchived]); dispatch(fetchEnhancedKanbanGroups(projectId));
} else {
// ... existing logic ...
}
}, [dispatch, projectId, position, showArchived]);
// Show fields dropdown functionality // Show fields dropdown functionality
const handleColumnVisibilityChange = useCallback(async (col: ITaskListColumn) => { const handleColumnVisibilityChange = useCallback(async (col: ITaskListColumn) => {