feat(task-management): enhance bulk action bar with new features and translations
- Added new bulk action options for changing status, priority, and phase, as well as adding labels and assigning tasks. - Integrated translations for English, German, Spanish, and Portuguese in the task table bulk actions. - Updated the `OptimizedBulkActionBar` to utilize Redux state for status, priority, and phase selections. - Improved user experience with dynamic dropdown menus for bulk actions and confirmation prompts. - Refactored task selection handling to ensure proper state management during bulk actions.
This commit is contained in:
@@ -20,5 +20,20 @@
|
|||||||
"hitEnterToCreate": "Enter drücken zum Erstellen",
|
"hitEnterToCreate": "Enter drücken zum Erstellen",
|
||||||
"pendingInvitation": "Einladung ausstehend",
|
"pendingInvitation": "Einladung ausstehend",
|
||||||
"noMatchingLabels": "Keine passenden Labels",
|
"noMatchingLabels": "Keine passenden Labels",
|
||||||
"noLabels": "Keine Labels"
|
"noLabels": "Keine Labels",
|
||||||
|
"CHANGE_STATUS": "Status ändern",
|
||||||
|
"CHANGE_PRIORITY": "Priorität ändern",
|
||||||
|
"CHANGE_PHASE": "Phase ändern",
|
||||||
|
"ADD_LABELS": "Labels hinzufügen",
|
||||||
|
"ASSIGN_TO_ME": "Mir zuweisen",
|
||||||
|
"ASSIGN_MEMBERS": "Mitglieder zuweisen",
|
||||||
|
"ARCHIVE": "Archivieren",
|
||||||
|
"DELETE": "Löschen",
|
||||||
|
"CANCEL": "Abbrechen",
|
||||||
|
"CLEAR_SELECTION": "Auswahl löschen",
|
||||||
|
"TASKS_SELECTED": "{{count}} Aufgabe ausgewählt",
|
||||||
|
"TASKS_SELECTED_plural": "{{count}} Aufgaben ausgewählt",
|
||||||
|
"DELETE_TASKS_CONFIRM": "{{count}} Aufgabe löschen?",
|
||||||
|
"DELETE_TASKS_CONFIRM_plural": "{{count}} Aufgaben löschen?",
|
||||||
|
"DELETE_TASKS_WARNING": "Diese Aktion kann nicht rückgängig gemacht werden."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,20 @@
|
|||||||
"hitEnterToCreate": "Press Enter to create",
|
"hitEnterToCreate": "Press Enter to create",
|
||||||
"pendingInvitation": "Pending Invitation",
|
"pendingInvitation": "Pending Invitation",
|
||||||
"noMatchingLabels": "No matching labels",
|
"noMatchingLabels": "No matching labels",
|
||||||
"noLabels": "No labels"
|
"noLabels": "No labels",
|
||||||
|
"CHANGE_STATUS": "Change Status",
|
||||||
|
"CHANGE_PRIORITY": "Change Priority",
|
||||||
|
"CHANGE_PHASE": "Change Phase",
|
||||||
|
"ADD_LABELS": "Add Labels",
|
||||||
|
"ASSIGN_TO_ME": "Assign to Me",
|
||||||
|
"ASSIGN_MEMBERS": "Assign Members",
|
||||||
|
"ARCHIVE": "Archive",
|
||||||
|
"DELETE": "Delete",
|
||||||
|
"CANCEL": "Cancel",
|
||||||
|
"CLEAR_SELECTION": "Clear Selection",
|
||||||
|
"TASKS_SELECTED": "{{count}} task selected",
|
||||||
|
"TASKS_SELECTED_plural": "{{count}} tasks selected",
|
||||||
|
"DELETE_TASKS_CONFIRM": "Delete {{count}} task?",
|
||||||
|
"DELETE_TASKS_CONFIRM_plural": "Delete {{count}} tasks?",
|
||||||
|
"DELETE_TASKS_WARNING": "This action cannot be undone."
|
||||||
}
|
}
|
||||||
@@ -20,5 +20,20 @@
|
|||||||
"hitEnterToCreate": "Presione Enter para crear",
|
"hitEnterToCreate": "Presione Enter para crear",
|
||||||
"pendingInvitation": "Invitación Pendiente",
|
"pendingInvitation": "Invitación Pendiente",
|
||||||
"noMatchingLabels": "No hay etiquetas coincidentes",
|
"noMatchingLabels": "No hay etiquetas coincidentes",
|
||||||
"noLabels": "Sin etiquetas"
|
"noLabels": "Sin etiquetas",
|
||||||
|
"CHANGE_STATUS": "Cambiar Estado",
|
||||||
|
"CHANGE_PRIORITY": "Cambiar Prioridad",
|
||||||
|
"CHANGE_PHASE": "Cambiar Fase",
|
||||||
|
"ADD_LABELS": "Agregar Etiquetas",
|
||||||
|
"ASSIGN_TO_ME": "Asignar a Mí",
|
||||||
|
"ASSIGN_MEMBERS": "Asignar Miembros",
|
||||||
|
"ARCHIVE": "Archivar",
|
||||||
|
"DELETE": "Eliminar",
|
||||||
|
"CANCEL": "Cancelar",
|
||||||
|
"CLEAR_SELECTION": "Limpiar Selección",
|
||||||
|
"TASKS_SELECTED": "{{count}} tarea seleccionada",
|
||||||
|
"TASKS_SELECTED_plural": "{{count}} tareas seleccionadas",
|
||||||
|
"DELETE_TASKS_CONFIRM": "¿Eliminar {{count}} tarea?",
|
||||||
|
"DELETE_TASKS_CONFIRM_plural": "¿Eliminar {{count}} tareas?",
|
||||||
|
"DELETE_TASKS_WARNING": "Esta acción no se puede deshacer."
|
||||||
}
|
}
|
||||||
@@ -20,5 +20,20 @@
|
|||||||
"hitEnterToCreate": "Pressione Enter para criar",
|
"hitEnterToCreate": "Pressione Enter para criar",
|
||||||
"pendingInvitation": "Convite Pendente",
|
"pendingInvitation": "Convite Pendente",
|
||||||
"noMatchingLabels": "Nenhuma etiqueta correspondente",
|
"noMatchingLabels": "Nenhuma etiqueta correspondente",
|
||||||
"noLabels": "Sem etiquetas"
|
"noLabels": "Sem etiquetas",
|
||||||
|
"CHANGE_STATUS": "Alterar Status",
|
||||||
|
"CHANGE_PRIORITY": "Alterar Prioridade",
|
||||||
|
"CHANGE_PHASE": "Alterar Fase",
|
||||||
|
"ADD_LABELS": "Adicionar Etiquetas",
|
||||||
|
"ASSIGN_TO_ME": "Atribuir a Mim",
|
||||||
|
"ASSIGN_MEMBERS": "Atribuir Membros",
|
||||||
|
"ARCHIVE": "Arquivar",
|
||||||
|
"DELETE": "Deletar",
|
||||||
|
"CANCEL": "Cancelar",
|
||||||
|
"CLEAR_SELECTION": "Limpar Seleção",
|
||||||
|
"TASKS_SELECTED": "{{count}} tarefa selecionada",
|
||||||
|
"TASKS_SELECTED_plural": "{{count}} tarefas selecionadas",
|
||||||
|
"DELETE_TASKS_CONFIRM": "Deletar {{count}} tarefa?",
|
||||||
|
"DELETE_TASKS_CONFIRM_plural": "Deletar {{count}} tarefas?",
|
||||||
|
"DELETE_TASKS_WARNING": "Esta ação não pode ser desfeita."
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { RootState } from '@/app/store';
|
import { RootState } from '@/app/store';
|
||||||
|
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
@@ -143,9 +144,14 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
onBulkExport,
|
onBulkExport,
|
||||||
onBulkSetDueDate,
|
onBulkSetDueDate,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('task-management');
|
const { t } = useTranslation('tasks/task-table-bulk-actions');
|
||||||
const isDarkMode = useSelector((state: RootState) => state.themeReducer?.mode === 'dark');
|
const isDarkMode = useSelector((state: RootState) => state.themeReducer?.mode === 'dark');
|
||||||
|
|
||||||
|
// Get data from Redux store
|
||||||
|
const statusList = useAppSelector(state => state.taskStatusReducer.status);
|
||||||
|
const priorityList = useAppSelector(state => state.priorityReducer.priorities);
|
||||||
|
const phaseList = useAppSelector(state => state.phaseReducer.phaseList);
|
||||||
|
|
||||||
// Performance state management
|
// Performance state management
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [loadingStates, setLoadingStates] = useState({
|
const [loadingStates, setLoadingStates] = useState({
|
||||||
@@ -178,6 +184,41 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
setLoadingStates(prev => ({ ...prev, [action]: loading }));
|
setLoadingStates(prev => ({ ...prev, [action]: loading }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Create dropdown menus
|
||||||
|
const statusMenuItems = useMemo(() =>
|
||||||
|
statusList.map(status => ({
|
||||||
|
key: status.id || '',
|
||||||
|
label: <Badge color={status.color_code} text={status.name} />,
|
||||||
|
})), [statusList]
|
||||||
|
);
|
||||||
|
|
||||||
|
const priorityMenuItems = useMemo(() =>
|
||||||
|
priorityList.map(priority => ({
|
||||||
|
key: priority.id || '',
|
||||||
|
label: <Badge color={priority.color_code} text={priority.name} />,
|
||||||
|
})), [priorityList]
|
||||||
|
);
|
||||||
|
|
||||||
|
const phaseMenuItems = useMemo(() =>
|
||||||
|
phaseList.map(phase => ({
|
||||||
|
key: phase.id || '',
|
||||||
|
label: <Badge color={phase.color_code} text={phase.name} />,
|
||||||
|
})), [phaseList]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Menu click handlers
|
||||||
|
const handleStatusMenuClick = useCallback((e: any) => {
|
||||||
|
onBulkStatusChange?.(e.key);
|
||||||
|
}, [onBulkStatusChange]);
|
||||||
|
|
||||||
|
const handlePriorityMenuClick = useCallback((e: any) => {
|
||||||
|
onBulkPriorityChange?.(e.key);
|
||||||
|
}, [onBulkPriorityChange]);
|
||||||
|
|
||||||
|
const handlePhaseMenuClick = useCallback((e: any) => {
|
||||||
|
onBulkPhaseChange?.(e.key);
|
||||||
|
}, [onBulkPhaseChange]);
|
||||||
|
|
||||||
// Memoized handlers with loading states
|
// Memoized handlers with loading states
|
||||||
const handleStatusChange = useCallback(async () => {
|
const handleStatusChange = useCallback(async () => {
|
||||||
updateLoadingState('status', true);
|
updateLoadingState('status', true);
|
||||||
@@ -289,54 +330,7 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
whiteSpace: 'nowrap' as const,
|
whiteSpace: 'nowrap' as const,
|
||||||
}), [isDarkMode]);
|
}), [isDarkMode]);
|
||||||
|
|
||||||
// Quick actions dropdown menu
|
if (!totalSelected || Number(totalSelected) < 1) {
|
||||||
const quickActionsMenu = useMemo(() => ({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
key: 'change-status',
|
|
||||||
label: 'Change Status',
|
|
||||||
icon: <RetweetOutlined />,
|
|
||||||
onClick: handleStatusChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'change-priority',
|
|
||||||
label: 'Change Priority',
|
|
||||||
icon: <FlagOutlined />,
|
|
||||||
onClick: handlePriorityChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'change-phase',
|
|
||||||
label: 'Change Phase',
|
|
||||||
icon: <RetweetOutlined />,
|
|
||||||
onClick: handlePhaseChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'set-due-date',
|
|
||||||
label: 'Set Due Date',
|
|
||||||
icon: <CalendarOutlined />,
|
|
||||||
onClick: () => onBulkSetDueDate?.(new Date().toISOString()),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider' as const,
|
|
||||||
key: 'divider-1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'duplicate',
|
|
||||||
label: 'Duplicate Tasks',
|
|
||||||
icon: <CopyOutlined />,
|
|
||||||
onClick: handleDuplicate,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'export',
|
|
||||||
label: 'Export Tasks',
|
|
||||||
icon: <ExportOutlined />,
|
|
||||||
onClick: handleExport,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}), [handleStatusChange, handlePriorityChange, handlePhaseChange, handleDuplicate, handleExport, onBulkSetDueDate]);
|
|
||||||
|
|
||||||
// Don't render if no tasks selected
|
|
||||||
if (totalSelected === 0) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,7 +350,7 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
marginRight: '6px'
|
marginRight: '6px'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{totalSelected} {totalSelected === 1 ? 'task' : 'tasks'} selected
|
{t('TASKS_SELECTED', { count: totalSelected })}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Divider
|
<Divider
|
||||||
@@ -370,10 +364,13 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
|
|
||||||
{/* Actions in same order as original component */}
|
{/* Actions in same order as original component */}
|
||||||
<Space size={2}>
|
<Space size={2}>
|
||||||
{/* Change Status/Priority/Phase */}
|
{/* Change Status */}
|
||||||
<Tooltip title="Change Status/Priority/Phase" placement="top">
|
<Tooltip title={t('CHANGE_STATUS')} placement="top">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={quickActionsMenu}
|
menu={{
|
||||||
|
items: statusMenuItems,
|
||||||
|
onClick: handleStatusMenuClick
|
||||||
|
}}
|
||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
placement="top"
|
placement="top"
|
||||||
arrow
|
arrow
|
||||||
@@ -396,7 +393,75 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
}}
|
}}
|
||||||
size="small"
|
size="small"
|
||||||
type="text"
|
type="text"
|
||||||
loading={loadingStates.status || loadingStates.priority || loadingStates.phase}
|
loading={loadingStates.status}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{/* Change Priority */}
|
||||||
|
<Tooltip title={t('CHANGE_PRIORITY')} placement="top">
|
||||||
|
<Dropdown
|
||||||
|
menu={{
|
||||||
|
items: priorityMenuItems,
|
||||||
|
onClick: handlePriorityMenuClick
|
||||||
|
}}
|
||||||
|
trigger={['click']}
|
||||||
|
placement="top"
|
||||||
|
arrow
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={<FlagOutlined />}
|
||||||
|
style={{
|
||||||
|
background: 'transparent',
|
||||||
|
color: isDarkMode ? '#e5e7eb' : '#374151',
|
||||||
|
border: 'none',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '6px',
|
||||||
|
height: '32px',
|
||||||
|
width: '32px',
|
||||||
|
fontSize: '14px',
|
||||||
|
borderRadius: '6px',
|
||||||
|
transition: 'all 0.15s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
loading={loadingStates.priority}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{/* Change Phase */}
|
||||||
|
<Tooltip title={t('CHANGE_PHASE')} placement="top">
|
||||||
|
<Dropdown
|
||||||
|
menu={{
|
||||||
|
items: phaseMenuItems,
|
||||||
|
onClick: handlePhaseMenuClick
|
||||||
|
}}
|
||||||
|
trigger={['click']}
|
||||||
|
placement="top"
|
||||||
|
arrow
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={<BulbOutlined />}
|
||||||
|
style={{
|
||||||
|
background: 'transparent',
|
||||||
|
color: isDarkMode ? '#e5e7eb' : '#374151',
|
||||||
|
border: 'none',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '6px',
|
||||||
|
height: '32px',
|
||||||
|
width: '32px',
|
||||||
|
fontSize: '14px',
|
||||||
|
borderRadius: '6px',
|
||||||
|
transition: 'all 0.15s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
loading={loadingStates.phase}
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -404,7 +469,7 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
{/* Change Labels */}
|
{/* Change Labels */}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<TagsOutlined />}
|
icon={<TagsOutlined />}
|
||||||
tooltip="Add Labels"
|
tooltip={t('ADD_LABELS')}
|
||||||
onClick={() => onBulkAddLabels?.([])}
|
onClick={() => onBulkAddLabels?.([])}
|
||||||
loading={loadingStates.labels}
|
loading={loadingStates.labels}
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
@@ -413,7 +478,7 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
{/* Assign to Me */}
|
{/* Assign to Me */}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<UserAddOutlined />}
|
icon={<UserAddOutlined />}
|
||||||
tooltip="Assign to Me"
|
tooltip={t('ASSIGN_TO_ME')}
|
||||||
onClick={handleAssignToMe}
|
onClick={handleAssignToMe}
|
||||||
loading={loadingStates.assignToMe}
|
loading={loadingStates.assignToMe}
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
@@ -422,7 +487,7 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
{/* Change Assignees */}
|
{/* Change Assignees */}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<UsergroupAddOutlined />}
|
icon={<UsergroupAddOutlined />}
|
||||||
tooltip="Assign Members"
|
tooltip={t('ASSIGN_MEMBERS')}
|
||||||
onClick={() => onBulkAssignMembers?.([])}
|
onClick={() => onBulkAssignMembers?.([])}
|
||||||
loading={loadingStates.assignMembers}
|
loading={loadingStates.assignMembers}
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
@@ -431,7 +496,7 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
{/* Archive */}
|
{/* Archive */}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<InboxOutlined />}
|
icon={<InboxOutlined />}
|
||||||
tooltip="Archive"
|
tooltip={t('ARCHIVE')}
|
||||||
onClick={handleArchive}
|
onClick={handleArchive}
|
||||||
loading={loadingStates.archive}
|
loading={loadingStates.archive}
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
@@ -439,17 +504,17 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
|
|
||||||
{/* Delete */}
|
{/* Delete */}
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={`Delete ${totalSelected} ${totalSelected === 1 ? 'task' : 'tasks'}?`}
|
title={t('DELETE_TASKS_CONFIRM', { count: totalSelected })}
|
||||||
description="This action cannot be undone."
|
description={t('DELETE_TASKS_WARNING')}
|
||||||
onConfirm={handleDelete}
|
onConfirm={handleDelete}
|
||||||
okText="Delete"
|
okText={t('DELETE')}
|
||||||
cancelText="Cancel"
|
cancelText={t('CANCEL')}
|
||||||
okType="danger"
|
okType="danger"
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
tooltip="Delete"
|
tooltip={t('DELETE')}
|
||||||
loading={loadingStates.delete}
|
loading={loadingStates.delete}
|
||||||
danger
|
danger
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
@@ -468,7 +533,7 @@ const OptimizedBulkActionBarContent: React.FC<OptimizedBulkActionBarProps> = Rea
|
|||||||
{/* Clear Selection */}
|
{/* Clear Selection */}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<CloseOutlined />}
|
icon={<CloseOutlined />}
|
||||||
tooltip="Clear Selection"
|
tooltip={t('CLEAR_SELECTION')}
|
||||||
onClick={onClearSelection}
|
onClick={onClearSelection}
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
/>
|
/>
|
||||||
@@ -481,8 +546,8 @@ OptimizedBulkActionBarContent.displayName = 'OptimizedBulkActionBarContent';
|
|||||||
|
|
||||||
// Portal wrapper for performance isolation
|
// Portal wrapper for performance isolation
|
||||||
const OptimizedBulkActionBar: React.FC<OptimizedBulkActionBarProps> = React.memo((props) => {
|
const OptimizedBulkActionBar: React.FC<OptimizedBulkActionBarProps> = React.memo((props) => {
|
||||||
// Only render portal if tasks are selected for better performance
|
console.log('BulkActionBar totalSelected:', props.totalSelected, typeof props.totalSelected);
|
||||||
if (props.totalSelected === 0) {
|
if (!props.totalSelected || Number(props.totalSelected) < 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
|
|||||||
import TaskRow from './task-row';
|
import TaskRow from './task-row';
|
||||||
// import BulkActionBar from './bulk-action-bar';
|
// import BulkActionBar from './bulk-action-bar';
|
||||||
import OptimizedBulkActionBar from './optimized-bulk-action-bar';
|
import OptimizedBulkActionBar from './optimized-bulk-action-bar';
|
||||||
|
// import OptimizedBulkActionBar from './optimized-bulk-action-bar';
|
||||||
import VirtualizedTaskList from './virtualized-task-list';
|
import VirtualizedTaskList from './virtualized-task-list';
|
||||||
import { AppDispatch } from '@/app/store';
|
import { AppDispatch } from '@/app/store';
|
||||||
import { shallowEqual } from 'react-redux';
|
import { shallowEqual } from 'react-redux';
|
||||||
@@ -443,6 +444,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
// Bulk action handlers - implementing real functionality from task-list-bulk-actions-bar
|
// Bulk action handlers - implementing real functionality from task-list-bulk-actions-bar
|
||||||
const handleClearSelection = useCallback(() => {
|
const handleClearSelection = useCallback(() => {
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const handleBulkStatusChange = useCallback(async (statusId: string) => {
|
const handleBulkStatusChange = useCallback(async (statusId: string) => {
|
||||||
@@ -480,6 +482,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_change_status);
|
trackMixpanelEvent(evt_project_task_list_bulk_change_status);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -501,6 +504,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_change_priority);
|
trackMixpanelEvent(evt_project_task_list_bulk_change_priority);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -522,6 +526,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_change_phase);
|
trackMixpanelEvent(evt_project_task_list_bulk_change_phase);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -540,6 +545,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_assign_me);
|
trackMixpanelEvent(evt_project_task_list_bulk_assign_me);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -571,6 +577,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_assign_members);
|
trackMixpanelEvent(evt_project_task_list_bulk_assign_members);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -595,6 +602,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_update_labels);
|
trackMixpanelEvent(evt_project_task_list_bulk_update_labels);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
dispatch(fetchLabels());
|
dispatch(fetchLabels());
|
||||||
}
|
}
|
||||||
@@ -614,6 +622,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_archive);
|
trackMixpanelEvent(evt_project_task_list_bulk_archive);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -632,6 +641,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
|||||||
if (res.done) {
|
if (res.done) {
|
||||||
trackMixpanelEvent(evt_project_task_list_bulk_delete);
|
trackMixpanelEvent(evt_project_task_list_bulk_delete);
|
||||||
dispatch(deselectAll());
|
dispatch(deselectAll());
|
||||||
|
dispatch(clearSelection());
|
||||||
dispatch(fetchTasksV3(projectId));
|
dispatch(fetchTasksV3(projectId));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user