Skip to content

Commit 00a01ef

Browse files
committed
feat(task): improve relation task search results
1 parent 95ecd3a commit 00a01ef

2 files changed

Lines changed: 88 additions & 2 deletions

File tree

frontend/src/components/tasks/partials/RelatedTasks.vue

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
{{ task.differentProject }} >
6969
</span>
7070
</span>
71+
<span class="task-identifier">{{ getTaskIdentifier(task) }}</span>
7172
{{ task.title }}
7273
</span>
7374
<span
@@ -145,6 +146,7 @@
145146
{{ task.differentProject }} >
146147
</span>
147148
</span>
149+
<span class="task-identifier">{{ getTaskIdentifier(task) }}</span>
148150
{{ task.title }}
149151
</RouterLink>
150152
</div>
@@ -194,7 +196,7 @@ import {useI18n} from 'vue-i18n'
194196
import {useRoute} from 'vue-router'
195197
196198
import TaskService from '@/services/task'
197-
import TaskModel from '@/models/task'
199+
import TaskModel, {getTaskIdentifier} from '@/models/task'
198200
import type {ITask} from '@/modelTypes/ITask'
199201
import type {ITaskRelation} from '@/modelTypes/ITaskRelation'
200202
import {RELATION_KINDS, type IRelationKind} from '@/types/IRelationKind'
@@ -281,6 +283,23 @@ function mapRelatedTasks(tasks: ITask[]) {
281283
})
282284
}
283285
286+
function sortTasksForRelationSearch(tasks: ITask[]) {
287+
return [...tasks].sort((a, b) => {
288+
if (a.done !== b.done) {
289+
return a.done ? 1 : -1
290+
}
291+
292+
const aIsCurrentProject = a.projectId === props.projectId
293+
const bIsCurrentProject = b.projectId === props.projectId
294+
295+
if (aIsCurrentProject === bIsCurrentProject) {
296+
return 0
297+
}
298+
299+
return aIsCurrentProject ? -1 : 1
300+
})
301+
}
302+
284303
const mapRelationKindsTitleGetter = computed(() => ({
285304
'subtask': (count: number) => t('task.relation.kinds.subtask', count),
286305
'parenttask': (count: number) => t('task.relation.kinds.parenttask', count),
@@ -302,7 +321,7 @@ const mappedRelatedTasks = computed(() => Object.entries(relatedTasks.value).map
302321
kind: kind as IRelationKind,
303322
}),
304323
))
305-
const mappedFoundTasks = computed(() => mapRelatedTasks(foundTasks.value.filter(t => t.id !== props.taskId)))
324+
const mappedFoundTasks = computed(() => mapRelatedTasks(sortTasksForRelationSearch(foundTasks.value.filter(t => t.id !== props.taskId))))
306325
307326
const taskRelationService = shallowReactive(new TaskRelationService())
308327
const saved = ref(false)
@@ -414,6 +433,11 @@ async function toggleTaskDone(task: ITask) {
414433
inline-size: auto;
415434
}
416435
436+
.task-identifier {
437+
color: var(--grey-500);
438+
margin-inline-end: .35rem;
439+
}
440+
417441
.title {
418442
font-size: 1rem;
419443
margin: 0;

frontend/tests/e2e/task/related-tasks-quick-add-magic.spec.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ async function openRelatedTasksForm(page) {
1212
return input
1313
}
1414

15+
function relationSearchResults(page) {
16+
return page.locator('.task-relations .multiselect .search-results .search-result-button')
17+
}
18+
19+
function relationTaskSearchResults(page) {
20+
return page.locator('.task-relations .multiselect .search-results .search-result-button:not(.is-create-option)')
21+
}
22+
1523
test.describe('Related tasks quick add magic', () => {
1624
test('Applies a label parsed via *prefix to the new related task', async ({authenticatedPage: page}) => {
1725
const project = (await ProjectFactory.create(1, {id: 1, title: 'Project A'}))[0]
@@ -72,6 +80,60 @@ test.describe('Related tasks quick add magic', () => {
7280
await expect(relatedTaskRow.locator('.different-project')).toContainText('TargetProject')
7381
})
7482

83+
test('Shows task identifiers in relation search results', async ({authenticatedPage: page}) => {
84+
const project = (await ProjectFactory.create(1, {id: 1, title: 'Project A', identifier: 'PA'}))[0]
85+
await createDefaultViews(project.id)
86+
const parent = (await TaskFactory.create(1, {id: 10, title: 'Parent task', project_id: project.id}, false))[0]
87+
await TaskFactory.create(1, {
88+
id: 11,
89+
title: 'Identifier search candidate',
90+
project_id: project.id,
91+
index: 42,
92+
}, false)
93+
94+
await page.goto(`/tasks/${parent.id}`)
95+
const input = await openRelatedTasksForm(page)
96+
await input.pressSequentially('Identifier search candidate')
97+
98+
const firstResult = relationTaskSearchResults(page).filter({hasText: 'Identifier search candidate'}).first()
99+
await expect(firstResult).toBeVisible({timeout: 10000})
100+
await expect(firstResult).toContainText('PA-42')
101+
})
102+
103+
test('Prioritizes tasks from the current project in relation search results', async ({authenticatedPage: page}) => {
104+
const currentProject = (await ProjectFactory.create(1, {id: 1, title: 'Current Project'}))[0]
105+
await createDefaultViews(currentProject.id)
106+
const otherProject = (await ProjectFactory.create(1, {id: 2, title: 'Other Project'}, false))[0]
107+
await createDefaultViews(otherProject.id, 5)
108+
109+
const currentProjectParent = (await TaskFactory.create(1, {
110+
id: 30,
111+
title: 'Parent task',
112+
project_id: currentProject.id,
113+
}, false))[0]
114+
await TaskFactory.create(1, {
115+
id: 5,
116+
title: 'Queue candidate other',
117+
project_id: otherProject.id,
118+
}, false)
119+
await TaskFactory.create(1, {
120+
id: 80,
121+
title: 'Queue candidate current',
122+
project_id: currentProject.id,
123+
}, false)
124+
125+
await page.goto(`/tasks/${currentProjectParent.id}`)
126+
const input = await openRelatedTasksForm(page)
127+
await input.pressSequentially('Queue candidate')
128+
129+
const results = relationTaskSearchResults(page)
130+
await expect(results).toHaveCount(2, {timeout: 10000})
131+
await expect(results.first()).toContainText('Queue candidate current')
132+
await expect(results.first().locator('.different-project')).toHaveCount(0)
133+
await expect(results.nth(1)).toContainText('Other Project')
134+
await expect(results.nth(1)).toContainText('Queue candidate other')
135+
})
136+
75137
test('Keeps the title literal when quick add magic is disabled', async ({page, apiContext}) => {
76138
const user = (await UserFactory.create(1, {
77139
frontend_settings: JSON.stringify({

0 commit comments

Comments
 (0)