= ({ task, isDarkMode = fals
>
{t('noLabelsFound')}
{searchQuery.trim() && (
-
+ <>
+
+
+ {t('labelsSelectorInputTip')}
+
+ >
)}
)}
diff --git a/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-labels/task-drawer-labels.tsx b/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-labels/task-drawer-labels.tsx
index 2b70d41fc..914abd787 100644
--- a/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-labels/task-drawer-labels.tsx
+++ b/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-labels/task-drawer-labels.tsx
@@ -31,6 +31,7 @@ import { setBoardLabels, updateBoardTaskLabel } from '@/features/board/board-sli
import { updateEnhancedKanbanTaskLabels } from '@/features/enhanced-kanban/enhanced-kanban.slice';
import { ILabelsChangeResponse } from '@/types/tasks/taskList.types';
import { ITaskLabelFilter } from '@/types/tasks/taskLabel.types';
+import { sortLabelsBySelection, isLabelSelected } from '@/utils/labelUtils';
interface TaskDrawerLabelsProps {
task: ITaskViewModel;
@@ -98,8 +99,13 @@ const TaskDrawerLabels = ({ task, t }: TaskDrawerLabelsProps) => {
// used useMemo hook for re render the list when searching
const filteredLabelData = useMemo(() => {
- return labelList.filter(label => label.name?.toLowerCase().includes(searchQuery.toLowerCase()));
- }, [labelList, searchQuery]);
+ const filtered = labelList.filter(label =>
+ label.name?.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ // Sort to show selected labels first using shared utility
+ return sortLabelsBySelection(filtered, task?.labels || []);
+ }, [labelList, searchQuery, task?.labels]);
const labelDropdownContent = (
{
>
existingLabel.id === label.id)
- : false
- }
+ checked={isLabelSelected(label.id || '', task?.labels)}
onChange={e => e.preventDefault()}
>
@@ -186,6 +188,11 @@ const TaskDrawerLabels = ({ task, t }: TaskDrawerLabelsProps) => {
{
+ e.preventDefault();
+ handleLabelChange(label);
+ }}
style={{
display: 'flex',
alignItems: 'center',
diff --git a/worklenz-frontend/src/components/task-list-v2/components/TaskRowColumns.tsx b/worklenz-frontend/src/components/task-list-v2/components/TaskRowColumns.tsx
index 3681140e5..84d3432b9 100644
--- a/worklenz-frontend/src/components/task-list-v2/components/TaskRowColumns.tsx
+++ b/worklenz-frontend/src/components/task-list-v2/components/TaskRowColumns.tsx
@@ -1,4 +1,4 @@
-import React, { memo } from 'react';
+ import React, { memo } from 'react';
import { CheckCircleOutlined, HolderOutlined } from '@/shared/antd-imports';
import { Checkbox } from '@/shared/antd-imports';
import { Task } from '@/types/task-management.types';
diff --git a/worklenz-frontend/src/components/taskListCommon/labelsSelector/LabelsSelector.tsx b/worklenz-frontend/src/components/taskListCommon/labelsSelector/LabelsSelector.tsx
index 5a711ca85..026aff04c 100644
--- a/worklenz-frontend/src/components/taskListCommon/labelsSelector/LabelsSelector.tsx
+++ b/worklenz-frontend/src/components/taskListCommon/labelsSelector/LabelsSelector.tsx
@@ -20,6 +20,7 @@ import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
import { useAuthService } from '@/hooks/useAuth';
import { SocketEvents } from '@/shared/socket-events';
import { useSocket } from '@/socket/socketContext';
+import { sortLabelsBySelection, isLabelSelected } from '@/utils/labelUtils';
interface LabelsSelectorProps {
task: IProjectTask;
@@ -67,8 +68,13 @@ const LabelsSelector = ({ task }: LabelsSelectorProps) => {
// used useMemo hook for re render the list when searching
const filteredLabelData = useMemo(() => {
- return labelList.filter(label => label.name?.toLowerCase().includes(searchQuery.toLowerCase()));
- }, [labelList, searchQuery]);
+ const filtered = labelList.filter(label =>
+ label.name?.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ // Sort to show selected labels first using shared utility
+ return sortLabelsBySelection(filtered, task?.labels || []);
+ }, [labelList, searchQuery, task?.labels]);
const labelDropdownContent = (
@@ -112,11 +118,7 @@ const LabelsSelector = ({ task }: LabelsSelectorProps) => {
>
existingLabel.id === label.id)
- : false
- }
+ checked={isLabelSelected(label.id || '', task?.labels)}
onChange={() => handleLabelChange(label)}
>
diff --git a/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts b/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts
index 72a1f71a5..462fd5915 100644
--- a/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts
+++ b/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts
@@ -180,9 +180,8 @@ export const useTaskSocketHandlers = () => {
await Promise.all([
dispatch(updateTaskLabel(labels)),
dispatch(setTaskLabels(labels)),
- // Remove unnecessary refetches - real-time updates handle this
- // dispatch(fetchLabels()),
- // projectId && dispatch(fetchLabelsByProject(projectId)),
+ // Fetch labels when a new label is created to update the global labels list
+ labels.is_new && dispatch(fetchLabels()),
]);
// Update enhanced kanban slice
diff --git a/worklenz-frontend/src/pages/settings/labels/LabelsSettings.tsx b/worklenz-frontend/src/pages/settings/labels/LabelsSettings.tsx
index 659a7d63e..6c73951bb 100644
--- a/worklenz-frontend/src/pages/settings/labels/LabelsSettings.tsx
+++ b/worklenz-frontend/src/pages/settings/labels/LabelsSettings.tsx
@@ -129,7 +129,12 @@ const LabelsSettings = () => {
onConfirm={() => deleteLabel(record.id!)}
>
- } size="small" />
+ }
+ size="small"
+ onClick={(e) => e.stopPropagation()}
+ />
diff --git a/worklenz-frontend/src/types/tasks/task.types.ts b/worklenz-frontend/src/types/tasks/task.types.ts
index 204d14189..6ae329e95 100644
--- a/worklenz-frontend/src/types/tasks/task.types.ts
+++ b/worklenz-frontend/src/types/tasks/task.types.ts
@@ -32,7 +32,7 @@ export interface ITask {
project_id: string;
team_id: string;
task_key: string;
- labels: string[];
+ labels: ITaskLabel[];
assignees: string[];
names: string[];
sub_tasks_count: number;
diff --git a/worklenz-frontend/src/utils/labelUtils.ts b/worklenz-frontend/src/utils/labelUtils.ts
new file mode 100644
index 000000000..5a1d50232
--- /dev/null
+++ b/worklenz-frontend/src/utils/labelUtils.ts
@@ -0,0 +1,35 @@
+import { ITaskLabel } from '@/types/tasks/taskLabel.types';
+
+/**
+ * Sorts labels to show selected labels first
+ * @param labels - All available labels
+ * @param selectedLabels - Currently selected labels
+ * @returns Sorted array with selected labels first
+ */
+export const sortLabelsBySelection = (
+ labels: ITaskLabel[],
+ selectedLabels: ITaskLabel[]
+): ITaskLabel[] => {
+ return [...labels].sort((a, b) => {
+ const aSelected = selectedLabels.some(label => label.id === a.id);
+ const bSelected = selectedLabels.some(label => label.id === b.id);
+
+ if (aSelected && !bSelected) return -1;
+ if (!aSelected && bSelected) return 1;
+ return 0;
+ });
+};
+
+/**
+ * Checks if a label is selected
+ * @param labelId - ID of the label to check
+ * @param selectedLabels - Currently selected labels
+ * @returns true if label is selected
+ */
+export const isLabelSelected = (
+ labelId: string,
+ selectedLabels?: ITaskLabel[]
+): boolean => {
+ if (!selectedLabels || selectedLabels.length === 0) return false;
+ return selectedLabels.some(label => label.id === labelId);
+};