mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-13 13:17:45 +02:00
@@ -167,7 +167,7 @@
|
||||
/>
|
||||
<ViewletSettingButton bind:viewOptions bind:viewlet />
|
||||
</div>
|
||||
<FilterBar {_class} query={searchQuery} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
|
||||
<FilterBar {_class} query={searchQuery} space={undefined} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
|
||||
|
||||
<Component is={tags.component.TagsCategoryBar} props={{ targetClass: _class, category }} on:change={handleChange} />
|
||||
|
||||
|
||||
@@ -13,60 +13,66 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Data, Ref } from '@hcengineering/core'
|
||||
import presentation, { Card, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { DoneState, SpaceWithStates, State, createState } from '@hcengineering/task'
|
||||
import { EditBox, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '../plugin'
|
||||
import presentation, { Card, getClient } from '@hcengineering/presentation'
|
||||
import { calcRank, DoneState, Kanban, KanbanTemplate, KanbanTemplateSpace, State } from '@hcengineering/task'
|
||||
import { Class, Data, generateId, Ref, SortingOrder } from '@hcengineering/core'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
export let status: State | undefined = undefined
|
||||
export let _class: Ref<Class<State | DoneState>> | undefined = status?._class
|
||||
export let template: KanbanTemplate | undefined = undefined
|
||||
export let value = status?.name ?? ''
|
||||
const isTemplate = template !== undefined
|
||||
export let space: Kanban | KanbanTemplateSpace | undefined
|
||||
export let space: Ref<SpaceWithStates>
|
||||
|
||||
let canSave = true
|
||||
let _space: SpaceWithStates | undefined = undefined
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.SpaceWithStates, { _id: space }, (res) => {
|
||||
_space = res[0]
|
||||
})
|
||||
|
||||
const canSave = true
|
||||
async function save () {
|
||||
if (space === undefined && template === undefined && status?.space === undefined) return
|
||||
const attachedTo = isTemplate && template?._id ? { attachedTo: template._id } : {}
|
||||
const kanban = space as Kanban
|
||||
if (_class !== undefined && status === undefined) {
|
||||
const query = isTemplate ? { ...attachedTo } : kanban?.attachedTo ? { space: kanban.attachedTo } : {}
|
||||
const lastOne = await client.findOne(_class, query, { sort: { rank: SortingOrder.Descending } })
|
||||
let newDoc: Data<State> = {
|
||||
ofAttribute: task.attribute.State,
|
||||
name: value.trim(),
|
||||
rank: calcRank(lastOne, undefined),
|
||||
...attachedTo
|
||||
}
|
||||
if (_space === undefined || _class === undefined) return
|
||||
if (status === undefined) {
|
||||
if (!hierarchy.isDerived(_class, task.class.DoneState)) {
|
||||
newDoc = {
|
||||
const newDoc: Data<State> = {
|
||||
ofAttribute: task.attribute.State,
|
||||
name: value.trim(),
|
||||
color: 9,
|
||||
rank: calcRank(lastOne, undefined),
|
||||
...attachedTo
|
||||
color: 9
|
||||
}
|
||||
const id = await createState(client, _class, newDoc)
|
||||
await client.update(_space, { $push: { states: id } })
|
||||
} else {
|
||||
const newDoc: Data<DoneState> = {
|
||||
ofAttribute: task.attribute.DoneState,
|
||||
name: value.trim()
|
||||
}
|
||||
const id = await createState(client, _class, newDoc)
|
||||
await client.update(_space, { $push: { states: id } })
|
||||
}
|
||||
} else {
|
||||
const id = await createState(client, _class, { ...status, name: value.trim() })
|
||||
if (!hierarchy.isDerived(_class, task.class.DoneState)) {
|
||||
const states = _space.states
|
||||
const index = states.findIndex((x) => x === status?._id)
|
||||
if (index !== -1) {
|
||||
states[index] = id
|
||||
await client.update(_space, { states })
|
||||
}
|
||||
} else {
|
||||
const states = _space.doneStates ?? []
|
||||
const index = states.findIndex((x) => x === status?._id)
|
||||
if (index !== -1) {
|
||||
states[index] = id
|
||||
await client.update(_space, { doneStates: states })
|
||||
}
|
||||
}
|
||||
const ops = client.apply(template?.space ?? kanban?.attachedTo ?? generateId()).notMatch(_class, {
|
||||
space: isTemplate && template ? template.space : kanban?.attachedTo,
|
||||
name: value.trim(),
|
||||
...attachedTo
|
||||
})
|
||||
await ops.createDoc(_class, isTemplate && template ? template.space : kanban?.attachedTo, newDoc)
|
||||
canSave = await ops.commit()
|
||||
}
|
||||
if (status !== undefined && _class !== undefined) {
|
||||
const ops = client.apply(status._id).notMatch(_class, { space: status.space, name: value.trim(), ...attachedTo })
|
||||
await ops.update(status, { name: value.trim() })
|
||||
canSave = await ops.commit()
|
||||
}
|
||||
if (canSave) dispatch('close')
|
||||
dispatch('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<!--
|
||||
// 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, Data, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import presentation, { Card, getClient } from '@hcengineering/presentation'
|
||||
import { DoneStateTemplate, KanbanTemplate, KanbanTemplateSpace, StateTemplate, calcRank } from '@hcengineering/task'
|
||||
import { EditBox, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '../plugin'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
export let status: StateTemplate | undefined = undefined
|
||||
export let _class: Ref<Class<StateTemplate | DoneStateTemplate>> | undefined = status?._class
|
||||
export let template: KanbanTemplate
|
||||
export let space: KanbanTemplateSpace
|
||||
export let value = status?.name ?? ''
|
||||
|
||||
let canSave = true
|
||||
async function save () {
|
||||
if (space === undefined && template === undefined && status?.space === undefined) return
|
||||
const attachedTo = { attachedTo: template._id }
|
||||
if (_class !== undefined && status === undefined) {
|
||||
const lastOne = await client.findOne(_class, attachedTo, { sort: { rank: SortingOrder.Descending } })
|
||||
let newDoc: Data<StateTemplate> = {
|
||||
ofAttribute: task.attribute.State,
|
||||
name: value.trim(),
|
||||
rank: calcRank(lastOne, undefined),
|
||||
...attachedTo
|
||||
}
|
||||
if (!hierarchy.isDerived(_class, task.class.DoneState)) {
|
||||
newDoc = {
|
||||
ofAttribute: task.attribute.State,
|
||||
name: value.trim(),
|
||||
color: 9,
|
||||
rank: calcRank(lastOne, undefined),
|
||||
...attachedTo
|
||||
}
|
||||
}
|
||||
const ops = client.apply(template.space).notMatch(_class, {
|
||||
space: template.space,
|
||||
name: value.trim(),
|
||||
...attachedTo
|
||||
})
|
||||
await ops.createDoc(_class, template.space, newDoc)
|
||||
canSave = await ops.commit()
|
||||
}
|
||||
if (status !== undefined && _class !== undefined) {
|
||||
const ops = client.apply(status._id).notMatch(_class, { space: status.space, name: value.trim(), ...attachedTo })
|
||||
await ops.update(status, { name: value.trim() })
|
||||
canSave = await ops.commit()
|
||||
}
|
||||
if (canSave) dispatch('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={task.string.StatusPopupTitle}
|
||||
okAction={save}
|
||||
canSave
|
||||
okLabel={presentation.string.Save}
|
||||
on:changeContent
|
||||
onCancel={() => dispatch('close')}
|
||||
>
|
||||
<EditBox focusIndex={1} bind:value placeholder={task.string.StatusName} kind={'large-style'} autoFocus fullSize />
|
||||
<svelte:fragment slot="error">
|
||||
{#if !canSave}
|
||||
<Label label={task.string.NameAlreadyExists} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
@@ -14,16 +14,16 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, DocumentQuery, FindOptions, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Class, DocumentQuery, FindOptions, IdMap, Ref, Status } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { DoneState, SpaceWithStates, State, Task } from '@hcengineering/task'
|
||||
import type { TabItem } from '@hcengineering/ui'
|
||||
import { TabList } from '@hcengineering/ui'
|
||||
import { TableBrowser } from '@hcengineering/view-resources'
|
||||
import { TableBrowser, statusStore } from '@hcengineering/view-resources'
|
||||
import task from '../plugin'
|
||||
import StatesBar from './state/StatesBar.svelte'
|
||||
import Lost from './icons/Lost.svelte'
|
||||
import Won from './icons/Won.svelte'
|
||||
import StatesBar from './state/StatesBar.svelte'
|
||||
|
||||
export let _class: Ref<Class<Task>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
@@ -33,14 +33,28 @@
|
||||
|
||||
let doneStatusesView: boolean = false
|
||||
let state: Ref<State> | undefined = undefined
|
||||
let _space: SpaceWithStates | undefined = undefined
|
||||
const selectedDoneStates: Set<Ref<DoneState>> = new Set<Ref<DoneState>>()
|
||||
$: resConfig = updateConfig(config)
|
||||
let doneStates: DoneState[] = []
|
||||
let itemsDS: TabItem[] = []
|
||||
$: doneStates = getDoneStates(_space, $statusStore)
|
||||
$: itemsDS = getItems(doneStates)
|
||||
let selectedDS: string[] = []
|
||||
let withoutDone: boolean = false
|
||||
let resultQuery: DocumentQuery<Task>
|
||||
|
||||
function getItems (doneStates: DoneState[]): TabItem[] {
|
||||
const itemsDS: TabItem[] = doneStates.map((s) => {
|
||||
return {
|
||||
id: s._id,
|
||||
label: s.name,
|
||||
icon: s._class === task.class.WonState ? Won : Lost,
|
||||
color: s._class === task.class.WonState ? 'var(--theme-won-color)' : 'var(--theme-lost-color)'
|
||||
}
|
||||
})
|
||||
itemsDS.unshift({ id: 'NoDoneState', labelIntl: task.string.NoDoneState })
|
||||
return itemsDS
|
||||
}
|
||||
|
||||
function updateConfig (config: string[]): string[] {
|
||||
if (state !== undefined) {
|
||||
return config.filter((p) => p !== 'status')
|
||||
@@ -51,34 +65,28 @@
|
||||
return config
|
||||
}
|
||||
|
||||
const doneStateQuery = createQuery()
|
||||
doneStateQuery.query(
|
||||
task.class.DoneState,
|
||||
space != null
|
||||
? {
|
||||
space
|
||||
}
|
||||
: {},
|
||||
(res) => {
|
||||
doneStates = res
|
||||
itemsDS = doneStates.map((s) => {
|
||||
return {
|
||||
id: s._id,
|
||||
label: s.name,
|
||||
icon: s._class === task.class.WonState ? Won : Lost,
|
||||
color: s._class === task.class.WonState ? 'var(--theme-won-color)' : 'var(--theme-lost-color)'
|
||||
}
|
||||
})
|
||||
itemsDS.unshift({ id: 'NoDoneState', labelIntl: task.string.NoDoneState })
|
||||
},
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
$: spaceQuery.query(
|
||||
task.class.SpaceWithStates,
|
||||
{
|
||||
sort: {
|
||||
_class: SortingOrder.Descending,
|
||||
rank: SortingOrder.Descending
|
||||
}
|
||||
_id: space
|
||||
},
|
||||
(res) => {
|
||||
_space = res[0]
|
||||
}
|
||||
)
|
||||
|
||||
function getDoneStates (space: SpaceWithStates | undefined, statusStore: IdMap<Status>): DoneState[] {
|
||||
if (space === undefined) {
|
||||
return []
|
||||
}
|
||||
const doneStates = space.doneStates
|
||||
? space.doneStates.map((x) => statusStore.get(x) as DoneState).filter((p) => p !== undefined)
|
||||
: []
|
||||
return doneStates
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function updateQuery (query: DocumentQuery<Task>, selectedDoneStates: Set<Ref<DoneState>>): Promise<void> {
|
||||
|
||||
@@ -13,29 +13,36 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IdMap, Ref, Status } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import type { Kanban } from '@hcengineering/task'
|
||||
import type { SpaceWithStates } from '@hcengineering/task'
|
||||
import task, { DoneState, LostState, WonState } from '@hcengineering/task'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Won from '../icons/Won.svelte'
|
||||
import Lost from '../icons/Lost.svelte'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
|
||||
export let kanban: Kanban
|
||||
export let space: Ref<SpaceWithStates>
|
||||
let wonStates: WonState[] = []
|
||||
let lostStates: LostState[] = []
|
||||
let _space: SpaceWithStates | undefined = undefined
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: if (kanban !== undefined) {
|
||||
doneStatesQ.query(task.class.DoneState, { space: kanban.space }, (result) => {
|
||||
wonStates = result.filter((x) => x._class === task.class.WonState)
|
||||
lostStates = result.filter((x) => x._class === task.class.LostState)
|
||||
})
|
||||
} else {
|
||||
doneStatesQ.unsubscribe()
|
||||
const query = createQuery()
|
||||
query.query(task.class.SpaceWithStates, { _id: space }, (result) => {
|
||||
_space = result[0]
|
||||
})
|
||||
|
||||
function getStates (space: SpaceWithStates | undefined, statusStore: IdMap<Status>): void {
|
||||
if (space === undefined) return
|
||||
const result: Status[] =
|
||||
(space.doneStates?.map((p) => statusStore.get(p))?.filter((p) => p !== undefined) as Status[]) ?? []
|
||||
wonStates = result.filter((x) => x._class === task.class.WonState)
|
||||
lostStates = result.filter((x) => x._class === task.class.LostState)
|
||||
}
|
||||
|
||||
$: getStates(_space, $statusStore)
|
||||
|
||||
let hoveredDoneState: Ref<DoneState> | undefined
|
||||
|
||||
const onDone = (state: DoneState) => async () => {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<!--
|
||||
// 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, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import type { DoneState, Kanban, State } from '@hcengineering/task'
|
||||
import task, { calcRank } from '@hcengineering/task'
|
||||
import StatesEditor from '../state/StatesEditor.svelte'
|
||||
|
||||
export let kanban: Kanban
|
||||
|
||||
let states: State[] = []
|
||||
|
||||
let doneStates: DoneState[] = []
|
||||
let wonStates: DoneState[] = []
|
||||
let lostStates: DoneState[] = []
|
||||
$: wonStates = doneStates.filter((x) => x._class === task.class.WonState)
|
||||
$: lostStates = doneStates.filter((x) => x._class === task.class.LostState)
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const statesQ = createQuery()
|
||||
$: statesQ.query(
|
||||
task.class.State,
|
||||
{ space: kanban.space },
|
||||
(result) => {
|
||||
states = result
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const doneStatesQ = createQuery()
|
||||
$: doneStatesQ.query(
|
||||
task.class.DoneState,
|
||||
{ space: kanban.space },
|
||||
(result) => {
|
||||
doneStates = result
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
async function onMove ({ detail: { stateID, position } }: { detail: { stateID: Ref<State>; position: number } }) {
|
||||
const [prev, next] = [states[position - 1], states[position + 1]]
|
||||
const state = states.find((x) => x._id === stateID)
|
||||
|
||||
if (state === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.updateDoc(state._class, state.space, state._id, {
|
||||
rank: calcRank(prev, next)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<StatesEditor {states} {wonStates} {lostStates} space={kanban} on:delete on:move={onMove} />
|
||||
@@ -14,21 +14,20 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Ref, Space, SortingOrder } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import core, { Ref, SortingOrder, Space } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import type {
|
||||
State,
|
||||
DoneState,
|
||||
DoneStateTemplate,
|
||||
KanbanTemplate,
|
||||
StateTemplate,
|
||||
DoneState,
|
||||
KanbanTemplateSpace
|
||||
KanbanTemplateSpace,
|
||||
State,
|
||||
StateTemplate
|
||||
} from '@hcengineering/task'
|
||||
import task, { calcRank } from '@hcengineering/task'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import StatesEditor from '../state/StatesEditor.svelte'
|
||||
import StatesTemplateEditor from '../state/StatesTemplateEditor.svelte'
|
||||
|
||||
export let kanban: KanbanTemplate
|
||||
export let folder: KanbanTemplateSpace
|
||||
@@ -103,7 +102,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<StatesEditor
|
||||
<StatesTemplateEditor
|
||||
template={kanban}
|
||||
space={folder}
|
||||
{states}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
import { Item, Kanban as KanbanUI } from '@hcengineering/kanban'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient, ActionContext } from '@hcengineering/presentation'
|
||||
import { Kanban, SpaceWithStates, Task, TaskGrouping, TaskOrdering } from '@hcengineering/task'
|
||||
import { SpaceWithStates, Task, TaskGrouping, TaskOrdering } from '@hcengineering/task'
|
||||
import {
|
||||
ColorDefinition,
|
||||
defaultBackground,
|
||||
@@ -75,7 +75,6 @@
|
||||
|
||||
export let options: FindOptions<Task> | undefined
|
||||
|
||||
$: currentSpace = space
|
||||
$: groupByKey = (viewOptions.groupBy[0] ?? noCategory) as TaskGrouping
|
||||
$: orderBy = viewOptions.orderBy
|
||||
$: sort = { [orderBy[0]]: orderBy[1] }
|
||||
@@ -88,13 +87,6 @@
|
||||
accentColors = accentColors
|
||||
}
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
let currentProject: SpaceWithStates | undefined
|
||||
$: spaceQuery.query(task.class.SpaceWithStates, { _id: currentSpace }, (res) => {
|
||||
currentProject = res.shift()
|
||||
})
|
||||
|
||||
let resultQuery: DocumentQuery<any> = { ...query }
|
||||
$: getResultQuery(query, viewOptionsConfig, viewOptions).then((p) => (resultQuery = { ...p, ...query }))
|
||||
|
||||
@@ -160,20 +152,21 @@
|
||||
|
||||
const queryId = generateId()
|
||||
|
||||
$: updateCategories(_class, tasks, groupByKey, viewOptions, viewOptionsConfig)
|
||||
$: updateCategories(_class, space, tasks, groupByKey, viewOptions, viewOptionsConfig)
|
||||
|
||||
function update () {
|
||||
updateCategories(_class, tasks, groupByKey, viewOptions, viewOptionsConfig)
|
||||
updateCategories(_class, space, tasks, groupByKey, viewOptions, viewOptionsConfig)
|
||||
}
|
||||
|
||||
async function updateCategories (
|
||||
_class: Ref<Class<Doc>>,
|
||||
space: Ref<SpaceWithStates> | undefined,
|
||||
docs: Doc[],
|
||||
groupByKey: string,
|
||||
viewOptions: ViewOptions,
|
||||
viewOptionsModel: ViewOptionModel[] | undefined
|
||||
) {
|
||||
categories = await getCategories(client, _class, docs, groupByKey, viewlet.descriptor)
|
||||
categories = await getCategories(client, _class, space, docs, groupByKey, viewlet.descriptor)
|
||||
for (const viewOption of viewOptionsModel ?? []) {
|
||||
if (viewOption.actionTarget !== 'category') continue
|
||||
const categoryFunc = viewOption as CategoryOption
|
||||
@@ -188,6 +181,7 @@
|
||||
const res = await categoryAction(
|
||||
_class,
|
||||
spaces.length > 0 ? { space: { $in: Array.from(spaces.values()) } } : {},
|
||||
space,
|
||||
groupByKey,
|
||||
update,
|
||||
queryId,
|
||||
@@ -231,13 +225,6 @@
|
||||
$: presenterMixin = client.getHierarchy().as(clazz, task.mixin.KanbanCard)
|
||||
$: cardPresenter = getResource(presenterMixin.card)
|
||||
|
||||
let kanban: Kanban
|
||||
|
||||
const kanbanQuery = createQuery()
|
||||
$: kanbanQuery.query(task.class.Kanban, { attachedTo: space }, (result) => {
|
||||
kanban = result[0]
|
||||
})
|
||||
|
||||
const getDoneUpdate = (e: any) => ({ doneState: e.detail._id } as DocumentUpdate<Doc>)
|
||||
</script>
|
||||
|
||||
@@ -301,13 +288,15 @@
|
||||
</svelte:fragment>
|
||||
<!-- eslint-disable-next-line no-undef -->
|
||||
<svelte:fragment slot="doneBar" let:onDone>
|
||||
<KanbanDragDone
|
||||
{kanban}
|
||||
on:done={(e) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
onDone(getDoneUpdate(e))
|
||||
}}
|
||||
/>
|
||||
{#if space}
|
||||
<KanbanDragDone
|
||||
{space}
|
||||
on:done={(e) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
onDone(getDoneUpdate(e))
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</KanbanUI>
|
||||
{/await}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
export let value: Ref<DoneState> | null | undefined
|
||||
export let onChange: (value: any) => void
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let space: Ref<SpaceWithStates> | undefined
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
<script lang="ts">
|
||||
import { Ref, Status, StatusValue } from '@hcengineering/core'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import type { DoneState } from '@hcengineering/task'
|
||||
import type { DoneState, SpaceWithStates } from '@hcengineering/task'
|
||||
import DoneStatePresenter from './DoneStatePresenter.svelte'
|
||||
import DoneStateEditor from './DoneStateEditor.svelte'
|
||||
|
||||
export let value: Ref<DoneState> | StatusValue
|
||||
export let space: Ref<SpaceWithStates> | undefined
|
||||
export let showTitle: boolean = true
|
||||
export let onChange: ((value: Ref<DoneState>) => void) | undefined = undefined
|
||||
|
||||
@@ -29,7 +30,7 @@
|
||||
|
||||
{#if value}
|
||||
{#if onChange !== undefined && state !== undefined}
|
||||
<DoneStateEditor value={state._id} space={state.space} {onChange} kind="link" size="medium" />
|
||||
<DoneStateEditor value={state._id} {space} {onChange} kind="link" size="medium" />
|
||||
{:else}
|
||||
<DoneStatePresenter value={state} {showTitle} />
|
||||
{/if}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Class, Doc, IdMap, Ref, Status } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { DoneState, SpaceWithStates } from '@hcengineering/task'
|
||||
import { Label, PaletteColorIndexes, getPlatformColor, resizeObserver, themeStore } from '@hcengineering/ui'
|
||||
@@ -23,24 +23,29 @@
|
||||
import Lost from '../icons/Lost.svelte'
|
||||
import Unknown from '../icons/Unknown.svelte'
|
||||
import Won from '../icons/Won.svelte'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
|
||||
export let space: Ref<SpaceWithStates>
|
||||
let states: DoneState[] = []
|
||||
const dispatch = createEventDispatcher()
|
||||
const statesQuery = createQuery()
|
||||
statesQuery.query(
|
||||
task.class.DoneState,
|
||||
{ space },
|
||||
(res) => {
|
||||
states = res
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
_class: SortingOrder.Descending,
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
let _space: SpaceWithStates
|
||||
|
||||
function getStates (space: SpaceWithStates | undefined, statesStore: IdMap<Status>): void {
|
||||
if (space === undefined) return
|
||||
const res: Status[] =
|
||||
(space.doneStates?.map((p) => statesStore.get(p))?.filter((p) => p !== undefined) as Status[]) ?? []
|
||||
res.sort((a, b) => a._class.localeCompare(b._class))
|
||||
states = res
|
||||
}
|
||||
|
||||
$: getStates(_space, $statusStore)
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
spaceQuery.query(task.class.SpaceWithStates, { _id: space }, (res) => {
|
||||
_space = res[0]
|
||||
})
|
||||
|
||||
function getColor (_class: Ref<Class<Doc>>): string {
|
||||
return _class === task.class.WonState
|
||||
? getPlatformColor(PaletteColorIndexes.Crocodile, $themeStore.dark)
|
||||
|
||||
@@ -14,64 +14,67 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, DocumentQuery, Obj, Ref } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import type { DoneState, Kanban, SpaceWithStates, State } from '@hcengineering/task'
|
||||
import task from '../../plugin'
|
||||
import KanbanEditor from '../kanban/KanbanEditor.svelte'
|
||||
import { Icon, Label, showPopup, Panel, Scroller } from '@hcengineering/ui'
|
||||
import type { Doc, DocumentQuery, IdMap, Ref, Status } from '@hcengineering/core'
|
||||
import { MessageBox, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import type { DoneState, LostState, SpaceWithStates, State, WonState } from '@hcengineering/task'
|
||||
import { Icon, Label, Panel, Scroller, showPopup } from '@hcengineering/ui'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import workbench from '@hcengineering/workbench'
|
||||
import task from '../../plugin'
|
||||
import StatesEditor from './StatesEditor.svelte'
|
||||
|
||||
export let _id: Ref<SpaceWithStates>
|
||||
export let spaceClass: Ref<Class<Obj>>
|
||||
|
||||
let kanban: Kanban | undefined
|
||||
let spaceClassInstance: Class<SpaceWithStates> | undefined
|
||||
let spaceInstance: SpaceWithStates | undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const kanbanQ = createQuery()
|
||||
$: kanbanQ.query(task.class.Kanban, { attachedTo: _id }, (result) => {
|
||||
kanban = result[0]
|
||||
})
|
||||
|
||||
const spaceQ = createQuery()
|
||||
$: spaceQ.query<Class<SpaceWithStates>>(core.class.Class, { _id: spaceClass }, (result) => {
|
||||
spaceClassInstance = result.shift()
|
||||
})
|
||||
|
||||
const spaceI = createQuery()
|
||||
$: spaceI.query<SpaceWithStates>(spaceClass, { _id }, (result) => {
|
||||
spaceInstance = result.shift()
|
||||
$: spaceI.query<SpaceWithStates>(task.class.SpaceWithStates, { _id }, (result) => {
|
||||
spaceInstance = result[0]
|
||||
})
|
||||
|
||||
$: spaceClass = spaceInstance ? hierarchy.getClass(spaceInstance._class) : undefined
|
||||
|
||||
$: [states, doneStates] = getStates(spaceInstance, $statusStore)
|
||||
|
||||
$: wonStates = doneStates.filter((x) => x._class === task.class.WonState) as WonState[]
|
||||
$: lostStates = doneStates.filter((x) => x._class === task.class.LostState) as LostState[]
|
||||
|
||||
function getStates (space: SpaceWithStates | undefined, statusStore: IdMap<Status>): [Status[], DoneState[]] {
|
||||
if (space === undefined) {
|
||||
return [[], []]
|
||||
}
|
||||
|
||||
const states = space.states.map((x) => statusStore.get(x) as Status).filter((p) => p !== undefined)
|
||||
const doneStates = space.doneStates
|
||||
? space.doneStates.map((x) => statusStore.get(x) as DoneState).filter((p) => p !== undefined)
|
||||
: []
|
||||
|
||||
return [states, doneStates]
|
||||
}
|
||||
|
||||
async function deleteState ({ state }: { state: State | DoneState }) {
|
||||
if (spaceInstance === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const spaceClassInstance = client.getHierarchy().getClass(spaceInstance._class)
|
||||
const spaceView = client.getHierarchy().as(spaceClassInstance, workbench.mixin.SpaceView)
|
||||
const containingClass = spaceView.view.class
|
||||
|
||||
let query: DocumentQuery<Doc>
|
||||
if (hierarchy.isDerived(state._class, task.class.DoneState)) {
|
||||
query = { doneState: state._id }
|
||||
query = { doneState: state._id, space: _id }
|
||||
} else {
|
||||
query = { status: state._id }
|
||||
query = { status: state._id, space: _id }
|
||||
}
|
||||
|
||||
const objectsInThisState = await client.findAll(containingClass, query)
|
||||
const objectsInThisState = await client.findAll(task.class.Task, query)
|
||||
|
||||
if (objectsInThisState.length > 0) {
|
||||
showPopup(MessageBox, {
|
||||
label: task.string.CantStatusDelete,
|
||||
message: task.string.CantStatusDeleteError
|
||||
message: task.string.CantStatusDeleteError,
|
||||
canSubmit: false
|
||||
})
|
||||
} else {
|
||||
showPopup(
|
||||
@@ -82,13 +85,43 @@
|
||||
},
|
||||
undefined,
|
||||
async (result) => {
|
||||
if (result && kanban !== undefined) {
|
||||
client.removeDoc(state._class, state.space, state._id)
|
||||
if (result !== undefined) {
|
||||
if (hierarchy.isDerived(state._class, task.class.DoneState)) {
|
||||
const index = doneStates.findIndex((x) => x._id === state._id)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
states.splice(index, 1)
|
||||
if (spaceInstance) {
|
||||
await client.update(spaceInstance, { doneStates: states.map((x) => x._id) })
|
||||
}
|
||||
} else {
|
||||
const index = states.findIndex((x) => x._id === state._id)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
states.splice(index, 1)
|
||||
if (spaceInstance) {
|
||||
await client.update(spaceInstance, { states: states.map((x) => x._id) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function onMove (stateID: Ref<State>, position: number) {
|
||||
const index = states.findIndex((x) => x._id === stateID)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
const elem = states.splice(index, 1)
|
||||
states = [...states.slice(0, position), elem[0], ...states.slice(position)]
|
||||
if (spaceInstance) {
|
||||
await client.update(spaceInstance, { states: states.map((x) => x._id) })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Panel
|
||||
@@ -108,7 +141,7 @@
|
||||
<div class="title-wrapper">
|
||||
<span class="wrapped-title">
|
||||
<Label label={task.string.ManageStatusesWithin} />
|
||||
{#if spaceClassInstance}<Label label={spaceClassInstance?.label} />{:else}...{/if}
|
||||
{#if spaceClass}<Label label={spaceClass?.label} />{:else}...{/if}
|
||||
</span>
|
||||
{#if spaceInstance?.name}<span class="wrapped-subtitle">{spaceInstance?.name}</span>{/if}
|
||||
</div>
|
||||
@@ -117,9 +150,13 @@
|
||||
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content py-10 clear-mins">
|
||||
{#if kanban !== undefined}
|
||||
<KanbanEditor {kanban} on:delete={(e) => deleteState(e.detail)} />
|
||||
{/if}
|
||||
<StatesEditor
|
||||
{states}
|
||||
{wonStates}
|
||||
{lostStates}
|
||||
on:delete={(e) => deleteState(e.detail)}
|
||||
on:move={(e) => onMove(e.detail.stateID, e.detail.position)}
|
||||
/>
|
||||
</div>
|
||||
</Scroller>
|
||||
</Panel>
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import task, { State } from '@hcengineering/task'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { State } from '@hcengineering/task'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { showPopup, Button, eventToHTMLElement } from '@hcengineering/ui'
|
||||
import { Button, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import StatePresenter from './StatePresenter.svelte'
|
||||
import StatesPopup from './StatesPopup.svelte'
|
||||
|
||||
@@ -32,18 +32,9 @@
|
||||
export let shouldShowName: boolean = true
|
||||
export let shrink: number = 0
|
||||
|
||||
let state: State
|
||||
$: state = $statusStore.get(value)
|
||||
let opened: boolean = false
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(
|
||||
task.class.State,
|
||||
{ _id: value },
|
||||
(res) => {
|
||||
state = res[0]
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
const handleClick = (ev: MouseEvent) => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
|
||||
@@ -14,14 +14,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, Status, StatusValue } from '@hcengineering/core'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { Ref, Space, Status, StatusValue } from '@hcengineering/core'
|
||||
import { State } from '@hcengineering/task'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import StateEditor from './StateEditor.svelte'
|
||||
import StatePresenter from './StatePresenter.svelte'
|
||||
|
||||
export let value: Ref<State> | StatusValue
|
||||
export let space: Ref<Space>
|
||||
export let onChange: ((value: Ref<State>) => void) | undefined = undefined
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'medium'
|
||||
@@ -33,7 +34,7 @@
|
||||
|
||||
{#if value}
|
||||
{#if onChange !== undefined && state !== undefined}
|
||||
<StateEditor value={state._id} space={state.space} {onChange} {kind} {size} {shouldShowName} {shrink} />
|
||||
<StateEditor value={state._id} {space} {onChange} {kind} {size} {shouldShowName} {shrink} />
|
||||
{:else}
|
||||
<StatePresenter value={state} {shouldShowName} on:accent-color />
|
||||
{/if}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { BreadcrumbsElement } from '@hcengineering/presentation'
|
||||
import task, { SpaceWithStates, State } from '@hcengineering/task'
|
||||
import { BreadcrumbsElement, createQuery } from '@hcengineering/presentation'
|
||||
import task, { SpaceWithStates, State, getStates } from '@hcengineering/task'
|
||||
import { ScrollerBar, getColorNumberByText, getPlatformColor, themeStore } from '@hcengineering/ui'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -26,7 +26,20 @@
|
||||
export let state: Ref<State> | undefined = undefined
|
||||
export let gap: 'none' | 'small' | 'big' = 'small'
|
||||
|
||||
$: states = $statusStore.filter((it) => it.space === space && it.ofAttribute === task.attribute.State)
|
||||
let _space: SpaceWithStates | undefined = undefined
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
spaceQuery.query(
|
||||
task.class.SpaceWithStates,
|
||||
{
|
||||
_id: space
|
||||
},
|
||||
(res) => {
|
||||
_space = res[0]
|
||||
}
|
||||
)
|
||||
|
||||
$: states = getStates(_space, $statusStore)
|
||||
let divScroll: HTMLElement
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import type { DoneState, Kanban, KanbanTemplate, KanbanTemplateSpace, State } from '@hcengineering/task'
|
||||
import type { DoneState, KanbanTemplate, KanbanTemplateSpace, State } from '@hcengineering/task'
|
||||
import {
|
||||
CircleButton,
|
||||
Component,
|
||||
@@ -40,7 +40,7 @@
|
||||
import StatusesPopup from './StatusesPopup.svelte'
|
||||
|
||||
export let template: KanbanTemplate | undefined = undefined
|
||||
export let space: KanbanTemplateSpace | Kanban | undefined = undefined
|
||||
export let space: KanbanTemplateSpace | undefined = undefined
|
||||
export let states: State[] = []
|
||||
export let wonStates: DoneState[] = []
|
||||
export let lostStates: DoneState[] = []
|
||||
|
||||
@@ -14,28 +14,23 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import task, { SpaceWithStates, State } from '@hcengineering/task'
|
||||
import task, { SpaceWithStates, getStates } from '@hcengineering/task'
|
||||
import { getColorNumberByText, getPlatformColorDef, resizeObserver, themeStore } from '@hcengineering/ui'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let space: Ref<SpaceWithStates>
|
||||
let states: State[] = []
|
||||
$: states = getStates(_space, $statusStore)
|
||||
const dispatch = createEventDispatcher()
|
||||
const statesQuery = createQuery()
|
||||
statesQuery.query(
|
||||
task.class.State,
|
||||
{ space },
|
||||
(res) => {
|
||||
states = res
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
let _space: SpaceWithStates | undefined = undefined
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.SpaceWithStates, { _id: space }, (res) => {
|
||||
_space = res[0]
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="selectPopup" use:resizeObserver={() => dispatch('changeContent')}>
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
<!--
|
||||
// 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, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import type {
|
||||
DoneStateTemplate,
|
||||
KanbanTemplate,
|
||||
KanbanTemplateSpace,
|
||||
State,
|
||||
StateTemplate
|
||||
} from '@hcengineering/task'
|
||||
import {
|
||||
CircleButton,
|
||||
Component,
|
||||
IconAdd,
|
||||
IconCircles,
|
||||
IconMoreH,
|
||||
Label,
|
||||
PaletteColorIndexes,
|
||||
defaultBackground,
|
||||
eventToHTMLElement,
|
||||
getColorNumberByText,
|
||||
getPlatformColorDef,
|
||||
showPopup,
|
||||
themeStore
|
||||
} from '@hcengineering/ui'
|
||||
import { ColorsPopup, StringPresenter } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '../../plugin'
|
||||
import Lost from '../icons/Lost.svelte'
|
||||
import Won from '../icons/Won.svelte'
|
||||
import StatusesPopup from './StatusesPopup.svelte'
|
||||
|
||||
export let template: KanbanTemplate
|
||||
export let space: KanbanTemplateSpace
|
||||
export let states: StateTemplate[] = []
|
||||
export let wonStates: DoneStateTemplate[] = []
|
||||
export let lostStates: DoneStateTemplate[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
const elements: HTMLElement[] = []
|
||||
let selected: number | undefined
|
||||
let dragState: Ref<State>
|
||||
|
||||
function dragswap (ev: MouseEvent, i: number): boolean {
|
||||
const s = selected as number
|
||||
if (i < s) {
|
||||
return ev.offsetY < elements[i].offsetHeight / 2
|
||||
} else if (i > s) {
|
||||
return ev.offsetY > elements[i].offsetHeight / 2
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function dragover (ev: MouseEvent, i: number) {
|
||||
const s = selected as number
|
||||
if (dragswap(ev, i)) {
|
||||
;[states[i], states[s]] = [states[s], states[i]]
|
||||
selected = i
|
||||
}
|
||||
}
|
||||
|
||||
async function onMove (to: number) {
|
||||
dispatch('move', {
|
||||
stateID: dragState,
|
||||
position: to
|
||||
})
|
||||
}
|
||||
|
||||
const onColorChange =
|
||||
(state: State) =>
|
||||
async (color: number | undefined): Promise<void> => {
|
||||
if (color == null) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.updateDoc(state._class, state.space, state._id, { color })
|
||||
}
|
||||
const spaceEditor = space.editor
|
||||
|
||||
function add (_class: Ref<Class<StateTemplate | DoneStateTemplate>>) {
|
||||
showPopup(task.component.CreateStateTemplatePopup, {
|
||||
space,
|
||||
template,
|
||||
_class
|
||||
})
|
||||
}
|
||||
|
||||
function edit (status: StateTemplate) {
|
||||
showPopup(task.component.CreateStateTemplatePopup, { status, template, space })
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if spaceEditor}
|
||||
<Component is={spaceEditor} props={{ template }} />
|
||||
{/if}
|
||||
<div class="flex-no-shrink flex-between trans-title uppercase">
|
||||
<Label label={task.string.ActiveStates} />
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'medium'}
|
||||
on:click={() => {
|
||||
add(task.class.StateTemplate)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-col mt-3">
|
||||
{#each states as state, i}
|
||||
{@const color = getPlatformColorDef(state.color ?? getColorNumberByText(state.name), $themeStore.dark)}
|
||||
{#if state}
|
||||
<div
|
||||
bind:this={elements[i]}
|
||||
class="flex-between states"
|
||||
style:background={color.background ?? defaultBackground($themeStore.dark)}
|
||||
draggable={true}
|
||||
on:dragover|preventDefault={(ev) => {
|
||||
dragover(ev, i)
|
||||
}}
|
||||
on:drop|preventDefault={() => {
|
||||
onMove(i)
|
||||
}}
|
||||
on:dragstart={() => {
|
||||
selected = i
|
||||
dragState = states[i]._id
|
||||
}}
|
||||
on:dragend={() => {
|
||||
selected = undefined
|
||||
}}
|
||||
>
|
||||
<div class="bar"><IconCircles size={'small'} /></div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="color"
|
||||
style:background-color={color.color}
|
||||
on:click={() => {
|
||||
showPopup(ColorsPopup, { selected: color.name }, elements[i], onColorChange(state))
|
||||
}}
|
||||
/>
|
||||
<div class="flex-grow caption-color">
|
||||
<StringPresenter value={state.name} oneLine />
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="tool hover-trans"
|
||||
on:click={(ev) => {
|
||||
showPopup(
|
||||
StatusesPopup,
|
||||
{
|
||||
onDelete: () => dispatch('delete', { state }),
|
||||
showDelete: states.length > 1,
|
||||
onUpdate: () => edit(state)
|
||||
},
|
||||
eventToHTMLElement(ev),
|
||||
() => {}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<IconMoreH size={'medium'} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex-col mt-9">
|
||||
<div class="flex-no-shrink flex-between trans-title uppercase">
|
||||
<Label label={task.string.DoneStatesWon} />
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'medium'}
|
||||
on:click={() => {
|
||||
add(task.class.WonStateTemplate)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-col mt-4">
|
||||
{#each wonStates as state}
|
||||
{@const color = getPlatformColorDef(PaletteColorIndexes.Crocodile, $themeStore.dark)}
|
||||
{#if state}
|
||||
<div
|
||||
class="states flex-row-center"
|
||||
style:color={color.title}
|
||||
style:background={color.background ?? defaultBackground($themeStore.dark)}
|
||||
>
|
||||
<div class="bar" />
|
||||
<div class="mr-2">
|
||||
<Won size={'medium'} />
|
||||
</div>
|
||||
<div class="flex-grow caption-color">
|
||||
<StringPresenter value={state.name} oneLine />
|
||||
<!-- <AttributeEditor maxWidth={'13rem'} _class={state._class} object={state} key="name" />-->
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="tool hover-trans"
|
||||
on:click={(ev) => {
|
||||
showPopup(
|
||||
StatusesPopup,
|
||||
{
|
||||
onDelete: () => dispatch('delete', { state }),
|
||||
showDelete: wonStates.length > 1,
|
||||
onUpdate: () => edit(state)
|
||||
},
|
||||
eventToHTMLElement(ev),
|
||||
() => {}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<IconMoreH size={'medium'} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-9">
|
||||
<div class="flex-no-shrink flex-between trans-title uppercase">
|
||||
<Label label={task.string.DoneStatesLost} />
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'medium'}
|
||||
on:click={() => {
|
||||
add(task.class.LostStateTemplate)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4 mb-10">
|
||||
{#each lostStates as state}
|
||||
{@const color = getPlatformColorDef(PaletteColorIndexes.Firework, $themeStore.dark)}
|
||||
{#if state}
|
||||
<div
|
||||
class="states flex-row-center"
|
||||
style:color={color.title}
|
||||
style:background={color.background ?? defaultBackground($themeStore.dark)}
|
||||
>
|
||||
<div class="bar" />
|
||||
<div class="mr-2">
|
||||
<Lost size={'medium'} />
|
||||
</div>
|
||||
<div class="flex-grow caption-color">
|
||||
<StringPresenter value={state.name} oneLine />
|
||||
<!-- <AttributeEditor maxWidth={'13rem'} _class={state._class} object={state} key="name" />-->
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="tool hover-trans"
|
||||
on:click={(ev) => {
|
||||
showPopup(
|
||||
StatusesPopup,
|
||||
{
|
||||
onDelete: () => dispatch('delete', { state }),
|
||||
showDelete: lostStates.length > 1,
|
||||
onUpdate: () => edit(state)
|
||||
},
|
||||
eventToHTMLElement(ev),
|
||||
() => {}
|
||||
)
|
||||
}}
|
||||
>
|
||||
<IconMoreH size={'medium'} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.states {
|
||||
padding: 0.5rem 1rem 0.5rem 0.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
background-color: var(--theme-button-default);
|
||||
border: 1px solid var(--theme-button-border);
|
||||
border-radius: 0.5rem;
|
||||
user-select: none;
|
||||
|
||||
.bar {
|
||||
margin-right: 0.25rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
opacity: 0.4;
|
||||
cursor: grabbing;
|
||||
}
|
||||
.color {
|
||||
margin-right: 0.75rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tool {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
.states + .states {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user