mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-11 20:27:43 +02:00
[UBER-82] Update components (#3227)
Signed-off-by: Ruslan Bayandinov <wazsone@ya.ru>
This commit is contained in:
@@ -28,18 +28,9 @@
|
||||
ViewletSettingButton,
|
||||
FilterBar
|
||||
} from '@hcengineering/view-resources'
|
||||
import {
|
||||
Button,
|
||||
IconAdd,
|
||||
Label,
|
||||
SearchEdit,
|
||||
TabItem,
|
||||
TabList,
|
||||
resolvedLocationStore,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { Button, IconAdd, Label, SearchEdit, TabList, resolvedLocationStore, showPopup } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { ComponentsFilterMode, componentsTitleMap, getIncludedComponentStatuses } from '../../utils'
|
||||
import { ComponentsFilterMode, componentsTitleMap } from '../../utils'
|
||||
import tracker from '../../plugin'
|
||||
import ComponentsContent from './ComponentsContent.svelte'
|
||||
import NewComponent from './NewComponent.svelte'
|
||||
@@ -53,20 +44,12 @@
|
||||
const viewletQuery = createQuery()
|
||||
const space = typeof query.space === 'string' ? query.space : tracker.project.DefaultProject
|
||||
|
||||
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
|
||||
@@ -74,19 +57,11 @@
|
||||
let docWidth: number
|
||||
let docSize = false
|
||||
|
||||
function handleFilterModeChanged (newMode: ComponentsFilterMode) {
|
||||
if (newMode !== filterMode) {
|
||||
filterMode = newMode
|
||||
}
|
||||
}
|
||||
|
||||
function showCreateDialog () {
|
||||
showPopup(NewComponent, { space, targetElement: null }, 'top')
|
||||
}
|
||||
|
||||
$: title = componentsTitleMap[filterMode]
|
||||
$: includedComponentStatuses = getIncludedComponentStatuses(filterMode)
|
||||
$: includedComponentsQuery = { status: { $in: includedComponentStatuses } }
|
||||
$: searchQuery = search === '' ? { ...query } : { ...query, $search: search }
|
||||
$: resultQuery = { ...searchQuery }
|
||||
|
||||
@@ -146,9 +121,6 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac-header tabs-start full divide">
|
||||
<TabList items={filterModeList} selected={filterMode} kind={'plain'} on:select={({ detail }) => detail?.action?.()} />
|
||||
</div>
|
||||
|
||||
<FilterBar
|
||||
_class={tracker.class.Component}
|
||||
@@ -159,7 +131,7 @@
|
||||
|
||||
<div class="flex w-full h-full clear-mins">
|
||||
{#if viewlet}
|
||||
<ComponentsContent {viewlet} query={{ ...resultQuery, ...includedComponentsQuery }} {space} {viewOptions} />
|
||||
<ComponentsContent {viewlet} query={{ ...resultQuery }} {space} {viewOptions} />
|
||||
{/if}
|
||||
{#if $$slots.aside !== undefined && asideShown}
|
||||
<div class="popupPanel-body__aside flex" class:float={asideFloat} class:shown={asideShown}>
|
||||
|
||||
@@ -15,42 +15,31 @@
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Icon, getCurrentResolvedLocation, navigate, tooltip } from '@hcengineering/ui'
|
||||
import { Icon, tooltip } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import view from '@hcengineering/view'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
|
||||
export let value: WithLookup<Component>
|
||||
export let shouldShowAvatar = true
|
||||
export let onClick: (() => void) | undefined = undefined
|
||||
export let disabled = false
|
||||
export let inline: boolean = false
|
||||
|
||||
function navigateToComponent () {
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
if (onClick) {
|
||||
onClick()
|
||||
}
|
||||
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.path[4] = 'components'
|
||||
loc.path[5] = value._id
|
||||
loc.path.length = 6
|
||||
loc.fragment = undefined
|
||||
navigate(loc)
|
||||
}
|
||||
export let noUnderline = false
|
||||
export let kind: 'list' | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex-presenter" class:inline-presenter={inline} on:click={navigateToComponent}>
|
||||
{#if !inline && shouldShowAvatar}
|
||||
<div class="icon" use:tooltip={{ label: tracker.string.Component }}>
|
||||
<Icon icon={tracker.icon.Component} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<span title={value.label} class="overflow-label label" class:no-underline={disabled}>
|
||||
{value.label}
|
||||
<DocNavLink object={value} {onClick} {disabled} {noUnderline} {inline} component={view.component.EditDoc}>
|
||||
<span class="flex-presenter" class:inline class:list={kind === 'list'}>
|
||||
{#if !inline && shouldShowAvatar}
|
||||
<div class="icon" use:tooltip={{ label: tracker.string.Component }}>
|
||||
<Icon icon={tracker.icon.Component} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<span title={value.label} class="label nowrap">
|
||||
{value.label}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import ComponentStatusPresenter from './ComponentStatusPresenter.svelte'
|
||||
|
||||
export let object: Component
|
||||
</script>
|
||||
|
||||
<ComponentStatusPresenter value={object} shouldShowLabel />
|
||||
@@ -1,54 +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 { Component, ComponentStatus } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
import ComponentStatusSelector from './ComponentStatusSelector.svelte'
|
||||
|
||||
export let value: Component
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = false
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'large'
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = '100%'
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const handleComponentStatusChanged = async (newStatus: ComponentStatus | undefined) => {
|
||||
if (!isEditable || newStatus === undefined || value.status === newStatus) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.update(value, { status: newStatus })
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<ComponentStatusSelector
|
||||
{kind}
|
||||
{size}
|
||||
{width}
|
||||
{justify}
|
||||
{isEditable}
|
||||
{shouldShowLabel}
|
||||
showTooltip={isEditable ? { label: tracker.string.SetStatus } : undefined}
|
||||
selectedComponentStatus={value.status}
|
||||
onComponentStatusChange={handleComponentStatusChanged}
|
||||
/>
|
||||
{/if}
|
||||
@@ -1,83 +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 { ComponentStatus } from '@hcengineering/tracker'
|
||||
import { Button, showPopup, SelectPopup, eventToHTMLElement, Icon } from '@hcengineering/ui'
|
||||
import type { ButtonKind, ButtonSize, LabelAndProps } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { defaultComponentStatuses, componentStatusAssets } from '../../utils'
|
||||
|
||||
export let selectedComponentStatus: ComponentStatus | undefined
|
||||
export let shouldShowLabel: boolean = true
|
||||
export let onComponentStatusChange: ((newComponentStatus: ComponentStatus | undefined) => void) | undefined =
|
||||
undefined
|
||||
export let isEditable: boolean = true
|
||||
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = 'min-content'
|
||||
export let showTooltip: LabelAndProps | undefined = undefined
|
||||
|
||||
$: selectedStatusIcon = selectedComponentStatus
|
||||
? componentStatusAssets[selectedComponentStatus].icon
|
||||
: tracker.icon.ComponentStatusBacklog
|
||||
|
||||
$: selectedStatusLabel = shouldShowLabel
|
||||
? selectedComponentStatus
|
||||
? componentStatusAssets[selectedComponentStatus].label
|
||||
: tracker.string.Backlog
|
||||
: undefined
|
||||
|
||||
$: statusesInfo = defaultComponentStatuses.map((s) => ({
|
||||
id: s,
|
||||
isSelected: componentStatusAssets[s].label === selectedStatusLabel,
|
||||
...componentStatusAssets[s]
|
||||
}))
|
||||
|
||||
const handleComponentStatusEditorOpened = (event: MouseEvent) => {
|
||||
if (!isEditable) {
|
||||
return
|
||||
}
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{ value: statusesInfo, placeholder: tracker.string.SetStatus, searchable: true },
|
||||
eventToHTMLElement(event),
|
||||
onComponentStatusChange
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if kind === 'list'}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="flex-no-shrink clear-mins cursor-pointer content-pointer-events-none"
|
||||
on:click={handleComponentStatusEditorOpened}
|
||||
>
|
||||
<Icon icon={selectedStatusIcon} {size} />
|
||||
</div>
|
||||
{:else}
|
||||
<Button
|
||||
{kind}
|
||||
{size}
|
||||
{width}
|
||||
{justify}
|
||||
disabled={!isEditable}
|
||||
icon={selectedStatusIcon}
|
||||
label={selectedStatusLabel}
|
||||
{showTooltip}
|
||||
on:click={handleComponentStatusEditorOpened}
|
||||
/>
|
||||
{/if}
|
||||
@@ -13,60 +13,17 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { DocumentQuery } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import {
|
||||
closePopup,
|
||||
closeTooltip,
|
||||
getCurrentResolvedLocation,
|
||||
navigate,
|
||||
resolvedLocationStore
|
||||
} from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
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 filterMode: ComponentsFilterMode = 'all'
|
||||
|
||||
let componentId: Ref<Component> | undefined
|
||||
let component: Component | undefined
|
||||
|
||||
onDestroy(
|
||||
resolvedLocationStore.subscribe(async (loc) => {
|
||||
closeTooltip()
|
||||
closePopup()
|
||||
|
||||
componentId = loc.path[5] as Ref<Component>
|
||||
})
|
||||
)
|
||||
|
||||
const componentQuery = createQuery()
|
||||
$: if (componentId !== undefined) {
|
||||
componentQuery.query(tracker.class.Component, { _id: componentId }, (result) => {
|
||||
component = result.shift()
|
||||
})
|
||||
} else {
|
||||
componentQuery.unsubscribe()
|
||||
component = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if component}
|
||||
<EditComponent
|
||||
{component}
|
||||
on:component={(evt) => {
|
||||
const loc = getCurrentResolvedLocation()
|
||||
loc.path[5] = evt.detail
|
||||
navigate(loc)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<ComponentBrowser {label} {query} {search} {filterMode} />
|
||||
{/if}
|
||||
<ComponentBrowser {label} {query} {search} {filterMode} />
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
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 { Viewlet, ViewOptions } from '@hcengineering/view'
|
||||
import tracker from '../../plugin'
|
||||
import CreateComponent from './NewComponent.svelte'
|
||||
|
||||
@@ -28,18 +27,6 @@
|
||||
|
||||
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}
|
||||
@@ -47,7 +34,7 @@
|
||||
is={viewlet.$lookup.descriptor.component}
|
||||
props={{
|
||||
_class: tracker.class.Component,
|
||||
config: updateConfig(viewlet.config),
|
||||
config: viewlet.config,
|
||||
options: viewlet.options,
|
||||
createItemDialog,
|
||||
createItemLabel,
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
<!--
|
||||
// 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)}
|
||||
/>
|
||||
@@ -1,65 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Button, EditBox, Icon, showPopup } from '@hcengineering/ui'
|
||||
import { DocAttributeBar } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { activeComponent } from '../../issues'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesView from '../issues/IssuesView.svelte'
|
||||
import ComponentPopup from './ComponentPopup.svelte'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let component: Component
|
||||
export let object: Component
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function change (field: string, value: any) {
|
||||
await client.update(component, { [field]: value })
|
||||
}
|
||||
function selectComponent (evt: MouseEvent): void {
|
||||
showPopup(ComponentPopup, { _class: tracker.class.Component }, evt.target as HTMLElement, (value) => {
|
||||
if (value != null) {
|
||||
component = value
|
||||
dispatch('component', component._id)
|
||||
}
|
||||
})
|
||||
let oldLabel = ''
|
||||
let rawLabel = ''
|
||||
|
||||
function change<K extends keyof Component> (field: K, value: Component[K]) {
|
||||
client.update(object, { [field]: value })
|
||||
}
|
||||
|
||||
$: $activeComponent = component?._id
|
||||
$: if (oldLabel !== object.label) {
|
||||
oldLabel = object.label
|
||||
rawLabel = object.label
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
$activeComponent = undefined
|
||||
})
|
||||
onMount(() => dispatch('open', { ignoreKeys: ['label'] }))
|
||||
</script>
|
||||
|
||||
<IssuesView
|
||||
query={{ component: component._id, space: component.space }}
|
||||
space={component.space}
|
||||
label={component.label}
|
||||
>
|
||||
<svelte:fragment slot="label_selector">
|
||||
<Button size={'small'} kind={'link'} on:click={selectComponent}>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="ac-header__icon"><Icon icon={tracker.icon.Component} size={'small'} /></div>
|
||||
<span class="ac-header__title">{component.label}</span>
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="aside">
|
||||
<div class="popupPanel-body__aside-content">
|
||||
<EditBox kind={'large-style'} bind:value={component.label} on:change={() => change('label', component.label)} />
|
||||
<div class="mt-2">
|
||||
<StyledTextBox
|
||||
alwaysEdit={true}
|
||||
showButtons={false}
|
||||
placeholder={tracker.string.Description}
|
||||
content={component.description ?? ''}
|
||||
on:value={(evt) => change('description', evt.detail)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DocAttributeBar object={component} mixins={[]} ignoreKeys={['icon', 'label', 'description']} />
|
||||
</svelte:fragment>
|
||||
</IssuesView>
|
||||
<EditBox
|
||||
bind:value={rawLabel}
|
||||
placeholder={tracker.string.Component}
|
||||
kind="large-style"
|
||||
focusable
|
||||
on:blur={() => {
|
||||
const trimmedLabel = rawLabel.trim()
|
||||
|
||||
if (trimmedLabel.length === 0) {
|
||||
rawLabel = oldLabel
|
||||
} else if (trimmedLabel !== object.label) {
|
||||
change('label', trimmedLabel)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
import { Data, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { Card, getClient, SpaceSelector } from '@hcengineering/presentation'
|
||||
import { EmployeeBox, UserBoxList } from '@hcengineering/contact-resources'
|
||||
import { Component, ComponentStatus, Project } from '@hcengineering/tracker'
|
||||
import { DatePresenter, EditBox } from '@hcengineering/ui'
|
||||
import { EmployeeBox } from '@hcengineering/contact-resources'
|
||||
import { Component, Project } from '@hcengineering/tracker'
|
||||
import { EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import ComponentStatusSelector from './ComponentStatusSelector.svelte'
|
||||
import { StyledTextArea } from '@hcengineering/text-editor'
|
||||
|
||||
export let space: Ref<Project>
|
||||
@@ -31,26 +30,14 @@
|
||||
const object: Data<Component> = {
|
||||
label: '' as IntlString,
|
||||
description: '',
|
||||
status: ComponentStatus.Backlog,
|
||||
lead: null,
|
||||
members: [],
|
||||
comments: 0,
|
||||
attachments: 0,
|
||||
startDate: null,
|
||||
targetDate: null
|
||||
attachments: 0
|
||||
}
|
||||
|
||||
async function onSave () {
|
||||
await client.createDoc(tracker.class.Component, space, object)
|
||||
}
|
||||
|
||||
const handleComponentStatusChanged = (newComponentStatus: ComponentStatus | undefined) => {
|
||||
if (newComponentStatus === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
object.status = newComponentStatus
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
@@ -77,12 +64,6 @@
|
||||
emphasized
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
<ComponentStatusSelector
|
||||
selectedComponentStatus={object.status}
|
||||
onComponentStatusChange={handleComponentStatusChanged}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
/>
|
||||
<EmployeeBox
|
||||
label={tracker.string.ComponentLead}
|
||||
placeholder={tracker.string.AssignTo}
|
||||
@@ -93,26 +74,5 @@
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
/>
|
||||
<UserBoxList
|
||||
bind:items={object.members}
|
||||
label={tracker.string.ComponentMembersSearchPlaceholder}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
/>
|
||||
<!-- TODO: add labels after customize IssueNeedsToBeCompletedByThisDate -->
|
||||
<DatePresenter
|
||||
bind:value={object.startDate}
|
||||
labelNull={tracker.string.StartDate}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
editable
|
||||
/>
|
||||
<DatePresenter
|
||||
bind:value={object.targetDate}
|
||||
labelNull={tracker.string.TargetDate}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
editable
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
@@ -1,32 +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 { Component } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ButtonKind, DueDatePresenter } from '@hcengineering/ui'
|
||||
|
||||
export let value: Component
|
||||
export let kind: ButtonKind = 'list'
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: dueDateMs = value.targetDate
|
||||
|
||||
const handleDueDateChanged = async (newDate: number | null) => {
|
||||
await client.update(value, { targetDate: newDate })
|
||||
}
|
||||
</script>
|
||||
|
||||
<DueDatePresenter value={dueDateMs} {kind} shouldRender onChange={handleDueDateChanged} />
|
||||
@@ -1,490 +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, Timestamp } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
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 ComponentPresenter from './ComponentPresenter.svelte'
|
||||
|
||||
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
|
||||
export let options: FindOptions<Component> | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: selectedObjectIdsSet = new Set<Ref<Doc>>(selectedObjectIds.map((it) => it._id))
|
||||
let selectedRows: number[] = []
|
||||
$: if (selectedObjectIdsSet.size > 0 && components !== undefined) {
|
||||
const tRows: number[] = []
|
||||
selectedObjectIdsSet.forEach((it) => {
|
||||
const index = components?.findIndex((f) => f._id === it)
|
||||
if (index !== undefined) tRows.push(index)
|
||||
})
|
||||
selectedRows = tRows
|
||||
} else selectedRows = []
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
let itemModels: AttributeModel[] | undefined = undefined
|
||||
$: buildModel({ client, _class, keys: itemsConfig, lookup: options?.lookup }).then((res) => (itemModels = res))
|
||||
|
||||
let lines: TimelineRow[] | undefined
|
||||
$: lines = components?.map((proj) => {
|
||||
const tR: TimelineRow = { items: [] }
|
||||
tR.items = [
|
||||
{
|
||||
presenter: ComponentPresenter,
|
||||
props: { value: proj },
|
||||
startDate: proj.startDate as Timestamp,
|
||||
targetDate: proj.targetDate as Timestamp
|
||||
}
|
||||
]
|
||||
return tR
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if components && itemModels && lines}
|
||||
<Timeline
|
||||
{lines}
|
||||
{selectedRows}
|
||||
selectedRow={selectedRowIndex}
|
||||
on:row-focus={(ev) => {
|
||||
if (ev.detail !== undefined && components !== undefined) handleRowFocused(components[ev.detail])
|
||||
}}
|
||||
on:check={(ev) => {
|
||||
if (ev.detail !== undefined && components !== undefined) {
|
||||
onObjectChecked([components[ev.detail.row]], ev.detail.value)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svelte:fragment let:row>
|
||||
{#each itemModels as attributeModel, attributeModelIndex}
|
||||
{#if attributeModelIndex === 0}
|
||||
<div class="gridElement">
|
||||
<div class="iconPresenter">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
object={components[row]}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if attributeModelIndex === 1}
|
||||
<div class="componentPresenter flex-grow">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
object={components[row]}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
<div class="filler" />
|
||||
{:else}
|
||||
<div class="gridElement">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
object={components[row]}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</Timeline>
|
||||
{: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}
|
||||
|
||||
<style lang="scss">
|
||||
.timeline-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 4rem;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
.timeline-header__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 0 2.25rem;
|
||||
height: 100%;
|
||||
background-color: var(--body-accent);
|
||||
box-shadow: var(--accent-shadow);
|
||||
// z-index: 2;
|
||||
}
|
||||
.timeline-header__time {
|
||||
// overflow: hidden;
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
background-color: var(--body-color);
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 0, 0, 0) 0,
|
||||
rgba(0, 0, 0, 1) 2rem,
|
||||
rgba(0, 0, 0, 1) calc(100% - 2rem),
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
|
||||
&-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
will-change: transform;
|
||||
|
||||
.day,
|
||||
.month {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.month {
|
||||
width: max-content;
|
||||
top: 0.25rem;
|
||||
font-size: 1rem;
|
||||
color: var(--accent-color);
|
||||
|
||||
&:first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
.day {
|
||||
bottom: 0.5rem;
|
||||
font-size: 1rem;
|
||||
color: var(--content-color);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.cursor {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-bottom: 1px;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
bottom: 0.375rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background-color: var(--primary-bg-color);
|
||||
border-radius: 50%;
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.todayMarker,
|
||||
.monthMarker {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.monthMarker {
|
||||
border-left: 1px dashed var(--highlight-select);
|
||||
}
|
||||
.todayMarker {
|
||||
border-left: 1px solid var(--primary-bg-color);
|
||||
}
|
||||
|
||||
.timeline-background__headers,
|
||||
.timeline-background__viewbox,
|
||||
.timeline-foreground__viewbox {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 4rem;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
.timeline-background__headers {
|
||||
left: 0;
|
||||
background-color: var(--body-accent);
|
||||
}
|
||||
.timeline-background__viewbox,
|
||||
.timeline-foreground__viewbox {
|
||||
right: 0;
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 0, 0, 0) 0,
|
||||
rgba(0, 0, 0, 1) 2rem,
|
||||
rgba(0, 0, 0, 1) calc(100% - 2rem),
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
}
|
||||
.timeline-foreground__viewbox {
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.timeline-splitter,
|
||||
.timeline-splitter::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.timeline-splitter {
|
||||
width: 1px;
|
||||
background-color: var(--divider-color);
|
||||
cursor: col-resize;
|
||||
z-index: 3;
|
||||
transition-property: width, background-color;
|
||||
transition-timing-function: var(--timing-main);
|
||||
transition-duration: 0.1s;
|
||||
transition-delay: 0s;
|
||||
|
||||
&:hover {
|
||||
width: 3px;
|
||||
background-color: var(--button-border-hover);
|
||||
transition-duration: 0.15s;
|
||||
transition-delay: 0.3s;
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
width: 10px;
|
||||
left: 50%;
|
||||
}
|
||||
&.moving {
|
||||
width: 2px;
|
||||
background-color: var(--primary-edit-border-color);
|
||||
transition-duration: 0.1s;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
}
|
||||
|
||||
.headerWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 1.15rem;
|
||||
// border-bottom: 1px solid var(--accent-bg-color);
|
||||
}
|
||||
.contentWrapper {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 0, 0, 0) 0,
|
||||
rgba(0, 0, 0, 1) 2rem,
|
||||
rgba(0, 0, 0, 1) calc(100% - 2rem),
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
|
||||
&.nullRow {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.timeline-wrapped_content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.timeline-action__button,
|
||||
.project-item {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
box-shadow: var(--button-shadow);
|
||||
}
|
||||
.project-item {
|
||||
top: 0.25rem;
|
||||
bottom: 0.25rem;
|
||||
background-color: var(--button-bg-color);
|
||||
border: 1px solid var(--button-border-color);
|
||||
border-radius: 0.75rem;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-bg-hover);
|
||||
border-color: var(--button-border-hover);
|
||||
}
|
||||
&.noTarget {
|
||||
mask-image: linear-gradient(to left, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 1) 2rem);
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.project-presenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.space {
|
||||
flex-shrink: 0;
|
||||
width: 0.25rem;
|
||||
min-width: 0.25rem;
|
||||
max-width: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.timeline-action__button {
|
||||
top: 0.625rem;
|
||||
bottom: 0.625rem;
|
||||
width: 2rem;
|
||||
color: var(--content-color);
|
||||
background-color: var(--button-bg-color);
|
||||
border: 1px solid var(--button-border-color);
|
||||
border-radius: 0.5rem;
|
||||
|
||||
&:hover {
|
||||
color: var(--accent-color);
|
||||
background-color: var(--button-bg-hover);
|
||||
border-color: var(--button-border-hover);
|
||||
}
|
||||
|
||||
&.left {
|
||||
left: 1rem;
|
||||
}
|
||||
&.right {
|
||||
right: 1rem;
|
||||
}
|
||||
&.add {
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.listGrid {
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
height: 3.25rem;
|
||||
min-height: 0;
|
||||
color: var(--caption-color);
|
||||
z-index: 2;
|
||||
|
||||
&.mListGridChecked {
|
||||
.headerWrapper {
|
||||
background-color: var(--highlight-select);
|
||||
}
|
||||
.contentWrapper {
|
||||
background-color: var(--trans-content-05);
|
||||
}
|
||||
.eListGridCheckBox {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.mListGridSelected {
|
||||
.headerWrapper {
|
||||
background-color: var(--highlight-select-hover);
|
||||
}
|
||||
.contentWrapper {
|
||||
background-color: var(--trans-content-10);
|
||||
}
|
||||
}
|
||||
|
||||
.eListGridCheckBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .eListGridCheckBox {
|
||||
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;
|
||||
}
|
||||
}
|
||||
.componentPresenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: 5.5rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -52,11 +52,10 @@
|
||||
if (issue.component) {
|
||||
const component = await client.findOne(tracker.class.Component, { _id: issue.component })
|
||||
projectLead = component?.lead || undefined
|
||||
projectMembers = component?.members || []
|
||||
} else {
|
||||
projectLead = undefined
|
||||
projectMembers = []
|
||||
}
|
||||
projectMembers = []
|
||||
if (hasSpace(issue)) {
|
||||
const project = await client.findOne(tracker.class.Project, { _id: issue.space })
|
||||
if (project !== undefined) {
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { getCurrentAccount } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Component, Issue, Project } from '@hcengineering/tracker'
|
||||
import { Button, Label } from '@hcengineering/ui'
|
||||
@@ -41,12 +39,8 @@
|
||||
async function createMissingComponent (cur: Component): Promise<void> {
|
||||
await client.createDoc(cur._class, targetProject._id, {
|
||||
label: cur.label,
|
||||
members: [(getCurrentAccount() as EmployeeAccount).employee],
|
||||
status: cur.status,
|
||||
startDate: cur.startDate,
|
||||
attachments: 0,
|
||||
description: cur.description,
|
||||
targetDate: cur.targetDate,
|
||||
comments: 0,
|
||||
lead: cur.lead
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user