Skip to content

Commit b061896

Browse files
committed
refactor: extract shared label sorting and selection logic to labelUtils
1 parent 6836c61 commit b061896

4 files changed

Lines changed: 57 additions & 43 deletions

File tree

worklenz-frontend/src/components/LabelsSelector.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useSocket } from '@/socket/socketContext';
1010
import { SocketEvents } from '@/shared/socket-events';
1111
import { useAuthService } from '@/hooks/useAuth';
1212
import { Button, Checkbox, Tag } from '@/components';
13+
import { sortLabelsBySelection, isLabelSelected } from '@/utils/labelUtils';
1314

1415
interface LabelsSelectorProps {
1516
task: IProjectTask;
@@ -33,16 +34,9 @@ const LabelsSelector: React.FC<LabelsSelectorProps> = ({ task, isDarkMode = fals
3334
const filtered = (labels as ITaskLabel[])?.filter(label =>
3435
label.name?.toLowerCase().includes(searchQuery.toLowerCase())
3536
) || [];
36-
37-
// Sort labels: selected ones first, then unselected ones
38-
return filtered.sort((a, b) => {
39-
const aSelected = task?.all_labels?.some(existingLabel => existingLabel.id === a.id) || false;
40-
const bSelected = task?.all_labels?.some(existingLabel => existingLabel.id === b.id) || false;
41-
42-
if (aSelected && !bSelected) return -1;
43-
if (!aSelected && bSelected) return 1;
44-
return 0;
45-
});
37+
38+
// Sort to show selected labels first using shared utility
39+
return sortLabelsBySelection(filtered, task?.labels || []);
4640
}, [labels, searchQuery, task?.labels]);
4741

4842
// Update dropdown position
@@ -157,7 +151,8 @@ const LabelsSelector: React.FC<LabelsSelectorProps> = ({ task, isDarkMode = fals
157151
};
158152

159153
const checkLabelSelected = (labelId: string) => {
160-
return task?.all_labels?.some(existingLabel => existingLabel.id === labelId) || false;
154+
// Use task.labels (currently selected labels) instead of all_labels
155+
return isLabelSelected(labelId, task?.labels);
161156
};
162157

163158
const handleKeyDown = (e: React.KeyboardEvent) => {

worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-labels/task-drawer-labels.tsx

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { setBoardLabels, updateBoardTaskLabel } from '@/features/board/board-sli
3131
import { updateEnhancedKanbanTaskLabels } from '@/features/enhanced-kanban/enhanced-kanban.slice';
3232
import { ILabelsChangeResponse } from '@/types/tasks/taskList.types';
3333
import { ITaskLabelFilter } from '@/types/tasks/taskLabel.types';
34+
import { sortLabelsBySelection, isLabelSelected } from '@/utils/labelUtils';
3435

3536
interface TaskDrawerLabelsProps {
3637
task: ITaskViewModel;
@@ -98,17 +99,12 @@ const TaskDrawerLabels = ({ task, t }: TaskDrawerLabelsProps) => {
9899

99100
// used useMemo hook for re render the list when searching
100101
const filteredLabelData = useMemo(() => {
101-
const filtered = labelList.filter(label => label.name?.toLowerCase().includes(searchQuery.toLowerCase()));
102-
103-
// Sort labels: selected ones first, then unselected ones
104-
return filtered.sort((a, b) => {
105-
const aSelected = task?.labels?.some(existingLabel => existingLabel.id === a.id) || false;
106-
const bSelected = task?.labels?.some(existingLabel => existingLabel.id === b.id) || false;
107-
108-
if (aSelected && !bSelected) return -1;
109-
if (!aSelected && bSelected) return 1;
110-
return 0;
111-
});
102+
const filtered = labelList.filter(label =>
103+
label.name?.toLowerCase().includes(searchQuery.toLowerCase())
104+
);
105+
106+
// Sort to show selected labels first using shared utility
107+
return sortLabelsBySelection(filtered, task?.labels || []);
112108
}, [labelList, searchQuery, task?.labels]);
113109

114110
const labelDropdownContent = (
@@ -153,11 +149,7 @@ const TaskDrawerLabels = ({ task, t }: TaskDrawerLabelsProps) => {
153149
>
154150
<Checkbox
155151
id={label.id}
156-
checked={
157-
task?.labels
158-
? task?.labels.some(existingLabel => existingLabel.id === label.id)
159-
: false
160-
}
152+
checked={isLabelSelected(label.id || '', task?.labels)}
161153
onChange={e => e.preventDefault()}
162154
>
163155
<Flex gap={8}>

worklenz-frontend/src/components/taskListCommon/labelsSelector/LabelsSelector.tsx

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
2020
import { useAuthService } from '@/hooks/useAuth';
2121
import { SocketEvents } from '@/shared/socket-events';
2222
import { useSocket } from '@/socket/socketContext';
23+
import { sortLabelsBySelection, isLabelSelected } from '@/utils/labelUtils';
2324

2425
interface LabelsSelectorProps {
2526
task: IProjectTask;
@@ -67,17 +68,12 @@ const LabelsSelector = ({ task }: LabelsSelectorProps) => {
6768

6869
// used useMemo hook for re render the list when searching
6970
const filteredLabelData = useMemo(() => {
70-
const filtered = labelList.filter(label => label.name?.toLowerCase().includes(searchQuery.toLowerCase()));
71-
72-
// Sort labels: selected ones first, then unselected ones
73-
return filtered.sort((a, b) => {
74-
const aSelected = task?.labels?.some(existingLabel => existingLabel.id === a.id) || false;
75-
const bSelected = task?.labels?.some(existingLabel => existingLabel.id === b.id) || false;
76-
77-
if (aSelected && !bSelected) return -1;
78-
if (!aSelected && bSelected) return 1;
79-
return 0;
80-
});
71+
const filtered = labelList.filter(label =>
72+
label.name?.toLowerCase().includes(searchQuery.toLowerCase())
73+
);
74+
75+
// Sort to show selected labels first using shared utility
76+
return sortLabelsBySelection(filtered, task?.labels || []);
8177
}, [labelList, searchQuery, task?.labels]);
8278

8379
const labelDropdownContent = (
@@ -122,11 +118,7 @@ const LabelsSelector = ({ task }: LabelsSelectorProps) => {
122118
>
123119
<Checkbox
124120
id={label.id}
125-
checked={
126-
task?.labels
127-
? task?.labels.some(existingLabel => existingLabel.id === label.id)
128-
: false
129-
}
121+
checked={isLabelSelected(label.id || '', task?.labels)}
130122
onChange={() => handleLabelChange(label)}
131123
>
132124
<Flex gap={8}>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { ITaskLabel } from '@/types/tasks/taskLabel.types';
2+
3+
/**
4+
* Sorts labels to show selected labels first
5+
* @param labels - All available labels
6+
* @param selectedLabels - Currently selected labels
7+
* @returns Sorted array with selected labels first
8+
*/
9+
export const sortLabelsBySelection = (
10+
labels: ITaskLabel[],
11+
selectedLabels: ITaskLabel[]
12+
): ITaskLabel[] => {
13+
return [...labels].sort((a, b) => {
14+
const aSelected = selectedLabels.some(label => label.id === a.id);
15+
const bSelected = selectedLabels.some(label => label.id === b.id);
16+
17+
if (aSelected && !bSelected) return -1;
18+
if (!aSelected && bSelected) return 1;
19+
return 0;
20+
});
21+
};
22+
23+
/**
24+
* Checks if a label is selected
25+
* @param labelId - ID of the label to check
26+
* @param selectedLabels - Currently selected labels
27+
* @returns true if label is selected
28+
*/
29+
export const isLabelSelected = (
30+
labelId: string,
31+
selectedLabels?: ITaskLabel[]
32+
): boolean => {
33+
if (!selectedLabels || selectedLabels.length === 0) return false;
34+
return selectedLabels.some(label => label.id === labelId);
35+
};

0 commit comments

Comments
 (0)