diff --git a/plugins/tracker-resources/src/components/issues/edit/SubIssues.svelte b/plugins/tracker-resources/src/components/issues/edit/SubIssues.svelte index 98971d121d..dfea38cde4 100644 --- a/plugins/tracker-resources/src/components/issues/edit/SubIssues.svelte +++ b/plugins/tracker-resources/src/components/issues/edit/SubIssues.svelte @@ -15,10 +15,21 @@ <script lang="ts"> import { Ref, SortingOrder, toIdMap, WithLookup } from '@hcengineering/core' import { createQuery } from '@hcengineering/presentation' - import { Issue, IssueStatus, Team } from '@hcengineering/tracker' - import { Button, Chevron, closeTooltip, ExpandCollapse, IconAdd, IconArrowRight, Label } from '@hcengineering/ui' + import { Issue, IssueStatus, Team, trackerId } from '@hcengineering/tracker' + import { + Button, + Chevron, + closeTooltip, + ExpandCollapse, + getCurrentLocation, + IconScaleFull, + IconAdd, + IconArrowRight, + Label, + navigate + } from '@hcengineering/ui' import view, { Viewlet } from '@hcengineering/view' - import { getViewOptions, ViewletSettingButton } from '@hcengineering/view-resources' + import { createFilter, filterStore, getViewOptions, ViewletSettingButton } from '@hcengineering/view-resources' import tracker from '../../../plugin' import CreateSubIssue from './CreateSubIssue.svelte' import SubIssueList from './SubIssueList.svelte' @@ -99,6 +110,27 @@ {#if viewlet && hasSubIssues && viewOptions} <ViewletSettingButton bind:viewOptions {viewlet} kind={'transparent'} /> {/if} + <Button + width="min-content" + icon={IconScaleFull} + kind={'transparent'} + size={'small'} + showTooltip={{ label: tracker.string.OpenSubIssues, direction: 'bottom' }} + on:click={() => { + const filter = createFilter(tracker.class.Issue, 'attachedTo', [issue._id]) + if (filter !== undefined) { + closeTooltip() + const loc = getCurrentLocation() + loc.fragment = undefined + loc.query = undefined + loc.path[2] = trackerId + loc.path[3] = issue.space + loc.path[4] = 'issues' + navigate(loc) + $filterStore = [filter] + } + }} + /> <Button id="add-sub-issue" width="min-content" diff --git a/plugins/view-resources/src/components/filter/FilterTypePopup.svelte b/plugins/view-resources/src/components/filter/FilterTypePopup.svelte index 9f77f06f50..56cd609fec 100644 --- a/plugins/view-resources/src/components/filter/FilterTypePopup.svelte +++ b/plugins/view-resources/src/components/filter/FilterTypePopup.svelte @@ -13,18 +13,7 @@ // limitations under the License. --> <script lang="ts"> - import core, { - AnyAttribute, - ArrOf, - AttachedDoc, - Class, - Collection, - Doc, - Ref, - RefTo, - Space, - Type - } from '@hcengineering/core' + import core, { AnyAttribute, ArrOf, Class, Doc, Ref, RefTo, Space, Type } from '@hcengineering/core' import { getClient } from '@hcengineering/presentation' import { closePopup, @@ -38,7 +27,7 @@ } from '@hcengineering/ui' import { Filter, KeyFilter } from '@hcengineering/view' import { createEventDispatcher } from 'svelte' - import { FilterQuery } from '../../filter' + import { buildFilterKey, FilterQuery } from '../../filter' import view from '../../plugin' export let _class: Ref<Class<Doc>> @@ -67,25 +56,7 @@ function buildFilterFromKey (_class: Ref<Class<Doc>>, key: string): KeyFilter | undefined { const attribute = hierarchy.getAttribute(_class, key) - return buildFilter(_class, key, attribute) - } - - function buildFilter (_class: Ref<Class<Doc>>, key: string, attribute: AnyAttribute): KeyFilter | undefined { - const isCollection = hierarchy.isDerived(attribute.type._class, core.class.Collection) - const targetClass = isCollection ? (attribute.type as Collection<AttachedDoc>).of : attribute.type._class - const clazz = hierarchy.getClass(targetClass) - const filter = hierarchy.as(clazz, view.mixin.AttributeFilter) - - const attrOf = hierarchy.getClass(attribute.attributeOf) - if (filter.component === undefined) return undefined - return { - _class, - key: isCollection ? '_id' : key, - attribute, - label: attribute.label, - icon: attribute.icon ?? clazz.icon ?? attrOf.icon ?? view.icon.Setting, - component: filter.component - } + return buildFilterKey(hierarchy, _class, key, attribute) } function getValue (name: string, type: Type<any>): string { @@ -103,7 +74,7 @@ if (result.findIndex((p) => p.key === value) !== -1) { return } - const filter = buildFilter(_class, value, attribute) + const filter = buildFilterKey(hierarchy, _class, value, attribute) if (filter !== undefined) { result.push(filter) } diff --git a/plugins/view-resources/src/filter.ts b/plugins/view-resources/src/filter.ts index 48b8fb688a..9308b0d0c4 100644 --- a/plugins/view-resources/src/filter.ts +++ b/plugins/view-resources/src/filter.ts @@ -1,7 +1,18 @@ -import { Doc, FindResult, ObjQueryType, Ref } from '@hcengineering/core' +import core, { + AnyAttribute, + AttachedDoc, + Class, + Collection, + Doc, + FindResult, + Hierarchy, + ObjQueryType, + Ref +} from '@hcengineering/core' import { getResource } from '@hcengineering/platform' import { createQuery, getClient, LiveQuery } from '@hcengineering/presentation' -import { Filter } from '@hcengineering/view' +import { AnyComponent } from '@hcengineering/ui' +import { Filter, FilterMode, KeyFilter } from '@hcengineering/view' import { writable } from 'svelte/store' import view from './plugin' @@ -94,3 +105,70 @@ export async function getRefs (filter: Filter, onUpdate: () => void): Promise<Ar }) return await promise } + +export function buildFilterKey ( + hierarchy: Hierarchy, + _class: Ref<Class<Doc>>, + key: string, + attribute: AnyAttribute +): KeyFilter | undefined { + const isCollection = hierarchy.isDerived(attribute.type._class, core.class.Collection) + const targetClass = isCollection ? (attribute.type as Collection<AttachedDoc>).of : attribute.type._class + const clazz = hierarchy.getClass(targetClass) + const filter = hierarchy.as(clazz, view.mixin.AttributeFilter) + + const attrOf = hierarchy.getClass(attribute.attributeOf) + if (filter.component === undefined) return undefined + return { + _class, + key: isCollection ? '_id' : key, + attribute, + label: attribute.label, + icon: attribute.icon ?? clazz.icon ?? attrOf.icon ?? view.icon.Setting, + component: filter.component + } +} + +interface FilterModes { + modes: Array<Ref<FilterMode>> + mode: Ref<FilterMode> +} + +function getFilterModes (component: AnyComponent): FilterModes | undefined { + if (component === view.component.ObjectFilter) { + return { + modes: [view.filter.FilterObjectIn, view.filter.FilterObjectNin], + mode: view.filter.FilterObjectIn + } + } + if (component === view.component.ValueFilter) { + return { + modes: [view.filter.FilterValueIn, view.filter.FilterValueNin], + mode: view.filter.FilterValueIn + } + } + if (component === view.component.TimestampFilter) { + return { + modes: [view.filter.FilterBefore, view.filter.FilterAfter], + mode: view.filter.FilterBefore + } + } +} + +export function createFilter (_class: Ref<Class<Doc>>, key: string, value: any[]): Filter | undefined { + const client = getClient() + const hierarchy = client.getHierarchy() + const attribute = hierarchy.getAttribute(_class, key) + const filterKey = buildFilterKey(hierarchy, _class, key, attribute) + if (filterKey === undefined) return + + const modes = getFilterModes(filterKey.component) + if (modes === undefined) return + return { + key: filterKey, + value, + index: 1, + modes: modes.modes, + mode: modes.mode + } +}