Ability to change done state (#888)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-01-31 16:07:56 +07:00 committed by GitHub
parent b2f3479a24
commit a5f662e43f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 230 additions and 13 deletions

View File

@ -428,6 +428,14 @@ export function createModel (builder: Builder): void {
presenter: task.component.StatePresenter
})
builder.mixin(task.class.DoneState, core.class.Class, view.mixin.AttributeEditor, {
editor: task.component.DoneStateEditor
})
builder.mixin(task.class.DoneState, core.class.Class, view.mixin.AttributePresenter, {
presenter: task.component.DoneStatePresenter
})
builder.createDoc(
view.class.ViewletDescriptor,
core.space.Model,

View File

@ -55,6 +55,7 @@ export default mergeIds(taskId, task, {
StatePresenter: '' as AnyComponent,
DoneStatePresenter: '' as AnyComponent,
StateEditor: '' as AnyComponent,
DoneStateEditor: '' as AnyComponent,
KanbanView: '' as AnyComponent,
Todos: '' as AnyComponent,
TodoItemPresenter: '' as AnyComponent,

View File

@ -13,7 +13,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import type { Doc } from '@anticrm/core'
import { getResource } from '@anticrm/platform'
@ -37,7 +36,7 @@
$: attribute = typeof key === 'string' ? hierarchy.getAttribute(_class, key) : key.attr
$: attributeKey = typeof key === 'string' ? key : key.key
$: typeClassId = (attribute !== undefined) ? getAttributePresenterClass(attribute) : undefined
$: typeClassId = attribute !== undefined ? getAttributePresenterClass(attribute) : undefined
let editor: Promise<AnySvelteComponent> | undefined
@ -57,7 +56,6 @@
{#await editor}
...
{:then instance}
{#if attribute.icon}
<div class="flex-row-center">
<CircleButton icon={attribute.icon} size={'large'} />
@ -66,19 +64,46 @@
{#if showHeader}
<Label label={attribute.label} />
{/if}
<div class="value"><svelte:component this={instance} label={attribute?.label} placeholder={attribute?.label} {maxWidth} value={getAttribute(client, object, { key: attributeKey, attr: attribute })} {onChange} {focus}/></div>
<div class="value">
<svelte:component
this={instance}
label={attribute?.label}
placeholder={attribute?.label}
{maxWidth}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
space={object.space}
{onChange}
{focus}
/>
</div>
</div>
{/if}
</div>
{:else if showHeader}
<div class="flex-col">
<Label label={attribute.label} />
<div class="value"><svelte:component this={instance} label={attribute?.label} placeholder={attribute?.label} {maxWidth} value={getAttribute(client, object, { key: attributeKey, attr: attribute })} {onChange} {focus}/></div>
<div class="value">
<svelte:component
this={instance}
label={attribute?.label}
placeholder={attribute?.label}
{maxWidth}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
space={object.space}
{onChange}
{focus}
/>
</div>
</div>
{:else}
<svelte:component this={instance} {maxWidth} value={getAttribute(client, object, { key: attributeKey, attr: attribute })} {onChange} {focus}/>
<svelte:component
this={instance}
{maxWidth}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
space={object.space}
{onChange}
{focus}
/>
{/if}
{/await}
{/if}

View File

@ -64,6 +64,7 @@
"DoneStates": "Done states",
"States": "States",
"ChooseAColor": "Choose a color",
"SearchTask": "Search for task..."
"SearchTask": "Search for task...",
"NoDoneState": "Not done"
}
}

View File

@ -67,6 +67,7 @@
</div>
</div>
<AttributeBarEditor key={'state'} {object} showHeader={false} />
<AttributeBarEditor key={'doneState'} {object} showHeader={false} />
</div>
<style lang="scss">

View File

@ -0,0 +1,70 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 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 '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
import { DoneState, SpaceWithStates } from '@anticrm/task'
import { Label, showPopup } from '@anticrm/ui'
import DoneStatePresenter from './DoneStatePresenter.svelte'
import DoneStatesPopup from './DoneStatesPopup.svelte'
import task from '../../plugin'
export let value: Ref<DoneState> | null | undefined
export let onChange: (value: any) => void
export let space: Ref<SpaceWithStates>
let state: DoneState | undefined
let container: HTMLElement
let opened: boolean = false
const query = createQuery()
$: if (value != null) {
query.query(
task.class.DoneState,
{ _id: value },
(res) => {
state = res[0]
},
{ limit: 1 }
)
}
</script>
<div
class="flex-row-center cursor-pointer"
bind:this={container}
on:click|preventDefault={() => {
if (!opened) {
opened = true
showPopup(DoneStatesPopup, { space }, container, (result) => {
if (result) {
value = result._id
onChange(value)
} else if (result == null) {
value = null
onChange(value)
}
opened = false
})
}
}}
>
{#if state}
<DoneStatePresenter value={state} showTitle />
{:else}
<Label label={task.string.NoDoneState} />
{/if}
</div>

View File

@ -17,16 +17,21 @@
<script lang="ts">
import type { DoneState } from '@anticrm/task'
import task from '@anticrm/task'
import { getPlatformColor } from '@anticrm/ui'
export let value: DoneState
export let showTitle: boolean = false
$: color = value._class === task.class.WonState ? '#a5d179' : '#f28469'
$: color = value._class === task.class.WonState ? getPlatformColor(0) : getPlatformColor(11)
</script>
{#if value }
<div class="flex-center">
<div class="state-container" style="background-color: {color};" />
<div class="state-container" class:state-container-title={showTitle} style="background-color: {color};" />
{#if showTitle}
{value.title}
{/if}
</div>
{/if}
@ -36,4 +41,7 @@
height: .5rem;
border-radius: .5rem;
}
.state-container-title {
margin-right: 0.75rem;
}
</style>

View File

@ -0,0 +1,100 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 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, Ref, SortingOrder } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
import { DoneState, SpaceWithStates } from '@anticrm/task'
import { getPlatformColor, Label } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import task from '../../plugin'
export let space: Ref<SpaceWithStates>
let states: DoneState[] = []
const dispatch = createEventDispatcher()
const statesQuery = createQuery()
statesQuery.query(
task.class.DoneState,
{ space },
(res) => {
states = res
},
{
sort: {
rank: SortingOrder.Ascending
}
}
)
function getColor (_class: Ref<Class<Doc>>): string {
return _class === task.class.WonState ? getPlatformColor(0) : getPlatformColor(11)
}
</script>
<div class="flex-col statuses-container">
{#each states as state}
<div
class="flex-row-center state"
on:click={() => {
dispatch('close', state)
}}
>
<div class="color" style="background-color: {getColor(state._class)}" />
{state.title}
</div>
{/each}
<div
class="flex-row-center state"
on:click={() => {
dispatch('close', null)
}}
>
<div class='not-done'>
<Label label={task.string.NoDoneState}/>
</div>
</div>
</div>
<style lang="scss">
.statuses-container {
padding: 0.5rem;
max-height: 100%;
min-width: 10rem;
color: var(--theme-caption-color);
background-color: var(--theme-button-bg-focused);
border: 1px solid var(--theme-button-border-enabled);
border-radius: 0.75rem;
user-select: none;
filter: drop-shadow(0 1.5rem 4rem rgba(0, 0, 0, 0.35));
.state {
padding: 0.5rem;
border-radius: 0.5rem;
cursor: pointer;
&:hover {
background-color: var(--theme-button-bg-hovered);
}
.color {
margin-right: 0.75rem;
width: .5rem;
height: .5rem;
border-radius: .5rem;
}
}
}
.not-done {
margin-left: 1.25rem;
}
</style>

View File

@ -26,6 +26,7 @@ import EditIssue from './components/EditIssue.svelte'
import KanbanView from './components/kanban/KanbanView.svelte'
import KanbanCard from './components/KanbanCard.svelte'
import DoneStatePresenter from './components/state/DoneStatePresenter.svelte'
import DoneStateEditor from './components/state/DoneStateEditor.svelte'
import EditStatuses from './components/state/EditStatuses.svelte'
import StateEditor from './components/state/StateEditor.svelte'
import StatePresenter from './components/state/StatePresenter.svelte'
@ -140,7 +141,8 @@ export default async (): Promise<Resources> => ({
TodoItemPresenter,
TodoStatePresenter,
StatusTableView,
TaskHeader
TaskHeader,
DoneStateEditor
},
actionImpl: {
CreateTask: createTask,

View File

@ -58,7 +58,8 @@ export default mergeIds(taskId, task, {
DoneStatesWon: '' as IntlString,
DoneStatesLost: '' as IntlString,
AllStates: '' as IntlString,
ChooseAColor: '' as IntlString
ChooseAColor: '' as IntlString,
NoDoneState: '' as IntlString
},
status: {
AssigneeRequired: '' as IntlString