mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 19:58:09 +00:00
Squashed commit of the following: (#3059)
Signed-off-by: Sergei Ogorelkov <sergei.ogorelkov@icloud.com>
This commit is contained in:
parent
8acdcf7c73
commit
45bdad87b9
@ -342,7 +342,7 @@ export class TTimeSpendReport extends TAttachedDoc implements TimeSpendReport {
|
||||
@UX(tracker.string.Component, tracker.icon.Component, 'COMPONENT')
|
||||
export class TComponent extends TDoc implements Component {
|
||||
@Prop(TypeString(), tracker.string.Title)
|
||||
// @Index(IndexKind.FullText)
|
||||
@Index(IndexKind.FullText)
|
||||
label!: string
|
||||
|
||||
@Prop(TypeMarkup(), tracker.string.Description)
|
||||
@ -1357,6 +1357,10 @@ export function createModel (builder: Builder): void {
|
||||
filters: []
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Component, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: []
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
presentation.class.ObjectSearchCategory,
|
||||
core.space.Model,
|
||||
@ -1892,4 +1896,107 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
tracker.action.SetSprintLead
|
||||
)
|
||||
|
||||
const componentListViewOptions: ViewOptionsModel = {
|
||||
groupBy: ['lead'],
|
||||
orderBy: [
|
||||
['startDate', SortingOrder.Descending],
|
||||
['modifiedOn', SortingOrder.Descending]
|
||||
],
|
||||
other: []
|
||||
}
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: tracker.class.Component,
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: componentListViewOptions,
|
||||
config: [
|
||||
{ key: '', presenter: tracker.component.IconPresenter },
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.ComponentPresenter,
|
||||
props: { kind: 'list', shouldShowAvatar: false }
|
||||
},
|
||||
{ key: '', presenter: view.component.GrowPresenter, props: { type: 'grow' } },
|
||||
{
|
||||
key: '$lookup.lead',
|
||||
presenter: tracker.component.LeadPresenter,
|
||||
props: { _class: tracker.class.Component, defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
presenter: contact.component.MembersPresenter,
|
||||
props: {
|
||||
kind: 'link',
|
||||
intlTitle: tracker.string.ComponentMembersTitle,
|
||||
intlSearchPh: tracker.string.ComponentMembersSearchPlaceholder
|
||||
}
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TargetDatePresenter },
|
||||
{ key: '', presenter: tracker.component.ComponentStatusPresenter, props: { width: 'min-content' } },
|
||||
{ key: '', presenter: tracker.component.DeleteComponentPresenter }
|
||||
]
|
||||
},
|
||||
tracker.viewlet.ComponentList
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.Timeline,
|
||||
icon: view.icon.Timeline,
|
||||
component: tracker.component.ComponentsTimeline
|
||||
},
|
||||
tracker.viewlet.Timeline
|
||||
)
|
||||
|
||||
const componentTimelineViewOptions: ViewOptionsModel = {
|
||||
groupBy: [],
|
||||
orderBy: [
|
||||
['startDate', SortingOrder.Descending],
|
||||
['modifiedOn', SortingOrder.Descending]
|
||||
],
|
||||
other: [],
|
||||
groupDepth: 1
|
||||
}
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: tracker.class.Component,
|
||||
descriptor: tracker.viewlet.Timeline,
|
||||
viewOptions: componentTimelineViewOptions,
|
||||
config: [
|
||||
{ key: '', presenter: tracker.component.IconPresenter },
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.ComponentPresenter,
|
||||
props: { kind: 'list', shouldShowAvatar: false }
|
||||
},
|
||||
{
|
||||
key: '$lookup.lead',
|
||||
presenter: tracker.component.LeadPresenter,
|
||||
props: { _class: tracker.class.Component, defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
presenter: contact.component.MembersPresenter,
|
||||
props: {
|
||||
kind: 'link',
|
||||
intlTitle: tracker.string.ComponentMembersTitle,
|
||||
intlSearchPh: tracker.string.ComponentMembersSearchPlaceholder
|
||||
}
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TargetDatePresenter },
|
||||
{ key: '', presenter: tracker.component.ComponentStatusPresenter, props: { width: 'min-content' } },
|
||||
{ key: '', presenter: tracker.component.DeleteComponentPresenter }
|
||||
]
|
||||
},
|
||||
tracker.viewlet.ComponentsTimeline
|
||||
)
|
||||
}
|
||||
|
@ -60,7 +60,9 @@ export default mergeIds(trackerId, tracker, {
|
||||
IssueList: '' as Ref<Viewlet>,
|
||||
IssueTemplateList: '' as Ref<Viewlet>,
|
||||
IssueKanban: '' as Ref<Viewlet>,
|
||||
SprintList: '' as Ref<Viewlet>
|
||||
SprintList: '' as Ref<Viewlet>,
|
||||
ComponentList: '' as Ref<Viewlet>,
|
||||
ComponentsTimeline: '' as Ref<Viewlet>
|
||||
},
|
||||
ids: {
|
||||
TxIssueCreated: '' as Ref<TxViewlet>,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
// Copyright © 2023 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
|
||||
@ -13,81 +13,104 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@hcengineering/contact'
|
||||
import { DocumentQuery, FindOptions, SortingOrder } from '@hcengineering/core'
|
||||
import { DocumentQuery, WithLookup } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Button, IconAdd, Label, showPopup, TabList } from '@hcengineering/ui'
|
||||
import type { TabItem } from '@hcengineering/ui'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import view, { Viewlet } from '@hcengineering/view'
|
||||
import {
|
||||
makeViewletKey,
|
||||
updateActiveViewlet,
|
||||
activeViewlet,
|
||||
getViewOptions,
|
||||
viewOptionStore,
|
||||
FilterButton,
|
||||
ViewletSettingButton,
|
||||
FilterBar
|
||||
} from '@hcengineering/view-resources'
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
IconAdd,
|
||||
IconMoreH,
|
||||
Label,
|
||||
SearchEdit,
|
||||
TabItem,
|
||||
TabList,
|
||||
resolvedLocationStore,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { ComponentsFilterMode, componentsTitleMap, getIncludedComponentStatuses } from '../../utils'
|
||||
import tracker from '../../plugin'
|
||||
import view from '@hcengineering/view'
|
||||
import { getIncludedComponentStatuses, componentsTitleMap, ComponentsViewMode } from '../../utils'
|
||||
import ComponentsContent from './ComponentsContent.svelte'
|
||||
import NewComponent from './NewComponent.svelte'
|
||||
import ComponentsListBrowser from './ComponentsListBrowser.svelte'
|
||||
|
||||
export let label: IntlString
|
||||
export let query: DocumentQuery<Component> = {}
|
||||
export let search: string = ''
|
||||
export let mode: ComponentsViewMode = 'all'
|
||||
export let viewMode: 'list' | 'timeline' = 'list'
|
||||
|
||||
const ENTRIES_LIMIT = 200
|
||||
const resultComponentsQuery = createQuery()
|
||||
|
||||
const componentOptions: FindOptions<Component> = {
|
||||
sort: { modifiedOn: SortingOrder.Descending },
|
||||
limit: ENTRIES_LIMIT,
|
||||
lookup: { lead: contact.class.Employee, members: contact.class.Employee }
|
||||
}
|
||||
|
||||
let resultComponents: Component[] = []
|
||||
|
||||
$: includedComponentStatuses = getIncludedComponentStatuses(mode)
|
||||
$: title = componentsTitleMap[mode]
|
||||
$: includedComponentsQuery = { status: { $in: includedComponentStatuses } }
|
||||
|
||||
$: baseQuery = {
|
||||
...includedComponentsQuery,
|
||||
...query
|
||||
}
|
||||
|
||||
$: resultQuery = search === '' ? baseQuery : { $search: search, ...baseQuery }
|
||||
|
||||
$: resultComponentsQuery.query<Component>(
|
||||
tracker.class.Component,
|
||||
{ ...resultQuery },
|
||||
(result) => {
|
||||
resultComponents = result
|
||||
},
|
||||
componentOptions
|
||||
)
|
||||
export let search = ''
|
||||
export let filterMode: ComponentsFilterMode = 'all'
|
||||
export let panelWidth: number = 0
|
||||
|
||||
const viewletQuery = createQuery()
|
||||
const space = typeof query.space === 'string' ? query.space : tracker.project.DefaultProject
|
||||
const showCreateDialog = async () => {
|
||||
|
||||
const filterModeList: TabItem[] = [
|
||||
{ id: 'all', labelIntl: tracker.string.AllComponents, action: () => handleFilterModeChanged('all') },
|
||||
{ id: 'backlog', labelIntl: tracker.string.BacklogComponents, action: () => handleFilterModeChanged('backlog') },
|
||||
{ id: 'active', labelIntl: tracker.string.ActiveComponents, action: () => handleFilterModeChanged('active') },
|
||||
{ id: 'closed', labelIntl: tracker.string.ClosedComponents, action: () => handleFilterModeChanged('closed') }
|
||||
]
|
||||
|
||||
let viewlet: WithLookup<Viewlet> | undefined
|
||||
let viewlets: WithLookup<Viewlet>[] | undefined
|
||||
let viewletKey = makeViewletKey()
|
||||
|
||||
let searchQuery: DocumentQuery<Component> = { ...query }
|
||||
let resultQuery: DocumentQuery<Component> = { ...searchQuery }
|
||||
let includedComponentsQuery: DocumentQuery<Component>
|
||||
|
||||
let asideFloat = false
|
||||
let asideShown = true
|
||||
|
||||
let docWidth: number
|
||||
let docSize = false
|
||||
|
||||
function handleFilterModeChanged (newMode: ComponentsFilterMode) {
|
||||
if (newMode !== filterMode) {
|
||||
filterMode = newMode
|
||||
}
|
||||
}
|
||||
|
||||
function showCreateDialog () {
|
||||
showPopup(NewComponent, { space, targetElement: null }, 'top')
|
||||
}
|
||||
|
||||
const handleViewModeChanged = (newMode: ComponentsViewMode) => {
|
||||
if (newMode === undefined || newMode === mode) {
|
||||
return
|
||||
}
|
||||
$: title = componentsTitleMap[filterMode]
|
||||
$: includedComponentStatuses = getIncludedComponentStatuses(filterMode)
|
||||
$: includedComponentsQuery = { status: { $in: includedComponentStatuses } }
|
||||
$: searchQuery = search === '' ? { ...query } : { ...query, $search: search }
|
||||
$: resultQuery = { ...searchQuery }
|
||||
|
||||
mode = newMode
|
||||
$: viewletQuery.query(view.class.Viewlet, { attachTo: tracker.class.Component }, (res) => (viewlets = res), {
|
||||
lookup: { descriptor: view.class.ViewletDescriptor }
|
||||
})
|
||||
$: viewlet = viewlets && updateActiveViewlet(viewlets, $activeViewlet[viewletKey])
|
||||
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
|
||||
$: views =
|
||||
viewlets?.map((v) => ({ id: v._id, icon: v.$lookup?.descriptor?.icon, tooltip: v.$lookup?.descriptor?.label })) ??
|
||||
[]
|
||||
|
||||
$: if (panelWidth < 900 && !asideFloat) asideFloat = true
|
||||
$: if (panelWidth >= 900 && asideFloat) {
|
||||
asideFloat = false
|
||||
asideShown = false
|
||||
}
|
||||
|
||||
const modeList: TabItem[] = [
|
||||
{ id: 'all', labelIntl: tracker.string.AllComponents, action: () => handleViewModeChanged('all') },
|
||||
{ id: 'backlog', labelIntl: tracker.string.BacklogComponents, action: () => handleViewModeChanged('backlog') },
|
||||
{ id: 'active', labelIntl: tracker.string.ActiveComponents, action: () => handleViewModeChanged('active') },
|
||||
{ id: 'closed', labelIntl: tracker.string.ClosedComponents, action: () => handleViewModeChanged('closed') }
|
||||
]
|
||||
const viewList: TabItem[] = [
|
||||
{ id: 'list', icon: view.icon.List, tooltip: view.string.List },
|
||||
{ id: 'timeline', icon: view.icon.Timeline, tooltip: view.string.Timeline }
|
||||
]
|
||||
$: if (docWidth <= 900 && !docSize) docSize = true
|
||||
$: if (docWidth > 900 && docSize) docSize = false
|
||||
|
||||
const retrieveMembers = (p: Component) => p.members
|
||||
onDestroy(resolvedLocationStore.subscribe((loc) => (viewletKey = makeViewletKey(loc))))
|
||||
</script>
|
||||
|
||||
<div class="ac-header full divide caption-height">
|
||||
@ -99,77 +122,57 @@
|
||||
</div>
|
||||
|
||||
<div class="ac-header-full medium-gap mb-1">
|
||||
<TabList
|
||||
items={viewList}
|
||||
selected={viewMode}
|
||||
on:select={(result) => {
|
||||
if (result.detail !== undefined && result.detail.id !== viewMode) viewMode = result.detail.id
|
||||
}}
|
||||
/>
|
||||
<Button icon={IconAdd} label={tracker.string.Component} kind={'primary'} on:click={showCreateDialog} />
|
||||
{#if viewlets && viewlets.length > 1}
|
||||
<TabList
|
||||
items={views}
|
||||
selected={viewlet?._id}
|
||||
kind="normal"
|
||||
on:select={({ detail }) =>
|
||||
(viewlet = viewlets && detail?.id ? updateActiveViewlet(viewlets, detail.id) : viewlet)}
|
||||
/>
|
||||
{/if}
|
||||
<Button icon={IconAdd} label={tracker.string.Component} kind="primary" on:click={showCreateDialog} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac-header full divide search-start">
|
||||
<div class="ac-header-full small-gap">
|
||||
<SearchEdit bind:value={search} />
|
||||
<ActionIcon icon={IconMoreH} size="small" />
|
||||
<div class="buttons-divider" />
|
||||
<FilterButton _class={tracker.class.Component} {space} />
|
||||
</div>
|
||||
<div class="ac-header-full medium-gap">
|
||||
{#if viewlet}
|
||||
<ViewletSettingButton bind:viewOptions {viewlet} />
|
||||
<ActionIcon icon={IconMoreH} size="small" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac-header full divide search-start">
|
||||
<div class="ac-header-full small-gap">
|
||||
<TabList
|
||||
items={modeList}
|
||||
selected={mode}
|
||||
kind={'normal'}
|
||||
on:select={(result) => {
|
||||
if (result.detail !== undefined && result.detail.action) result.detail.action()
|
||||
}}
|
||||
items={filterModeList}
|
||||
selected={filterMode}
|
||||
kind="normal"
|
||||
on:select={({ detail }) => detail?.action?.()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ComponentsListBrowser
|
||||
<FilterBar
|
||||
_class={tracker.class.Component}
|
||||
itemsConfig={[
|
||||
{ key: '', presenter: tracker.component.IconPresenter },
|
||||
{ key: '', presenter: tracker.component.ComponentPresenter, props: { kind: 'list', shouldShowAvatar: false } },
|
||||
{
|
||||
key: '$lookup.lead',
|
||||
presenter: tracker.component.LeadPresenter,
|
||||
props: { _class: tracker.class.Component, defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
presenter: contact.component.MembersPresenter,
|
||||
props: {
|
||||
kind: 'link',
|
||||
intlTitle: tracker.string.ComponentMembersTitle,
|
||||
intlSearchPh: tracker.string.ComponentMembersSearchPlaceholder,
|
||||
retrieveMembers
|
||||
}
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TargetDatePresenter },
|
||||
{ key: '', presenter: tracker.component.ComponentStatusPresenter },
|
||||
{ key: '', presenter: tracker.component.DeleteComponentPresenter, props: { space } }
|
||||
]}
|
||||
components={resultComponents}
|
||||
{viewMode}
|
||||
query={searchQuery}
|
||||
{viewOptions}
|
||||
on:change={({ detail }) => (resultQuery = detail)}
|
||||
/>
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
padding: 0.5rem 0.75rem 0.5rem 2.25rem;
|
||||
}
|
||||
|
||||
.componentTitle {
|
||||
display: flex;
|
||||
margin-left: 0.25rem;
|
||||
color: var(--content-color);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.itemsContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.65rem 0.75rem 0.65rem 2.25rem;
|
||||
background-color: var(--board-bg-color);
|
||||
border-top: 1px solid var(--divider-color);
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
</style>
|
||||
<div class="flex w-full h-full clear-mins">
|
||||
{#if viewlet}
|
||||
<ComponentsContent {viewlet} query={{ ...resultQuery, ...includedComponentsQuery }} {space} {viewOptions} />
|
||||
{/if}
|
||||
{#if $$slots.aside !== undefined && asideShown}
|
||||
<div class="popupPanel-body__aside flex" class:float={asideFloat} class:shown={asideShown}>
|
||||
<slot name="aside" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -26,14 +26,14 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { ComponentsViewMode } from '../../utils'
|
||||
import { ComponentsFilterMode } from '../../utils'
|
||||
import ComponentBrowser from './ComponentBrowser.svelte'
|
||||
import EditComponent from './EditComponent.svelte'
|
||||
|
||||
export let label: IntlString = tracker.string.Components
|
||||
export let query: DocumentQuery<Component> = {}
|
||||
export let search: string = ''
|
||||
export let mode: ComponentsViewMode = 'all'
|
||||
export let filterMode: ComponentsFilterMode = 'all'
|
||||
|
||||
let componentId: Ref<Component> | undefined
|
||||
let component: Component | undefined
|
||||
@ -68,5 +68,5 @@
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<ComponentBrowser {label} {query} {search} {mode} />
|
||||
<ComponentBrowser {label} {query} {search} {filterMode} />
|
||||
{/if}
|
||||
|
@ -0,0 +1,61 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Component as ViewComponent } from '@hcengineering/ui'
|
||||
import { BuildModelKey, Viewlet, ViewOptions } from '@hcengineering/view'
|
||||
import contact from '@hcengineering/contact'
|
||||
import tracker from '../../plugin'
|
||||
import CreateComponent from './NewComponent.svelte'
|
||||
|
||||
export let viewlet: WithLookup<Viewlet>
|
||||
export let viewOptions: ViewOptions
|
||||
export let query: DocumentQuery<Component> = {}
|
||||
export let space: Ref<Space> | undefined
|
||||
|
||||
const createItemDialog = CreateComponent
|
||||
const createItemLabel = tracker.string.Component
|
||||
const retrieveMembers = (s: Component) => s.members
|
||||
|
||||
function updateConfig (config: (string | BuildModelKey)[]): (string | BuildModelKey)[] {
|
||||
return config.map((it) => {
|
||||
if (typeof it === 'string') {
|
||||
return it
|
||||
}
|
||||
return it.presenter === contact.component.MembersPresenter
|
||||
? { ...it, props: { ...it.props, retrieveMembers } }
|
||||
: it
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if viewlet?.$lookup?.descriptor?.component}
|
||||
<ViewComponent
|
||||
is={viewlet.$lookup.descriptor.component}
|
||||
props={{
|
||||
_class: tracker.class.Component,
|
||||
config: updateConfig(viewlet.config),
|
||||
options: viewlet.options,
|
||||
createItemDialog,
|
||||
createItemLabel,
|
||||
viewOptions,
|
||||
viewOptionsConfig: viewlet.viewOptions?.other,
|
||||
viewlet,
|
||||
space,
|
||||
query
|
||||
}}
|
||||
/>
|
||||
{/if}
|
@ -1,243 +0,0 @@
|
||||
<!--
|
||||
// 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, FindOptions, getObjectValue, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Component, Issue } from '@hcengineering/tracker'
|
||||
import { CheckBox, Spinner, tooltip } from '@hcengineering/ui'
|
||||
import { BuildModelKey } from '@hcengineering/view'
|
||||
import { buildModel, LoadingProps } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let itemsConfig: (BuildModelKey | string)[]
|
||||
export let selectedObjectIds: Doc[] = []
|
||||
export let selectedRowIndex: number | undefined = undefined
|
||||
export let components: Component[] | undefined = undefined
|
||||
export let loadingProps: LoadingProps | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const objectRefs: HTMLElement[] = []
|
||||
|
||||
const baseOptions: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
status: tracker.class.IssueStatus
|
||||
}
|
||||
}
|
||||
|
||||
$: options = { ...baseOptions } as FindOptions<Component>
|
||||
$: selectedObjectIdsSet = new Set<Ref<Doc>>(selectedObjectIds.map((it) => it._id))
|
||||
$: objectRefs.length = components?.length ?? 0
|
||||
|
||||
export const onObjectChecked = (docs: Doc[], value: boolean) => {
|
||||
dispatch('check', { docs, value })
|
||||
}
|
||||
|
||||
const handleRowFocused = (object: Doc) => {
|
||||
dispatch('row-focus', object)
|
||||
}
|
||||
|
||||
export const onElementSelected = (offset: 1 | -1 | 0, docObject?: Doc) => {
|
||||
if (!components) {
|
||||
return
|
||||
}
|
||||
|
||||
let position =
|
||||
(docObject !== undefined ? components?.findIndex((x) => x._id === docObject?._id) : selectedRowIndex) ?? -1
|
||||
|
||||
position += offset
|
||||
|
||||
if (position < 0) {
|
||||
position = 0
|
||||
}
|
||||
|
||||
if (position >= components.length) {
|
||||
position = components.length - 1
|
||||
}
|
||||
|
||||
const objectRef = objectRefs[position]
|
||||
|
||||
selectedRowIndex = position
|
||||
|
||||
handleRowFocused(components[position])
|
||||
|
||||
if (objectRef) {
|
||||
objectRef.scrollIntoView({ behavior: 'auto', block: 'nearest' })
|
||||
}
|
||||
}
|
||||
|
||||
const getLoadingElementsLength = (props: LoadingProps, options?: FindOptions<Doc>) => {
|
||||
if (options?.limit && options?.limit > 0) {
|
||||
return Math.min(options.limit, props.length)
|
||||
}
|
||||
|
||||
return props.length
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await buildModel({ client, _class, keys: itemsConfig, lookup: options.lookup }) then itemModels}
|
||||
<div class="listRoot">
|
||||
{#if components}
|
||||
{#each components as docObject (docObject._id)}
|
||||
<div
|
||||
bind:this={objectRefs[components.findIndex((x) => x === docObject)]}
|
||||
class="listGrid"
|
||||
class:mListGridChecked={selectedObjectIdsSet.has(docObject._id)}
|
||||
class:mListGridFixed={selectedRowIndex === components.findIndex((x) => x === docObject)}
|
||||
class:mListGridSelected={selectedRowIndex === components.findIndex((x) => x === docObject)}
|
||||
on:focus={() => {}}
|
||||
on:mouseover={() => handleRowFocused(docObject)}
|
||||
>
|
||||
<div class="contentWrapper">
|
||||
{#each itemModels as attributeModel, attributeModelIndex}
|
||||
{#if attributeModelIndex === 0}
|
||||
<div class="gridElement">
|
||||
<div
|
||||
class="eListGridCheckBox"
|
||||
use:tooltip={{ direction: 'bottom', label: tracker.string.SelectIssue }}
|
||||
>
|
||||
<CheckBox
|
||||
checked={selectedObjectIdsSet.has(docObject._id)}
|
||||
on:value={(event) => {
|
||||
onObjectChecked([docObject], event.detail)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="iconPresenter">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if attributeModelIndex === 1}
|
||||
<div class="componentPresenter flex-grow">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
<div class="filler" />
|
||||
{:else}
|
||||
<div class="gridElement">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
parentId={docObject._id}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else if loadingProps !== undefined}
|
||||
{#each Array(getLoadingElementsLength(loadingProps, options)) as _, rowIndex}
|
||||
<div class="listGrid" class:fixed={rowIndex === selectedRowIndex}>
|
||||
<div class="contentWrapper">
|
||||
<div class="gridElement">
|
||||
<CheckBox checked={false} />
|
||||
<div class="ml-4">
|
||||
<Spinner size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
.listRoot {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contentWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 1.15rem;
|
||||
}
|
||||
|
||||
.listGrid {
|
||||
width: 100%;
|
||||
height: 3.25rem;
|
||||
color: var(--caption-color);
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
|
||||
&.mListGridChecked {
|
||||
background-color: var(--highlight-select);
|
||||
border-bottom-color: var(--highlight-select-border);
|
||||
|
||||
.eListGridCheckBox {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.mListGridSelected {
|
||||
background-color: var(--highlight-hover);
|
||||
}
|
||||
&.mListGridChecked.mListGridSelected {
|
||||
background-color: var(--highlight-select-hover);
|
||||
}
|
||||
|
||||
.eListGridCheckBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filler {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.gridElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-left: 0.5rem;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.iconPresenter {
|
||||
padding-left: 0.45rem;
|
||||
}
|
||||
|
||||
.componentPresenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: 5.5rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
</style>
|
@ -1,97 +0,0 @@
|
||||
<!--
|
||||
// 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 type { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { BuildModelKey } from '@hcengineering/view'
|
||||
import {
|
||||
ActionContext,
|
||||
focusStore,
|
||||
ListSelectionProvider,
|
||||
SelectDirection,
|
||||
selectionStore,
|
||||
LoadingProps
|
||||
} from '@hcengineering/view-resources'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { onMount } from 'svelte'
|
||||
import ComponentsList from './ComponentsList.svelte'
|
||||
import ComponentTimeline from './ComponentTimeline.svelte'
|
||||
import { Scroller } from '@hcengineering/ui'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let itemsConfig: (BuildModelKey | string)[]
|
||||
export let loadingProps: LoadingProps | undefined = undefined
|
||||
export let components: Component[] = []
|
||||
export let viewMode: 'list' | 'timeline' = 'list'
|
||||
|
||||
const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {
|
||||
if (dir === 'vertical') {
|
||||
if (viewMode === 'list') componentsList.onElementSelected(offset, of)
|
||||
else componentTimeline.onElementSelected(offset, of)
|
||||
}
|
||||
})
|
||||
|
||||
let componentsList: ComponentsList
|
||||
let componentTimeline: ComponentTimeline
|
||||
|
||||
$: if (componentsList !== undefined) {
|
||||
listProvider.update(components)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
;(document.activeElement as HTMLElement)?.blur()
|
||||
})
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
context={{
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if viewMode === 'list'}
|
||||
<Scroller>
|
||||
<ComponentsList
|
||||
bind:this={componentsList}
|
||||
{_class}
|
||||
{itemsConfig}
|
||||
{loadingProps}
|
||||
{components}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
selectedRowIndex={listProvider.current($focusStore)}
|
||||
on:row-focus={(event) => {
|
||||
listProvider.updateFocus(event.detail ?? undefined)
|
||||
}}
|
||||
on:check={(event) => {
|
||||
listProvider.updateSelection(event.detail.docs, event.detail.value)
|
||||
}}
|
||||
/>
|
||||
</Scroller>
|
||||
{:else}
|
||||
<ComponentTimeline
|
||||
bind:this={componentTimeline}
|
||||
{_class}
|
||||
{itemsConfig}
|
||||
{loadingProps}
|
||||
{components}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
selectedRowIndex={listProvider.current($focusStore)}
|
||||
on:row-focus={(event) => {
|
||||
listProvider.updateFocus(event.detail ?? undefined)
|
||||
}}
|
||||
on:check={(event) => {
|
||||
listProvider.updateSelection(event.detail.docs, event.detail.value)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
@ -0,0 +1,89 @@
|
||||
<!--
|
||||
// Copyright © 2023 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, FindOptions, Ref } from '@hcengineering/core'
|
||||
import { BuildModelKey, ViewOptionModel, ViewOptions, ViewQueryOption } from '@hcengineering/view'
|
||||
import {
|
||||
ActionContext,
|
||||
ListSelectionProvider,
|
||||
SelectDirection,
|
||||
selectionStore,
|
||||
focusStore,
|
||||
buildConfigLookup
|
||||
} from '@hcengineering/view-resources'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import Timeline from './Timeline.svelte'
|
||||
|
||||
export let _class: Ref<Class<Component>>
|
||||
export let query: DocumentQuery<Component> = {}
|
||||
export let options: FindOptions<Component> | undefined = undefined
|
||||
export let config: (string | BuildModelKey)[]
|
||||
export let viewOptions: ViewOptions
|
||||
export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
|
||||
|
||||
const selectionProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {
|
||||
if (timeline && dir === 'vertical') {
|
||||
timeline.onElementSelected(offset, of)
|
||||
}
|
||||
})
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const componentsQuery = createQuery()
|
||||
|
||||
let timeline: Timeline | undefined
|
||||
let components: Component[] | undefined
|
||||
let resultOptions: FindOptions<Component> | undefined
|
||||
let resultQuery: DocumentQuery<Component> = query
|
||||
|
||||
// TODO: move to "view-resources" utils
|
||||
async function getResultQuery<T extends Doc> (
|
||||
query: DocumentQuery<T>,
|
||||
viewOptions: ViewOptionModel[] | undefined,
|
||||
viewOptionsStore: ViewOptions
|
||||
): Promise<DocumentQuery<T>> {
|
||||
if (viewOptions === undefined) return query
|
||||
let result = hierarchy.clone(query)
|
||||
for (const viewOption of viewOptions) {
|
||||
if (viewOption.actionTarget !== 'query') continue
|
||||
const queryOption = viewOption as ViewQueryOption
|
||||
const f = await getResource(queryOption.action)
|
||||
result = f(viewOptionsStore[queryOption.key] ?? queryOption.defaultValue, query)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
$: orderBy = viewOptions.orderBy
|
||||
$: lookup = buildConfigLookup(client.getHierarchy(), _class, config, options?.lookup)
|
||||
$: resultOptions = { ...options, lookup, sort: { [orderBy[0]]: orderBy[1] } }
|
||||
$: getResultQuery(query, viewOptionsConfig, viewOptions).then((res) => (resultQuery = { ...res, ...query }))
|
||||
|
||||
$: componentsQuery.query(_class, resultQuery, (result) => (components = result), resultOptions)
|
||||
</script>
|
||||
|
||||
<ActionContext context={{ mode: 'browser' }} />
|
||||
<Timeline
|
||||
bind:this={timeline}
|
||||
{_class}
|
||||
{components}
|
||||
itemsConfig={config}
|
||||
options={resultOptions}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
selectedRowIndex={selectionProvider.current($focusStore)}
|
||||
on:row-focus={(event) => selectionProvider.updateFocus(event.detail ?? undefined)}
|
||||
on:check={(event) => selectionProvider.updateSelection(event.detail.docs, event.detail.value)}
|
||||
/>
|
@ -17,11 +17,9 @@
|
||||
import { Button, ButtonSize, LabelAndProps, showPopup } from '@hcengineering/ui'
|
||||
import { getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import type { Component } from '@hcengineering/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let space: Ref<Space>
|
||||
export let value: Component
|
||||
export let size: ButtonSize = 'medium'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
@ -49,7 +47,7 @@
|
||||
}
|
||||
|
||||
async function removeComponent () {
|
||||
await client.removeDoc(tracker.class.Component, space, value._id)
|
||||
await client.remove(value)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@hcengineering/contact'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { Component, Sprint } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { UsersPopup } from '@hcengineering/contact-resources'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
@ -26,9 +26,8 @@
|
||||
import LeadPopup from './LeadPopup.svelte'
|
||||
|
||||
export let value: Employee | null
|
||||
export let _class: Ref<Class<Component | Sprint>>
|
||||
export let object: Component
|
||||
export let size: IconSize = 'x-small'
|
||||
export let parentId: Ref<Doc>
|
||||
export let defaultClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = false
|
||||
@ -55,15 +54,8 @@
|
||||
return
|
||||
}
|
||||
|
||||
const currentParent = await client.findOne(_class, { _id: parentId as Ref<Component> })
|
||||
|
||||
if (currentParent === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const newLead = result === null ? null : result._id
|
||||
|
||||
await client.update(currentParent, { lead: newLead })
|
||||
await client.update(object, { lead: newLead })
|
||||
}
|
||||
|
||||
const handleLeadEditorOpened = async (event: MouseEvent) => {
|
||||
|
@ -15,9 +15,10 @@
|
||||
<script lang="ts">
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { DueDatePresenter } from '@hcengineering/ui'
|
||||
import { ButtonKind, DueDatePresenter } from '@hcengineering/ui'
|
||||
|
||||
export let value: Component
|
||||
export let kind: ButtonKind = 'list'
|
||||
|
||||
const client = getClient()
|
||||
|
||||
@ -28,4 +29,4 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<DueDatePresenter value={dueDateMs} shouldRender={true} onChange={handleDueDateChanged} />
|
||||
<DueDatePresenter value={dueDateMs} {kind} shouldRender onChange={handleDueDateChanged} />
|
||||
|
@ -15,12 +15,11 @@
|
||||
<script lang="ts">
|
||||
import { Class, Doc, FindOptions, getObjectValue, Ref, Timestamp } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Component, Issue } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { CheckBox, Spinner, Timeline, TimelineRow } from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey } from '@hcengineering/view'
|
||||
import { buildModel, LoadingProps } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import ComponentPresenter from './ComponentPresenter.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
@ -29,18 +28,12 @@
|
||||
export let selectedRowIndex: number | undefined = undefined
|
||||
export let components: Component[] | undefined = undefined
|
||||
export let loadingProps: LoadingProps | undefined = undefined
|
||||
export let options: FindOptions<Component> | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const baseOptions: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
status: tracker.class.IssueStatus
|
||||
}
|
||||
}
|
||||
|
||||
$: options = { ...baseOptions } as FindOptions<Component>
|
||||
$: selectedObjectIdsSet = new Set<Ref<Doc>>(selectedObjectIds.map((it) => it._id))
|
||||
let selectedRows: number[] = []
|
||||
$: if (selectedObjectIdsSet.size > 0 && components !== undefined) {
|
||||
@ -86,7 +79,7 @@
|
||||
}
|
||||
|
||||
let itemModels: AttributeModel[] | undefined = undefined
|
||||
$: buildModel({ client, _class, keys: itemsConfig, lookup: options.lookup }).then((res) => (itemModels = res))
|
||||
$: buildModel({ client, _class, keys: itemsConfig, lookup: options?.lookup }).then((res) => (itemModels = res))
|
||||
|
||||
let lines: TimelineRow[] | undefined
|
||||
$: lines = components?.map((proj) => {
|
||||
@ -125,6 +118,7 @@
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
object={components[row]}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
@ -134,6 +128,7 @@
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
object={components[row]}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
@ -143,7 +138,7 @@
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
parentId={components[row]._id}
|
||||
object={components[row]}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
@ -179,35 +179,3 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
padding: 0.5rem 0.75rem 0.5rem 2.25rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
margin-left: 0.25rem;
|
||||
color: var(--content-color);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.itemsContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.65rem 0.75rem 0.65rem 2.25rem;
|
||||
background-color: var(--board-bg-color);
|
||||
border-top: 1px solid var(--divider-color);
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
|
||||
.buttonWrapper {
|
||||
margin-right: 1px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -30,6 +30,7 @@ import { showPopup } from '@hcengineering/ui'
|
||||
import ComponentEditor from './components/components/ComponentEditor.svelte'
|
||||
import ComponentPresenter from './components/components/ComponentPresenter.svelte'
|
||||
import Components from './components/components/Components.svelte'
|
||||
import ComponentsTimeline from './components/components/ComponentsTimeline.svelte'
|
||||
import ComponentStatusEditor from './components/components/ComponentStatusEditor.svelte'
|
||||
import ComponentStatusPresenter from './components/components/ComponentStatusPresenter.svelte'
|
||||
import ComponentTitlePresenter from './components/components/ComponentTitlePresenter.svelte'
|
||||
@ -381,6 +382,7 @@ export default async (): Promise<Resources> => ({
|
||||
Inbox,
|
||||
MyIssues,
|
||||
Components,
|
||||
ComponentsTimeline,
|
||||
Views,
|
||||
IssuePresenter,
|
||||
ComponentPresenter,
|
||||
|
@ -24,7 +24,8 @@ export default mergeIds(trackerId, tracker, {
|
||||
viewlet: {
|
||||
SubIssues: '' as Ref<Viewlet>,
|
||||
List: '' as Ref<ViewletDescriptor>,
|
||||
Kanban: '' as Ref<ViewletDescriptor>
|
||||
Kanban: '' as Ref<ViewletDescriptor>,
|
||||
Timeline: '' as Ref<ViewletDescriptor>
|
||||
},
|
||||
string: {
|
||||
More: '' as IntlString,
|
||||
@ -319,6 +320,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
Active: '' as AnyComponent,
|
||||
Backlog: '' as AnyComponent,
|
||||
Components: '' as AnyComponent,
|
||||
ComponentsTimeline: '' as AnyComponent,
|
||||
IssuePresenter: '' as AnyComponent,
|
||||
ComponentTitlePresenter: '' as AnyComponent,
|
||||
ComponentPresenter: '' as AnyComponent,
|
||||
|
@ -227,13 +227,13 @@ export const getArraysUnion = (a: any[], b: any[]): any[] => {
|
||||
return Array.from(union)
|
||||
}
|
||||
|
||||
export type ComponentsViewMode = 'all' | 'backlog' | 'active' | 'closed'
|
||||
export type ComponentsFilterMode = 'all' | 'backlog' | 'active' | 'closed'
|
||||
|
||||
export type SprintViewMode = 'all' | 'planned' | 'active' | 'closed'
|
||||
|
||||
export type ScrumRecordViewMode = 'timeReports' | 'objects'
|
||||
|
||||
export const getIncludedComponentStatuses = (mode: ComponentsViewMode): ComponentStatus[] => {
|
||||
export const getIncludedComponentStatuses = (mode: ComponentsFilterMode): ComponentStatus[] => {
|
||||
switch (mode) {
|
||||
case 'all': {
|
||||
return defaultComponentStatuses
|
||||
@ -273,7 +273,7 @@ export const getIncludedSprintStatuses = (mode: SprintViewMode): SprintStatus[]
|
||||
}
|
||||
}
|
||||
|
||||
export const componentsTitleMap: Record<ComponentsViewMode, IntlString> = Object.freeze({
|
||||
export const componentsTitleMap: Record<ComponentsFilterMode, IntlString> = Object.freeze({
|
||||
all: tracker.string.AllComponents,
|
||||
backlog: tracker.string.BacklogComponents,
|
||||
active: tracker.string.ActiveComponents,
|
||||
|
Loading…
Reference in New Issue
Block a user