UBERF-9483 Task type improvements (#8015)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2025-02-17 10:51:48 +07:00
committed by GitHub
parent 2fe638297c
commit 9072b4cbba
17 changed files with 213 additions and 89 deletions
@@ -1,31 +0,0 @@
<!--
// Copyright © 2024 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 { ButtonIcon, IconAdd, IconFolder, Label } from '@hcengineering/ui'
import { ProjectType, ProjectTypeDescriptor } from '@hcengineering/task'
import task from '../../plugin'
export let type: ProjectType | undefined
export let descriptor: ProjectTypeDescriptor | undefined
export let disabled: boolean = true
</script>
{#if descriptor !== undefined}
<div class="hulyTableAttr-header font-medium-12">
<IconFolder size="small" />
<span><Label label={task.string.Collections} /></span>
<ButtonIcon kind="primary" icon={IconAdd} size="small" dataId={'btnAdd'} {disabled} on:click={() => {}} />
</div>
{/if}
@@ -1,10 +1,13 @@
<script lang="ts">
import { Class, Doc, Ref, toIdMap } from '@hcengineering/core'
import { getEmbeddedLabel } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import task, { ProjectType, TaskType } from '@hcengineering/task'
import { ButtonKind, ButtonSize, DropdownLabels } from '@hcengineering/ui'
import { ButtonKind, ButtonSize, DropdownLabelsIntl, type DropdownIntlItem } from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import { selectedTaskTypeStore, taskTypeStore } from '../..'
import TaskTypeListPresenter from './TaskTypeListPresenter.svelte'
import TaskTypeIcon from './TaskTypeIcon.svelte'
export let value: Ref<TaskType> | undefined
export let projectType: Ref<ProjectType> | undefined
@@ -28,13 +31,18 @@
(taskTypeDescriptors.get(it.descriptor)?.allowCreate ?? false) &&
(baseClass === undefined || client.getHierarchy().isDerived(it.targetClass, baseClass))
)
.map((it) => ({ id: it._id, label: it.name }))
.map((it) => ({
id: it._id,
label: getEmbeddedLabel(it.name),
icon: TaskTypeIcon,
iconProps: { value: it }
})) as DropdownIntlItem[]
$: if (
(value === undefined && items.length > 0) ||
(items.length > 0 && items.find((it) => it.id === value) === undefined)
) {
value = items[0].id
value = items[0].id as Ref<TaskType>
change()
}
@@ -50,16 +58,17 @@
</script>
{#if projectType !== undefined && (items.length > 1 || showAlways)}
<DropdownLabels
<DropdownLabelsIntl
{focusIndex}
{kind}
{size}
{items}
{justify}
{width}
icon={TaskTypeIcon}
iconProps={value !== undefined ? { value: $taskTypeStore.get(value) } : {}}
dataId={'btnSelectTaskType'}
bind:selected={value}
enableSearch={false}
on:selected={change}
/>
{/if}
@@ -14,28 +14,31 @@
// limitations under the License.
-->
<script lang="ts">
import { AttributeEditor, createQuery, getClient } from '@hcengineering/presentation'
import task, { ProjectType, TaskType, calculateStatuses, findStatusAttr } from '@hcengineering/task'
import { Ref, SortingOrder, Status } from '@hcengineering/core'
import { Asset, getEmbeddedLabel } from '@hcengineering/platform'
import {
Label,
showPopup,
ButtonIcon,
ModernButton,
IconSquareExpand,
IconAdd,
Icon,
Scroller
} from '@hcengineering/ui'
import { IconPicker, statusStore } from '@hcengineering/view-resources'
import { Asset, getEmbeddedLabel, getResource } from '@hcengineering/platform'
import { AttributeEditor, MessageBox, createQuery, getClient } from '@hcengineering/presentation'
import { ClassAttributes, settingsStore } from '@hcengineering/setting-resources'
import task, { ProjectType, TaskType, calculateStatuses, findStatusAttr } from '@hcengineering/task'
import {
ButtonIcon,
Icon,
IconAdd,
IconDelete,
IconSquareExpand,
Label,
ModernButton,
Scroller,
getCurrentLocation,
navigate,
showPopup
} from '@hcengineering/ui'
import { IconPicker, deleteObjects, statusStore } from '@hcengineering/view-resources'
import { taskTypeStore } from '../..'
import plugin from '../../plugin'
import StatesProjectEditor from '../state/StatesProjectEditor.svelte'
import TaskTypeKindEditor from '../taskTypes/TaskTypeKindEditor.svelte'
import TaskTypeRefEditor from '../taskTypes/TaskTypeRefEditor.svelte'
import TaskTypeIcon from './TaskTypeIcon.svelte'
import plugin from '../../plugin'
export let spaceType: ProjectType
export let objectId: Ref<TaskType>
@@ -65,13 +68,15 @@
$: states = (taskType?.statuses.map((p) => $statusStore.byId.get(p)).filter((p) => p !== undefined) as Status[]) ?? []
let tasksCounter: number = 0
let loading: boolean = true
const tasksCounterQuery = createQuery()
$: if (taskType !== undefined) {
tasksCounterQuery.query(
loading = tasksCounterQuery.query(
task.class.Task,
{ kind: taskType._id },
(res) => {
tasksCounter = res.total
loading = false
},
{
total: true,
@@ -87,6 +92,7 @@
if (readonly) {
return
}
// TODO: Be aware icon equal to descriptor one will not be shown in lists.
const icons: Asset[] = [descriptor[0].icon]
showPopup(
@@ -125,6 +131,41 @@
}
}
}
$: canDelete = !loading && tasksCounter === 0
async function handleDelete (): Promise<void> {
if (!canDelete || readonly || taskType == null) {
return
}
showPopup(MessageBox, {
label: plugin.string.Delete,
message: plugin.string.Delete,
action: async () => {
if (taskType == null) {
return
}
await deleteObjects(client, [taskType])
const loc = getCurrentLocation()
loc.path.length -= 2
navigate(loc)
}
})
}
async function showIssuesOfTaskType (): Promise<void> {
if (taskType == null) return
const descriptor = client
.getModel()
.findAllSync(task.class.TaskTypeDescriptor, { _id: taskType?.descriptor })
.shift()
if (descriptor?.openTasks !== undefined) {
const f = await getResource(descriptor.openTasks)
await f?.(taskType)
}
}
</script>
{#if taskType !== undefined}
@@ -157,15 +198,29 @@
/>
{/if}
</div>
<ModernButton
icon={IconSquareExpand}
label={plugin.string.CountTasks}
labelParams={{ count: tasksCounter }}
disabled={tasksCounter === 0}
kind={'tertiary'}
size={'medium'}
hasMenu
/>
<div class="flex-row">
<ModernButton
icon={IconSquareExpand}
label={plugin.string.CountTasks}
labelParams={{ count: tasksCounter }}
disabled={tasksCounter === 0}
kind={'tertiary'}
size={'medium'}
hasMenu
on:click={() => {
showIssuesOfTaskType()
}}
/>
{#if canDelete}
<ButtonIcon
icon={IconDelete}
size="small"
kind="secondary"
disabled={readonly}
on:click={handleDelete}
/>
{/if}
</div>
</div>
<div class="name" class:editable={!readonly}>
@@ -0,0 +1,35 @@
<!--
// Copyright © 2025 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 { Ref } from '@hcengineering/core'
import type { TaskType } from '@hcengineering/task'
import view from '@hcengineering/view'
import { taskTypeStore } from '../../'
import TaskTypeIcon from './TaskTypeIcon.svelte'
import { getClient } from '@hcengineering/presentation'
import task from '@hcengineering/task'
export let value: TaskType | Ref<TaskType> | undefined
$: _value = typeof value === 'string' ? $taskTypeStore.get(value) : value
$: descriptor = getClient().getModel().findAllSync(task.class.TaskTypeDescriptor, { _id: _value?.descriptor }).shift()
$: visible = !(_value?.icon === descriptor?.icon)
</script>
{#if _value !== undefined && visible}
<span class="label flex-row-center gap-1 ml-3">
<TaskTypeIcon value={_value} size={'small'} />
</span>
{/if}