[UBER-426] Add milestone status editor (#3391)

Signed-off-by: Sergei Ogorelkov <sergei.ogorelkov@icloud.com>
This commit is contained in:
Sergei Ogorelkov 2023-06-07 18:12:04 +04:00 committed by GitHub
parent 5eaaa3f8a6
commit 7536a8f51e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 116 deletions

View File

@ -1488,6 +1488,10 @@ export function createModel (builder: Builder): void {
presenter: tracker.component.MilestoneStatusPresenter
})
builder.mixin(tracker.class.TypeMilestoneStatus, core.class.Class, view.mixin.AttributeEditor, {
inlineEditor: tracker.component.MilestoneStatusEditor
})
builder.mixin(tracker.class.TypeMilestoneStatus, core.class.Class, view.mixin.AttributeFilter, {
component: view.component.ValueFilter
})

View File

@ -0,0 +1,87 @@
<!--
// 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 { createEventDispatcher } from 'svelte'
import { Data } from '@hcengineering/core'
import { Milestone } from '@hcengineering/tracker'
import { getClient } from '@hcengineering/presentation'
import { Button, ButtonKind, ButtonSize, Icon, SelectPopup, eventToHTMLElement, showPopup } from '@hcengineering/ui'
import { defaultMilestoneStatuses, milestoneStatusAssets } from '../../utils'
import tracker from '../../plugin'
export let value: Milestone['status'] | undefined
export let object: Milestone | Data<Milestone>
export let kind: ButtonKind = 'link'
export let size: ButtonSize = 'large'
export let justify: 'left' | 'center' = 'left'
export let width: string | undefined = undefined
export let disabled = false
const dispatch = createEventDispatcher()
const client = getClient()
function handlePopupOpen (event: MouseEvent) {
showPopup(
SelectPopup,
{ value: itemsInfo, placeholder: tracker.string.SetStatus },
eventToHTMLElement(event),
changeStatus
)
}
async function changeStatus (newStatus: Milestone['status'] | null | undefined) {
if (disabled || newStatus == null || value === newStatus) {
return
}
value = newStatus
dispatch('change', value)
if ('_id' in object) {
await client.update(object, { status: newStatus })
}
}
$: itemsInfo = defaultMilestoneStatuses.map((status) => ({
id: status,
isSelected: value === status,
...milestoneStatusAssets[status]
}))
$: icon = value === undefined ? tracker.icon.MilestoneStatusPlanned : milestoneStatusAssets[value].icon
$: label = value === undefined ? tracker.string.Planned : milestoneStatusAssets[value].label
</script>
{#if kind === 'list'}
<button
class="flex-no-shrink clear-mins cursor-pointer content-pointer-events-none"
{disabled}
on:click={handlePopupOpen}
>
<Icon {icon} {size} />
</button>
{:else}
<Button
{label}
{kind}
{icon}
{justify}
{size}
{width}
{disabled}
showTooltip={{ label: tracker.string.SetStatus }}
on:click={handlePopupOpen}
/>
{/if}

View File

@ -13,37 +13,28 @@
// limitations under the License.
-->
<script lang="ts">
import { MilestoneStatus } from '@hcengineering/tracker'
import { Milestone, MilestoneStatus } from '@hcengineering/tracker'
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
import tracker from '../../plugin'
import MilestoneStatusSelector from './MilestoneStatusSelector.svelte'
import MilestoneStatusEditor from './MilestoneStatusEditor.svelte'
export let value: MilestoneStatus
export let object: Milestone
export let onChange: ((value: MilestoneStatus) => void) | undefined = undefined
export let kind: ButtonKind = 'link'
export let size: ButtonSize = 'large'
export let justify: 'left' | 'center' = 'left'
export let width: string | undefined = '100%'
function handleComponentStatusChange (newMilestoneStatus: MilestoneStatus | undefined) {
if (newMilestoneStatus === undefined || onChange === undefined) {
return
}
onChange(newMilestoneStatus)
}
$: isEditable = onChange !== undefined
$: disabled = onChange === undefined
</script>
<MilestoneStatusSelector
<MilestoneStatusEditor
{value}
{object}
{kind}
{size}
{width}
{justify}
{isEditable}
showTooltip={isEditable ? { label: tracker.string.SetStatus } : undefined}
selectedMilestoneStatus={value}
onMilestoneStatusChange={handleComponentStatusChange}
{disabled}
on:change={({ detail }) => onChange?.(detail)}
/>

View File

@ -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 { MilestoneStatus } 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 { defaultMilestoneStatuses, milestoneStatusAssets } from '../../utils'
export let selectedMilestoneStatus: MilestoneStatus | undefined
export let shouldShowLabel: boolean = true
export let onMilestoneStatusChange: ((newMilestoneStatus: MilestoneStatus | 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 = selectedMilestoneStatus
? milestoneStatusAssets[selectedMilestoneStatus].icon
: tracker.icon.MilestoneStatusPlanned
$: selectedStatusLabel = shouldShowLabel
? selectedMilestoneStatus
? milestoneStatusAssets[selectedMilestoneStatus].label
: tracker.string.Planned
: undefined
$: statusesInfo = defaultMilestoneStatuses.map((s) => ({
id: s,
isSelected: milestoneStatusAssets[s].label === selectedStatusLabel,
...milestoneStatusAssets[s]
}))
const handleMilestoneStatusEditorOpened = (event: MouseEvent) => {
if (!isEditable) {
return
}
showPopup(
SelectPopup,
{ value: statusesInfo, placeholder: tracker.string.SetStatus, searchable: true },
eventToHTMLElement(event),
onMilestoneStatusChange
)
}
</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={handleMilestoneStatusEditorOpened}
>
<Icon icon={selectedStatusIcon} {size} />
</div>
{:else}
<Button
{kind}
{size}
{width}
{justify}
disabled={!isEditable}
icon={selectedStatusIcon}
label={selectedStatusLabel}
{showTooltip}
on:click={handleMilestoneStatusEditorOpened}
/>
{/if}

View File

@ -21,7 +21,7 @@
import { StyledTextArea } from '@hcengineering/text-editor'
import { createEventDispatcher } from 'svelte'
import tracker from '../../plugin'
import MilestoneStatusSelector from './MilestoneStatusSelector.svelte'
import MilestoneStatusEditor from './MilestoneStatusEditor.svelte'
import { createBacklinks } from '@hcengineering/chunter-resources'
export let space: Ref<Project>
@ -42,14 +42,6 @@
// Create an backlink to document
await createBacklinks(client, _id, tracker.class.Milestone, _id, object.description ?? '')
}
const handleComponentStatusChanged = (newMilestoneStatus: MilestoneStatus | undefined) => {
if (newMilestoneStatus === undefined) {
return
}
object.status = newMilestoneStatus
}
</script>
<Card
@ -83,12 +75,7 @@
showButtons={false}
/>
<svelte:fragment slot="pool">
<MilestoneStatusSelector
selectedMilestoneStatus={object.status}
onMilestoneStatusChange={handleComponentStatusChanged}
kind={'secondary'}
size={'large'}
/>
<MilestoneStatusEditor bind:value={object.status} {object} kind="secondary" />
<DatePresenter
bind:value={object.targetDate}
editable

View File

@ -87,6 +87,7 @@ import MilestonePresenter from './components/milestones/MilestonePresenter.svelt
import Milestones from './components/milestones/Milestones.svelte'
import MilestoneSelector from './components/milestones/MilestoneSelector.svelte'
import MilestoneStatusPresenter from './components/milestones/MilestoneStatusPresenter.svelte'
import MilestoneStatusEditor from './components/milestones/MilestoneStatusEditor.svelte'
import MilestoneTitlePresenter from './components/milestones/MilestoneTitlePresenter.svelte'
import ScrumRecordPanel from './components/scrums/ScrumRecordPanel.svelte'
@ -456,6 +457,7 @@ export default async (): Promise<Resources> => ({
Scrums,
ScrumRecordPanel,
MilestoneStatusPresenter,
MilestoneStatusEditor,
MilestoneTitlePresenter,
MilestoneSelector,
MilestoneEditor,

View File

@ -359,6 +359,7 @@ export default mergeIds(trackerId, tracker, {
Milestones: '' as AnyComponent,
MilestonePresenter: '' as AnyComponent,
MilestoneStatusPresenter: '' as AnyComponent,
MilestoneStatusEditor: '' as AnyComponent,
MilestoneTitlePresenter: '' as AnyComponent,
MilestoneDatePresenter: '' as AnyComponent,
EditMilestone: '' as AnyComponent,