Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions models/tracker/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,30 @@ export function createActions (builder: Builder, issuesId: string, componentsId:
},
tracker.action.SetParent
)
createAction(
builder,
{
action: view.actionImpl.UpdateDocument,
actionProps: {
key: 'attachedTo',
value: tracker.ids.NoParent
},
query: {
attachedTo: { $ne: tracker.ids.NoParent }
},
label: tracker.string.UnsetParentIssue,
icon: tracker.icon.UnsetParent,
input: 'none',
category: tracker.category.Tracker,
target: tracker.class.Issue,
context: {
mode: ['context'],
application: tracker.app.Tracker,
group: 'associate'
}
},
tracker.action.UnsetParent
)

createAction(
builder,
Expand Down
7 changes: 1 addition & 6 deletions models/tracker/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
//
import { type DocUpdateMessageViewlet } from '@hcengineering/activity'
import { type ChatMessageViewlet } from '@hcengineering/chunter'
import { type StatusCategory, type Doc, type Ref, type DocManager } from '@hcengineering/core'
import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation'
import { type Doc, type DocManager, type Ref, type StatusCategory } from '@hcengineering/core'
import { type NotificationGroup, type NotificationType } from '@hcengineering/notification'
import { mergeIds, type IntlString, type Resource } from '@hcengineering/platform'
import { type ProjectType } from '@hcengineering/task'
Expand Down Expand Up @@ -87,10 +86,6 @@ export default mergeIds(trackerId, tracker, {
MilestoneChatMessageViewlet: '' as Ref<ChatMessageViewlet>,
DefaultProjectType: '' as Ref<ProjectType>
},
completion: {
IssueQuery: '' as Resource<ObjectSearchFactory>,
IssueCategory: '' as Ref<ObjectSearchCategory>
},
actionImpl: {
Move: '' as ViewAction,
CopyToClipboard: '' as ViewAction,
Expand Down
47 changes: 35 additions & 12 deletions packages/presentation/src/components/ObjectPopup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import type { IntlString } from '@hcengineering/platform'
import { Label } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import presentation from '..'
import { ObjectCreate } from '../types'
import presentation, { searchFor, type SearchItem } from '..'
import { ObjectCreate, type ObjectSearchCategory } from '../types'
import { createQuery } from '../utils'
import DocPopup from './DocPopup.svelte'

Expand All @@ -46,7 +46,8 @@
export let width: 'medium' | 'large' | 'full' | 'auto' = 'medium'
export let size: 'small' | 'medium' | 'large' = 'large'

export let searchMode: 'field' | 'fulltext' | 'disabled' = 'field'
export let searchMode: 'field' | 'fulltext' | 'disabled' | 'spotlight' = 'field'
export let category: Ref<ObjectSearchCategory> | undefined = undefined
export let searchField: string = 'name'
export let groupBy = '_class'

Expand Down Expand Up @@ -74,21 +75,39 @@
let search: string = ''
let objects: Doc[] = []

let extraItems: Ref<Doc>[] = []

const query = createQuery()

$: noSearchField = searchMode === 'disabled'
$: _idExtra = typeof docQuery?._id === 'object' ? docQuery?._id : {}
$: if (searchMode === 'spotlight' && search !== '') {
void searchSpotlight(search).then((items) => {
extraItems = items.map((it) => it.item.id)
})
} else {
extraItems = []
}
$: fquery = {
...(docQuery ?? {}),
...(() => {
switch (searchMode) {
case 'disabled':
return { _id: { $nin: ignoreObjects, ..._idExtra } }
case 'fulltext':
return { $search: search, _id: { $nin: ignoreObjects, ..._idExtra } }
case 'spotlight':
return extraItems.length > 0
? { _id: { $in: extraItems, $nin: ignoreObjects } }
: { _id: { $nin: ignoreObjects, ..._idExtra } }
default:
return { [searchField]: { $like: '%' + search + '%' }, _id: { $nin: ignoreObjects, ..._idExtra } }
}
})()
}
$: query.query<Doc>(
_class,
{
...(docQuery ?? {}),
...(searchMode !== 'disabled' && search !== ''
? searchMode === 'fulltext'
? { $search: search }
: { [searchField]: { $like: '%' + search + '%' } }
: {}),
_id: { $nin: ignoreObjects, ..._idExtra }
},
fquery,
(result) => {
result.sort(sort)
if (created.length > 0) {
Expand All @@ -100,6 +119,10 @@
},
{ ...(options ?? {}), limit: 200 }
)

async function searchSpotlight (search: string): Promise<SearchItem[]> {
return (await searchFor('spotlight', search, category, 50)).items
}
</script>

<DocPopup
Expand Down
19 changes: 12 additions & 7 deletions packages/presentation/src/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,16 @@ async function searchCategory (
client: TxOperations,
cl: Ref<Class<Doc>>,
query: string,
categories: ObjectSearchCategory[]
categories: ObjectSearchCategory[],
limit?: number
): Promise<SearchSection | undefined> {
const r = await client.searchFulltext(
{
query: `${query}*`,
classes: [cl]
},
{
limit: 5
limit: limit ?? 5
}
)
const category = findCategoryByClass(categories, cl)
Expand All @@ -84,12 +85,13 @@ async function doFulltextSearch (
client: TxOperations,
classes: Array<Ref<Class<Doc>>>,
query: string,
categories: ObjectSearchCategory[]
categories: ObjectSearchCategory[],
limit?: number
): Promise<SearchSection[]> {
const sections: SearchSection[] = []
const promises: Array<Promise<SearchSection | undefined>> = []
for (const cl of classes) {
promises.push(searchCategory(client, cl, query, categories))
promises.push(searchCategory(client, cl, query, categories, limit))
}

const resolvedSections = await Promise.all(promises)
Expand All @@ -113,7 +115,9 @@ const categoriesByContext = new Map<string, ObjectSearchCategory[]>()

export async function searchFor (
context: 'mention' | 'spotlight',
query: string
query: string,
category?: Ref<ObjectSearchCategory>,
limit?: number
): Promise<{ items: SearchItem[], query: string }> {
const client = getClient()
let categories = categoriesByContext.get(context)
Expand All @@ -129,12 +133,13 @@ export async function searchFor (
}

const classesToSearch: Array<Ref<Class<Doc>>> = []
for (const cat of categories) {
const cats = category === undefined ? categories : categories.filter((it) => it._id === category)
for (const cat of cats) {
if (cat.classToSearch !== undefined) {
classesToSearch.push(cat.classToSearch)
}
}

const sections = await doFulltextSearch(client, classesToSearch, query, categories)
const sections = await doFulltextSearch(client, classesToSearch, query, categories, limit)
return { items: packSearchResultsForListView(sections), query }
}
5 changes: 5 additions & 0 deletions plugins/tracker-assets/assets/icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@
"DefaultIssueStatus": "Výchozí stav úkolu",
"IssueStatus": "Stav",
"Extensions": "Rozšíření",
"RoleLabel": "Role: {role}"
"RoleLabel": "Role: {role}",
"UnsetParentIssue": "Odebrat nadřazený úkol"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@
"DefaultIssueStatus": "Standard-Aufgabenstatus",
"IssueStatus": "Status",
"Extensions": "Erweiterungen",
"RoleLabel": "Rolle: {role}"
"RoleLabel": "Rolle: {role}",
"UnsetParentIssue": "Übergeordnete Aufgabe entfernen"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@
"DefaultIssueStatus": "Default issue status",
"IssueStatus": "Status",
"Extensions": "Extensions",
"RoleLabel": "Role: {role}"
"RoleLabel": "Role: {role}",
"UnsetParentIssue": "Unset parent issue"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@
"DefaultIssueStatus": "Estado de problema predeterminado",
"IssueStatus": "Estado",
"Extensions": "Extensions",
"RoleLabel": "Role: {role}"
"RoleLabel": "Role: {role}",
"UnsetParentIssue": "Unset parent issue"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@
"DefaultIssueStatus": "Statut par défaut de l'issue",
"IssueStatus": "Statut",
"Extensions": "Extensions",
"RoleLabel": "Rôle : {role}"
"RoleLabel": "Rôle : {role}",
"UnsetParentIssue": "Désélectionner l'issue parent"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@
"DefaultIssueStatus": "Stato predefinito della issue",
"IssueStatus": "Stato",
"Extensions": "Estensioni",
"RoleLabel": "Ruolo: {role}"
"RoleLabel": "Ruolo: {role}",
"UnsetParentIssue": "Annulla l'issue genitore"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@
"DefaultIssueStatus": "Estado padrão do problema",
"IssueStatus": "Estado",
"Extensions": "Extensions",
"RoleLabel": "Cargo: {role}"
"RoleLabel": "Cargo: {role}",
"UnsetParentIssue": "Desmarcar problema pai"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@
"DefaultIssueStatus": "Статус по умолчанию",
"IssueStatus": "Статус",
"Extensions": "Дополнительно",
"RoleLabel": "Роль: {role}"
"RoleLabel": "Роль: {role}",
"UnsetParentIssue": "Снять родительскую задачу"
},
"status": {}
}
3 changes: 2 additions & 1 deletion plugins/tracker-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@
"DefaultIssueStatus": "默认问题状态",
"IssueStatus": "状态",
"Extensions": "扩展",
"RoleLabel": "角色:{role}"
"RoleLabel": "角色:{role}",
"UnsetParentIssue": "取消父问题"
},
"status": {}
}
1 change: 1 addition & 0 deletions plugins/tracker-assets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ loadMetadata(tracker.icon, {
Labels: `${icons}#labels`,
DueDate: `${icons}#dueDate`, // TODO: add icon
Parent: `${icons}#parent-issue`, // TODO: add icon
UnsetParent: `${icons}#unset-parent-issue`, // TODO: add icon
Milestone: `${icons}#milestone`,
IssueTemplates: `${icons}#issuetemplates`,
Start: `${icons}#start`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,21 @@

$: selected = !Array.isArray(value) ? ('attachedTo' in value ? value.attachedTo : undefined) : undefined
$: ignoreObjects = !Array.isArray(value) ? ('_id' in value ? [value._id] : []) : undefined
$: docQuery = {
'parents.parentId': {
$nin: [
...new Set(
(Array.isArray(value) ? value : [value])
.map((issue) => ('_id' in issue ? issue._id : null))
.filter((x): x is Ref<Issue> => x !== null)
)
]
}
}
</script>

<ObjectPopup
_class={tracker.class.Issue}
{options}
{docQuery}
{selected}
category={tracker.completion.IssueCategory}
multiSelect={false}
allowDeselect={true}
placeholder={tracker.string.SetParent}
create={undefined}
{ignoreObjects}
shadows={true}
{width}
searchMode={'fulltext'}
searchMode={'spotlight'}
on:update
on:close={onClose}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@
$: taskType = issue?.kind !== undefined ? $taskTypeStore.get(issue?.kind) : undefined

$: projectType = taskType?.parent !== undefined ? $typeStore.get(taskType.parent) : undefined

async function unsetParentIssue (): Promise<void> {
if (issue === undefined || readonly) return

await client.update(issue, { attachedTo: tracker.ids.NoParent })
Analytics.handleEvent(TrackerEvents.IssueParentUnset, { issue: issue.identifier ?? issue._id })
}
</script>

{#if !embedded}
Expand Down Expand Up @@ -276,8 +283,22 @@
</svelte:fragment>

{#if hasParentIssue}
<div class="mb-6">
<div class="mb-6 flex-row-center">
<SubIssueSelector {issue} />
{#if !readonly}
<div class="ml-2">
<Button
icon={tracker.icon.UnsetParent}
iconProps={{ size: 'medium' }}
kind={'regular'}
showTooltip={{ label: tracker.string.UnsetParentIssue }}
dataId={'btnUnsetParent'}
on:click={() => {
void unsetParentIssue()
}}
/>
</div>
{/if}
</div>
{/if}
<EditBox
Expand Down
Loading