mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-21 00:57:56 +02:00
UBER-799: Allow extensions to tracker for github (#3727)
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
import { AttachmentStyleBoxEditor } from '@hcengineering/attachment-resources'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { EditBox, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import QueryIssuesList from '../issues/edit/QueryIssuesList.svelte'
|
||||
|
||||
export let object: Component
|
||||
|
||||
@@ -53,3 +54,20 @@
|
||||
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full mt-6">
|
||||
<QueryIssuesList
|
||||
focusIndex={50}
|
||||
{object}
|
||||
query={{ component: object._id }}
|
||||
shouldSaveDraft
|
||||
hasSubIssues={true}
|
||||
viewletId={tracker.viewlet.ComponentIssuesList}
|
||||
createParams={{ component: object._id }}
|
||||
on:docs
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<Label label={tracker.string.Issues} />
|
||||
</svelte:fragment>
|
||||
</QueryIssuesList>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { FilterBar, SpaceHeader, ViewletContentView, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
import CreateIssue from '../CreateIssue.svelte'
|
||||
import { ComponentExtensions } from '@hcengineering/presentation'
|
||||
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
export let query: DocumentQuery<Issue> = {}
|
||||
@@ -51,7 +52,7 @@
|
||||
bind:viewlet
|
||||
bind:search
|
||||
showLabelSelector={$$slots.label_selector}
|
||||
viewletQuery={{ attachTo: tracker.class.Issue, variant: { $ne: 'subissue' } }}
|
||||
viewletQuery={{ attachTo: tracker.class.Issue, variant: { $nin: ['subissue', 'component', 'milestone'] } }}
|
||||
{viewlets}
|
||||
{label}
|
||||
{space}
|
||||
@@ -61,6 +62,11 @@
|
||||
<slot name="label_selector" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="extra">
|
||||
<ComponentExtensions
|
||||
extension={tracker.extensions.IssueListHeader}
|
||||
props={{ size: 'medium', kind: 'ghost', space }}
|
||||
/>
|
||||
|
||||
<ViewletSettingButton bind:viewOptions bind:viewlet />
|
||||
{#if asideFloat && $$slots.aside}
|
||||
<div class="buttons-divider" />
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, DocumentQuery, Ref, toIdMap } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Button, Chevron, ExpandCollapse, IconAdd, closeTooltip, showPopup } from '@hcengineering/ui'
|
||||
import view, { ViewOptions, Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { ViewletsSettingButton } from '@hcengineering/view-resources'
|
||||
import { afterUpdate } from 'svelte'
|
||||
import tracker from '../../../plugin'
|
||||
import CreateIssue from '../../CreateIssue.svelte'
|
||||
import SubIssueList from './SubIssueList.svelte'
|
||||
|
||||
export let projects: Map<Ref<Project>, Project> | undefined = undefined
|
||||
export let shouldSaveDraft: boolean = false
|
||||
export let object: Doc
|
||||
export let query: DocumentQuery<Issue> = {}
|
||||
|
||||
export let createParams: Record<string, any> = {}
|
||||
export let viewletId = tracker.viewlet.SubIssues
|
||||
export let createLabel = tracker.string.CreatedIssue
|
||||
export let hasSubIssues = false
|
||||
|
||||
let isCollapsed = false
|
||||
let listWidth: number
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let viewOptions: ViewOptions | undefined
|
||||
let _projects = projects
|
||||
export let focusIndex = -1
|
||||
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
function openNewIssueDialog (): void {
|
||||
showPopup(tracker.component.CreateIssue, { space: object.space, ...createParams, shouldSaveDraft }, 'top')
|
||||
}
|
||||
|
||||
$: if (projects === undefined) {
|
||||
projectsQuery.query(tracker.class.Project, { archived: false }, async (result) => {
|
||||
_projects = toIdMap(result)
|
||||
})
|
||||
} else {
|
||||
projectsQuery.unsubscribe()
|
||||
}
|
||||
|
||||
let lastIssueId: Ref<Doc>
|
||||
afterUpdate(() => {
|
||||
if (lastIssueId !== object._id) {
|
||||
lastIssueId = object._id
|
||||
}
|
||||
})
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
const objectConfigurations = createQuery()
|
||||
let preference: ViewletPreference[] = []
|
||||
let loading = true
|
||||
|
||||
let configurationRaw: Viewlet[] = []
|
||||
let configurations: Record<Ref<Class<Doc>>, Viewlet['config']> = {}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: viewlet &&
|
||||
objectConfigurations.query(
|
||||
view.class.Viewlet,
|
||||
{
|
||||
attachTo: { $in: client.getHierarchy().getDescendants(viewlet.attachTo) },
|
||||
descriptor: viewlet.descriptor,
|
||||
variant: viewlet.variant ? viewlet.variant : { $exists: false }
|
||||
},
|
||||
(res) => {
|
||||
configurationRaw = res
|
||||
loading = false
|
||||
}
|
||||
)
|
||||
|
||||
$: viewlet &&
|
||||
preferenceQuery.query(
|
||||
view.class.ViewletPreference,
|
||||
{
|
||||
attachedTo: { $in: configurationRaw.map((it) => it._id) }
|
||||
},
|
||||
(res) => {
|
||||
preference = res
|
||||
loading = false
|
||||
}
|
||||
)
|
||||
|
||||
function updateConfiguration (configurationRaw: Viewlet[], preference: ViewletPreference[]): void {
|
||||
const newConfigurations: Record<Ref<Class<Doc>>, Viewlet['config']> = {}
|
||||
|
||||
for (const v of configurationRaw) {
|
||||
newConfigurations[v.attachTo] = v.config
|
||||
}
|
||||
|
||||
// Add viewlet configurations.
|
||||
for (const pref of preference) {
|
||||
if (pref.config.length > 0) {
|
||||
const viewlet = configurationRaw.find((it) => it._id === pref.attachedTo)
|
||||
if (viewlet !== undefined) {
|
||||
newConfigurations[viewlet.attachTo] = pref.config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations = newConfigurations
|
||||
}
|
||||
|
||||
$: updateConfiguration(configurationRaw, preference)
|
||||
</script>
|
||||
|
||||
<div class="flex-between mb-1">
|
||||
{#if hasSubIssues}
|
||||
{#if $$slots.header}
|
||||
<slot name="header" />
|
||||
{:else}
|
||||
<Button
|
||||
width="min-content"
|
||||
kind="ghost"
|
||||
on:click={() => {
|
||||
isCollapsed = !isCollapsed
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
<Chevron
|
||||
size={'small'}
|
||||
expanded={!isCollapsed}
|
||||
outline
|
||||
fill={'var(--caption-color)'}
|
||||
marginRight={'.375rem'}
|
||||
/>
|
||||
<slot name="chevron" />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="flex-row-center gap-2">
|
||||
{#if hasSubIssues}
|
||||
<ViewletsSettingButton bind:viewOptions viewletQuery={{ _id: viewletId }} kind={'ghost'} bind:viewlet />
|
||||
{/if}
|
||||
{#if hasSubIssues}
|
||||
<slot name="buttons" />
|
||||
{/if}
|
||||
<Button
|
||||
id="add-sub-issue"
|
||||
icon={IconAdd}
|
||||
label={hasSubIssues ? undefined : createLabel}
|
||||
labelParams={{ subIssues: 0 }}
|
||||
kind={'ghost'}
|
||||
showTooltip={{ label: createLabel, direction: 'bottom' }}
|
||||
on:click={() => {
|
||||
isCollapsed = false
|
||||
closeTooltip()
|
||||
openNewIssueDialog()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if hasSubIssues && viewOptions && viewlet}
|
||||
{#if !isCollapsed}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<div class="list" class:collapsed={isCollapsed} bind:clientWidth={listWidth}>
|
||||
<SubIssueList
|
||||
createItemDialog={CreateIssue}
|
||||
createItemLabel={tracker.string.AddIssueTooltip}
|
||||
createItemDialogProps={{ space: object.space, ...createParams, shouldSaveDraft }}
|
||||
focusIndex={focusIndex === -1 ? -1 : focusIndex + 1}
|
||||
projects={_projects}
|
||||
{configurations}
|
||||
{preference}
|
||||
{viewlet}
|
||||
{viewOptions}
|
||||
{query}
|
||||
compactMode={listWidth <= 600}
|
||||
on:docs
|
||||
/>
|
||||
</div>
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.list {
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--divider-color);
|
||||
|
||||
&.collapsed {
|
||||
padding-top: 1px;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -21,6 +21,7 @@
|
||||
import { ViewOptions, Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { List, ListSelectionProvider, SelectDirection, selectionStore } from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let query: DocumentQuery<Issue> | undefined = undefined
|
||||
export let viewlet: Viewlet
|
||||
@@ -66,6 +67,8 @@
|
||||
export let createItemDialog: AnySvelteComponent | AnyComponent | undefined = undefined
|
||||
export let createItemLabel: IntlString | undefined = undefined
|
||||
export let createItemDialogProps: Record<string, any> | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
@@ -100,6 +103,7 @@
|
||||
on:content={(evt) => {
|
||||
docs = evt.detail
|
||||
listProvider.update(evt.detail)
|
||||
dispatch('docs', docs)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -13,213 +13,58 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref, toIdMap } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Issue, Project, trackerId } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
Chevron,
|
||||
ExpandCollapse,
|
||||
IconAdd,
|
||||
IconScaleFull,
|
||||
Label,
|
||||
closeTooltip,
|
||||
getCurrentResolvedLocation,
|
||||
navigate,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import view, { ViewOptions, Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { ViewletsSettingButton, createFilter, setFilters } from '@hcengineering/view-resources'
|
||||
import { afterUpdate } from 'svelte'
|
||||
import { Button, IconScaleFull, Label, closeTooltip, getCurrentResolvedLocation, navigate } from '@hcengineering/ui'
|
||||
import { createFilter, setFilters } from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
import CreateIssue from '../../CreateIssue.svelte'
|
||||
import SubIssueList from './SubIssueList.svelte'
|
||||
import QueryIssuesList from './QueryIssuesList.svelte'
|
||||
|
||||
export let issue: Issue
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
export let projects: Map<Ref<Project>, Project> | undefined
|
||||
export let shouldSaveDraft: boolean = false
|
||||
|
||||
let isCollapsed = false
|
||||
let listWidth: number
|
||||
|
||||
$: hasSubIssues = issue.subIssues > 0
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let viewOptions: ViewOptions | undefined
|
||||
let _projects = projects
|
||||
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
function openNewIssueDialog (): void {
|
||||
showPopup(tracker.component.CreateIssue, { space: issue.space, parentIssue: issue, shouldSaveDraft }, 'top')
|
||||
}
|
||||
|
||||
$: if (projects === undefined) {
|
||||
projectsQuery.query(tracker.class.Project, {}, async (result) => {
|
||||
_projects = toIdMap(result)
|
||||
})
|
||||
} else {
|
||||
projectsQuery.unsubscribe()
|
||||
}
|
||||
|
||||
// showPopup(tracker.component.CreateIssue, { space: issue.space, parentIssue: issue, shouldSaveDraft }, 'top')
|
||||
export let focusIndex = -1
|
||||
|
||||
let lastIssueId: Ref<Issue>
|
||||
afterUpdate(() => {
|
||||
if (lastIssueId !== issue._id) {
|
||||
lastIssueId = issue._id
|
||||
}
|
||||
})
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
const objectConfigurations = createQuery()
|
||||
let preference: ViewletPreference[] = []
|
||||
let loading = true
|
||||
|
||||
let configurationRaw: Viewlet[] = []
|
||||
let configurations: Record<Ref<Class<Doc>>, Viewlet['config']> = {}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: viewlet &&
|
||||
objectConfigurations.query(
|
||||
view.class.Viewlet,
|
||||
{
|
||||
attachTo: { $in: client.getHierarchy().getDescendants(viewlet.attachTo) },
|
||||
descriptor: viewlet.descriptor,
|
||||
variant: viewlet.variant ? viewlet.variant : { $exists: false }
|
||||
},
|
||||
(res) => {
|
||||
configurationRaw = res
|
||||
loading = false
|
||||
}
|
||||
)
|
||||
|
||||
$: viewlet &&
|
||||
preferenceQuery.query(
|
||||
view.class.ViewletPreference,
|
||||
{
|
||||
attachedTo: { $in: configurationRaw.map((it) => it._id) }
|
||||
},
|
||||
(res) => {
|
||||
preference = res
|
||||
loading = false
|
||||
}
|
||||
)
|
||||
|
||||
function updateConfiguration (configurationRaw: Viewlet[], preference: ViewletPreference[]): void {
|
||||
const newConfigurations: Record<Ref<Class<Doc>>, Viewlet['config']> = {}
|
||||
|
||||
for (const v of configurationRaw) {
|
||||
newConfigurations[v.attachTo] = v.config
|
||||
}
|
||||
|
||||
// Add viewlet configurations.
|
||||
for (const pref of preference) {
|
||||
if (pref.config.length > 0) {
|
||||
const viewlet = configurationRaw.find((it) => it._id === pref.attachedTo)
|
||||
if (viewlet !== undefined) {
|
||||
newConfigurations[viewlet.attachTo] = pref.config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations = newConfigurations
|
||||
}
|
||||
|
||||
$: updateConfiguration(configurationRaw, preference)
|
||||
let size = issue.subIssues
|
||||
</script>
|
||||
|
||||
<div class="flex-between mb-1">
|
||||
{#if hasSubIssues}
|
||||
<QueryIssuesList
|
||||
object={issue}
|
||||
query={{ attachedTo: issue._id }}
|
||||
createParams={{ space: issue.space, parentIssue: issue }}
|
||||
createLabel={tracker.string.AddSubIssues}
|
||||
hasSubIssues={issue.subIssues > 0}
|
||||
{focusIndex}
|
||||
{projects}
|
||||
{shouldSaveDraft}
|
||||
on:docs={(evt) => {
|
||||
size = evt.detail.length
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="chevron">
|
||||
<Label label={tracker.string.SubIssuesList} params={{ subIssues: size }} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button
|
||||
width="min-content"
|
||||
kind="ghost"
|
||||
on:click={() => {
|
||||
isCollapsed = !isCollapsed
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
<Chevron size={'small'} expanded={!isCollapsed} outline fill={'var(--caption-color)'} marginRight={'.375rem'} />
|
||||
<Label label={tracker.string.SubIssuesList} params={{ subIssues: issue.subIssues }} />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{/if}
|
||||
<div class="flex-row-center gap-2">
|
||||
{#if hasSubIssues}
|
||||
<ViewletsSettingButton
|
||||
bind:viewOptions
|
||||
viewletQuery={{ _id: tracker.viewlet.SubIssues }}
|
||||
kind={'ghost'}
|
||||
bind:viewlet
|
||||
/>
|
||||
{/if}
|
||||
{#if hasSubIssues}
|
||||
<Button
|
||||
icon={IconScaleFull}
|
||||
kind={'ghost'}
|
||||
showTooltip={{ label: tracker.string.OpenSubIssues, direction: 'bottom' }}
|
||||
on:click={() => {
|
||||
const filter = createFilter(tracker.class.Issue, 'attachedTo', [issue._id])
|
||||
if (filter !== undefined) {
|
||||
closeTooltip()
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.fragment = undefined
|
||||
loc.query = undefined
|
||||
loc.path[2] = trackerId
|
||||
loc.path[3] = issue.space
|
||||
loc.path[4] = 'issues'
|
||||
navigate(loc)
|
||||
setFilters([filter])
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<Button
|
||||
id="add-sub-issue"
|
||||
icon={hasSubIssues ? IconAdd : undefined}
|
||||
label={hasSubIssues ? undefined : tracker.string.AddSubIssues}
|
||||
labelParams={{ subIssues: 0 }}
|
||||
icon={IconScaleFull}
|
||||
kind={'ghost'}
|
||||
showTooltip={{ label: tracker.string.AddSubIssues, props: { subIssues: 1 }, direction: 'bottom' }}
|
||||
showTooltip={{ label: tracker.string.OpenSubIssues, direction: 'bottom' }}
|
||||
on:click={() => {
|
||||
isCollapsed = false
|
||||
closeTooltip()
|
||||
openNewIssueDialog()
|
||||
const filter = createFilter(tracker.class.Issue, 'attachedTo', [issue._id])
|
||||
if (filter !== undefined) {
|
||||
closeTooltip()
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.fragment = undefined
|
||||
loc.query = undefined
|
||||
loc.path[2] = trackerId
|
||||
loc.path[3] = issue.space
|
||||
loc.path[4] = 'issues'
|
||||
navigate(loc)
|
||||
setFilters([filter])
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if hasSubIssues && viewOptions && viewlet}
|
||||
{#if !isCollapsed}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<div class="list" class:collapsed={isCollapsed} bind:clientWidth={listWidth}>
|
||||
<SubIssueList
|
||||
createItemDialog={CreateIssue}
|
||||
createItemLabel={tracker.string.AddIssueTooltip}
|
||||
createItemDialogProps={{ space: issue.space, parentIssue: issue, shouldSaveDraft }}
|
||||
focusIndex={focusIndex === -1 ? -1 : focusIndex + 1}
|
||||
projects={_projects}
|
||||
{configurations}
|
||||
{preference}
|
||||
{viewlet}
|
||||
{viewOptions}
|
||||
query={{ attachedTo: issue._id }}
|
||||
compactMode={listWidth <= 600}
|
||||
/>
|
||||
</div>
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.list {
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--divider-color);
|
||||
|
||||
&.collapsed {
|
||||
padding-top: 1px;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</svelte:fragment>
|
||||
</QueryIssuesList>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
$: projectsQuery.query(tracker.class.Project, {}, async (result) => {
|
||||
$: projectsQuery.query(tracker.class.Project, { archived: false }, async (result) => {
|
||||
projects = new Map(result.map((it) => [it._id, it]))
|
||||
})
|
||||
</script>
|
||||
|
||||
+14
-71
@@ -1,86 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { configurationStore, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Issue, trackerId } from '@hcengineering/tracker'
|
||||
import { Button, Component, Icon, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||
import { ViewOptions, Viewlet } from '@hcengineering/view'
|
||||
import { ViewletsSettingButton, getAdditionalHeader } from '@hcengineering/view-resources'
|
||||
import viewplg from '@hcengineering/view-resources/src/plugin'
|
||||
import { fade } from 'svelte/transition'
|
||||
import tracker from '../../../plugin'
|
||||
import RelatedIssues from './RelatedIssues.svelte'
|
||||
import { configurationStore } from '@hcengineering/presentation'
|
||||
import tracker, { Issue, trackerId } from '@hcengineering/tracker'
|
||||
import { Icon, Label } from '@hcengineering/ui'
|
||||
import QueryIssuesList from '../edit/QueryIssuesList.svelte'
|
||||
|
||||
export let object: Doc
|
||||
export let label: IntlString
|
||||
|
||||
const client = getClient()
|
||||
let viewlet: Viewlet | undefined
|
||||
let listWidth: number
|
||||
|
||||
let viewOptions: ViewOptions | undefined
|
||||
const createIssue = () => showPopup(tracker.component.CreateIssue, { relatedTo: object, space: object.space }, 'top')
|
||||
|
||||
let query: DocumentQuery<Issue>
|
||||
$: query = { 'relations._id': object._id, 'relations._class': object._class }
|
||||
const subIssuesQuery = createQuery()
|
||||
let subIssues: Issue[] = []
|
||||
$: subIssuesQuery.query(tracker.class.Issue, query, async (result) => (subIssues = result))
|
||||
|
||||
$: headerRemoval = viewOptions?.groupBy?.length === 0 || viewOptions?.groupBy?.[0] === '#no_category'
|
||||
$: extraHeaders = headerRemoval ? getAdditionalHeader(client, tracker.class.Issue) : undefined
|
||||
</script>
|
||||
|
||||
{#if $configurationStore.has(trackerId)}
|
||||
<div class="antiSection" bind:clientWidth={listWidth}>
|
||||
<div class="antiSection-header mb-3">
|
||||
<div class="antiSection-header__icon">
|
||||
<Icon icon={tracker.icon.Issue} size={'small'} />
|
||||
</div>
|
||||
<span class="antiSection-header__title short overflow-label">
|
||||
<Label {label} />
|
||||
</span>
|
||||
{#if headerRemoval}
|
||||
<div in:fade|local={{ duration: 150 }} class="antiSection-header__header flex-between">
|
||||
<span class="content-dark-color"><Label label={viewplg.string.NoGrouping} /></span>
|
||||
<div class="buttons-group font-normal text-normal">
|
||||
{#if extraHeaders}
|
||||
{#each extraHeaders as extra}
|
||||
<Component is={extra} props={{ docs: subIssues }} />
|
||||
{/each}
|
||||
{/if}
|
||||
<span class="antiSection-header__counter">{subIssues.length}</span>
|
||||
</div>
|
||||
<QueryIssuesList {object} {query} createParams={{ relatedTo: object }} hasSubIssues>
|
||||
<svelte:fragment slot="header">
|
||||
<div class="flex-row-center mb-3">
|
||||
<div class="antiSection-header__icon">
|
||||
<Icon icon={tracker.icon.Issue} size={'small'} />
|
||||
</div>
|
||||
{:else}
|
||||
<span class="flex-grow" />
|
||||
{/if}
|
||||
<div class="flex-row-center gap-2">
|
||||
<ViewletsSettingButton
|
||||
bind:viewOptions
|
||||
viewletQuery={{ _id: tracker.viewlet.SubIssues }}
|
||||
kind={'ghost'}
|
||||
bind:viewlet
|
||||
/>
|
||||
<Button
|
||||
id="add-sub-issue"
|
||||
icon={IconAdd}
|
||||
label={undefined}
|
||||
labelParams={{ subIssues: 0 }}
|
||||
kind={'ghost'}
|
||||
on:click={createIssue}
|
||||
/>
|
||||
<span class="antiSection-header__title short">
|
||||
<Label {label} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{#if viewlet && viewOptions}
|
||||
<RelatedIssues
|
||||
{object}
|
||||
{viewOptions}
|
||||
{viewlet}
|
||||
on:add-issue={createIssue}
|
||||
disableHeader={headerRemoval}
|
||||
compactMode={listWidth <= 600}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</QueryIssuesList>
|
||||
{/if}
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachmentStyleBoxEditor } from '@hcengineering/attachment-resources'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Milestone } from '@hcengineering/tracker'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { EditBox, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { AttachmentStyleBoxEditor } from '@hcengineering/attachment-resources'
|
||||
import QueryIssuesList from '../issues/edit/QueryIssuesList.svelte'
|
||||
|
||||
export let object: Milestone
|
||||
|
||||
@@ -66,3 +67,19 @@
|
||||
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full mt-6">
|
||||
<QueryIssuesList
|
||||
focusIndex={50}
|
||||
{object}
|
||||
query={{ milestone: object._id }}
|
||||
shouldSaveDraft
|
||||
hasSubIssues={true}
|
||||
viewletId={tracker.viewlet.MilestoneIssuesList}
|
||||
createParams={{ milestone: object._id }}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<Label label={tracker.string.Issues} />
|
||||
</svelte:fragment>
|
||||
</QueryIssuesList>
|
||||
</div>
|
||||
|
||||
@@ -75,10 +75,9 @@
|
||||
width="min-content"
|
||||
icon={hasSubIssues ? IconAdd : undefined}
|
||||
label={hasSubIssues ? undefined : tracker.string.AddSubIssues}
|
||||
labelParams={{ subIssues: 0 }}
|
||||
kind={'ghost'}
|
||||
size={'small'}
|
||||
showTooltip={{ label: tracker.string.AddSubIssues, props: { subIssues: 1 } }}
|
||||
showTooltip={{ label: tracker.string.AddSubIssues }}
|
||||
on:click={() => {
|
||||
closeTooltip()
|
||||
isCreating = true
|
||||
|
||||
Reference in New Issue
Block a user