From ccde08b7007c9c80f1076765d06d593637826df6 Mon Sep 17 00:00:00 2001 From: chamiakJ Date: Wed, 2 Jul 2025 07:59:34 +0530 Subject: [PATCH 1/2] 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. --- .../de/tasks/task-table-bulk-actions.json | 17 +- .../en/tasks/task-table-bulk-actions.json | 17 +- .../es/tasks/task-table-bulk-actions.json | 17 +- .../pt/tasks/task-table-bulk-actions.json | 17 +- .../optimized-bulk-action-bar.tsx | 197 ++++++++++++------ .../task-management/task-list-board.tsx | 10 + 6 files changed, 205 insertions(+), 70 deletions(-) diff --git a/worklenz-frontend/public/locales/de/tasks/task-table-bulk-actions.json b/worklenz-frontend/public/locales/de/tasks/task-table-bulk-actions.json index 3c2d7132..297987c5 100644 --- a/worklenz-frontend/public/locales/de/tasks/task-table-bulk-actions.json +++ b/worklenz-frontend/public/locales/de/tasks/task-table-bulk-actions.json @@ -20,5 +20,20 @@ "hitEnterToCreate": "Enter drücken zum Erstellen", "pendingInvitation": "Einladung ausstehend", "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." } diff --git a/worklenz-frontend/public/locales/en/tasks/task-table-bulk-actions.json b/worklenz-frontend/public/locales/en/tasks/task-table-bulk-actions.json index 2434aea4..4beab4f6 100644 --- a/worklenz-frontend/public/locales/en/tasks/task-table-bulk-actions.json +++ b/worklenz-frontend/public/locales/en/tasks/task-table-bulk-actions.json @@ -20,5 +20,20 @@ "hitEnterToCreate": "Press Enter to create", "pendingInvitation": "Pending Invitation", "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." } \ No newline at end of file diff --git a/worklenz-frontend/public/locales/es/tasks/task-table-bulk-actions.json b/worklenz-frontend/public/locales/es/tasks/task-table-bulk-actions.json index 0040c407..94963c91 100644 --- a/worklenz-frontend/public/locales/es/tasks/task-table-bulk-actions.json +++ b/worklenz-frontend/public/locales/es/tasks/task-table-bulk-actions.json @@ -20,5 +20,20 @@ "hitEnterToCreate": "Presione Enter para crear", "pendingInvitation": "Invitación Pendiente", "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." } \ No newline at end of file diff --git a/worklenz-frontend/public/locales/pt/tasks/task-table-bulk-actions.json b/worklenz-frontend/public/locales/pt/tasks/task-table-bulk-actions.json index 40147210..6581803a 100644 --- a/worklenz-frontend/public/locales/pt/tasks/task-table-bulk-actions.json +++ b/worklenz-frontend/public/locales/pt/tasks/task-table-bulk-actions.json @@ -20,5 +20,20 @@ "hitEnterToCreate": "Pressione Enter para criar", "pendingInvitation": "Convite Pendente", "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." } \ No newline at end of file diff --git a/worklenz-frontend/src/components/task-management/optimized-bulk-action-bar.tsx b/worklenz-frontend/src/components/task-management/optimized-bulk-action-bar.tsx index fa14b4e1..fc052fcf 100644 --- a/worklenz-frontend/src/components/task-management/optimized-bulk-action-bar.tsx +++ b/worklenz-frontend/src/components/task-management/optimized-bulk-action-bar.tsx @@ -30,6 +30,7 @@ import { import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { RootState } from '@/app/store'; +import { useAppSelector } from '@/hooks/useAppSelector'; const { Text } = Typography; @@ -143,9 +144,14 @@ const OptimizedBulkActionBarContent: React.FC = Rea onBulkExport, onBulkSetDueDate, }) => { - const { t } = useTranslation('task-management'); + const { t } = useTranslation('tasks/task-table-bulk-actions'); 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 const [isVisible, setIsVisible] = useState(false); const [loadingStates, setLoadingStates] = useState({ @@ -178,6 +184,41 @@ const OptimizedBulkActionBarContent: React.FC = Rea setLoadingStates(prev => ({ ...prev, [action]: loading })); }, []); + // Create dropdown menus + const statusMenuItems = useMemo(() => + statusList.map(status => ({ + key: status.id || '', + label: , + })), [statusList] + ); + + const priorityMenuItems = useMemo(() => + priorityList.map(priority => ({ + key: priority.id || '', + label: , + })), [priorityList] + ); + + const phaseMenuItems = useMemo(() => + phaseList.map(phase => ({ + key: phase.id || '', + label: , + })), [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 const handleStatusChange = useCallback(async () => { updateLoadingState('status', true); @@ -289,54 +330,7 @@ const OptimizedBulkActionBarContent: React.FC = Rea whiteSpace: 'nowrap' as const, }), [isDarkMode]); - // Quick actions dropdown menu - const quickActionsMenu = useMemo(() => ({ - items: [ - { - key: 'change-status', - label: 'Change Status', - icon: , - onClick: handleStatusChange, - }, - { - key: 'change-priority', - label: 'Change Priority', - icon: , - onClick: handlePriorityChange, - }, - { - key: 'change-phase', - label: 'Change Phase', - icon: , - onClick: handlePhaseChange, - }, - { - key: 'set-due-date', - label: 'Set Due Date', - icon: , - onClick: () => onBulkSetDueDate?.(new Date().toISOString()), - }, - { - type: 'divider' as const, - key: 'divider-1', - }, - { - key: 'duplicate', - label: 'Duplicate Tasks', - icon: , - onClick: handleDuplicate, - }, - { - key: 'export', - label: 'Export Tasks', - icon: , - onClick: handleExport, - }, - ], - }), [handleStatusChange, handlePriorityChange, handlePhaseChange, handleDuplicate, handleExport, onBulkSetDueDate]); - - // Don't render if no tasks selected - if (totalSelected === 0) { + if (!totalSelected || Number(totalSelected) < 1) { return null; } @@ -356,7 +350,7 @@ const OptimizedBulkActionBarContent: React.FC = Rea marginRight: '6px' }} /> - {totalSelected} {totalSelected === 1 ? 'task' : 'tasks'} selected +{t('TASKS_SELECTED', { count: totalSelected })} = Rea {/* Actions in same order as original component */} - {/* Change Status/Priority/Phase */} - + {/* Change Status */} + = Rea }} size="small" type="text" - loading={loadingStates.status || loadingStates.priority || loadingStates.phase} + loading={loadingStates.status} + /> + + + + {/* Change Priority */} + + +