mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-21 09:07:50 +02:00
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getStates } from '@hcengineering/task'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Button, Label, SelectPopup, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import presentation, { getClient } from '@hcengineering/presentation'
|
||||
import tracker from '../../plugin'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import IssueStatusIcon from '../issues/IssueStatusIcon.svelte'
|
||||
import { StatusPresenter, statusStore } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import IssueStatusIcon from '../issues/IssueStatusIcon.svelte'
|
||||
|
||||
export let projectId: Ref<Project>
|
||||
export let issues: Issue[]
|
||||
@@ -14,13 +15,18 @@
|
||||
|
||||
let processing = false
|
||||
|
||||
let _space: Project | undefined = undefined
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(tracker.class.Project, { space: projectId }, (result) => {
|
||||
_space = result[0]
|
||||
})
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let newStatus: IssueStatus =
|
||||
$statusStore
|
||||
.getDocs()
|
||||
.find((s) => s._id !== status._id && s.category === status.category && s.space === projectId) ??
|
||||
$statusStore.getDocs().find((s) => s._id !== status._id && s.space === projectId) ??
|
||||
$: newStatus =
|
||||
statuses.find((s) => s._id !== status._id && s.category === status.category && s.space === projectId) ??
|
||||
statuses.find((s) => s._id !== status._id && s.space === projectId) ??
|
||||
status
|
||||
|
||||
async function remove () {
|
||||
@@ -32,12 +38,14 @@
|
||||
})
|
||||
})
|
||||
)
|
||||
await client.remove(status)
|
||||
if (_space) {
|
||||
await client.update(_space, { $pull: { states: status._id } })
|
||||
}
|
||||
processing = false
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
$: statuses = $statusStore.filter((it) => it.space === projectId && it._id !== status._id)
|
||||
$: statuses = getStates(_space, $statusStore).filter((p) => p._id !== status._id)
|
||||
$: statusesInfo = statuses?.map((s) => {
|
||||
return {
|
||||
id: s._id,
|
||||
@@ -55,7 +63,7 @@
|
||||
{ value: statusesInfo, placeholder: tracker.string.SetStatus, searchable: true },
|
||||
eventToHTMLElement(event),
|
||||
(val) => {
|
||||
newStatus = $statusStore.getIdMap().get(val) ?? newStatus
|
||||
newStatus = $statusStore.get(val) ?? newStatus
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -74,9 +82,7 @@
|
||||
<Button kind={'link'} justify={'left'} width={'10rem'} on:click={handleStatusEditorOpened}>
|
||||
<span slot="content" class="flex-row-center pointer-events-none">
|
||||
{#if newStatus}
|
||||
<IssueStatusIcon value={newStatus} size="inline" />
|
||||
{/if}
|
||||
{#if newStatus}
|
||||
<IssueStatusIcon value={newStatus} space={projectId} size="inline" />
|
||||
<span class="overflow-label disabled ml-1">
|
||||
{newStatus.name}
|
||||
</span>
|
||||
|
||||
@@ -13,13 +13,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { IssueStatus } from '@hcengineering/tracker'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Icon, IconCircles, IconDelete, IconEdit, Label, tooltip } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import IssueStatusIcon from '../issues/IssueStatusIcon.svelte'
|
||||
|
||||
export let value: IssueStatus
|
||||
export let space: Ref<Project>
|
||||
export let isDefault = false
|
||||
export let isSingle = true
|
||||
|
||||
@@ -36,7 +38,7 @@
|
||||
<IconCircles size={'small'} />
|
||||
</div>
|
||||
<div class="flex-no-shrink">
|
||||
<IssueStatusIcon {value} size="small" />
|
||||
<IssueStatusIcon {value} size="small" {space} />
|
||||
</div>
|
||||
<span class="caption-color ml-2">{value.name}</span>
|
||||
{#if value.description}
|
||||
|
||||
@@ -14,16 +14,17 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { Class, Data, Ref, SortingOrder, StatusCategory } from '@hcengineering/core'
|
||||
import { createQuery, getClient, MessageBox, Card } from '@hcengineering/presentation'
|
||||
import { calcRank, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Button, closeTooltip, ExpandCollapse, IconAdd, Label, Loading, Scroller, showPopup } from '@hcengineering/ui'
|
||||
import { Card, MessageBox, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createState, getStates } from '@hcengineering/task'
|
||||
import { IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Button, ExpandCollapse, IconAdd, Label, Loading, Scroller, closeTooltip, showPopup } from '@hcengineering/ui'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { flip } from 'svelte/animate'
|
||||
import tracker from '../../plugin'
|
||||
import RemoveStatus from './RemoveStatus.svelte'
|
||||
import StatusEditor from './StatusEditor.svelte'
|
||||
import StatusPresenter from './StatusPresenter.svelte'
|
||||
import RemoveStatus from './RemoveStatus.svelte'
|
||||
import { statusStore } from '@hcengineering/view-resources'
|
||||
|
||||
export let projectId: Ref<Project>
|
||||
export let projectClass: Ref<Class<Project>>
|
||||
@@ -56,18 +57,16 @@
|
||||
}
|
||||
|
||||
async function addStatus () {
|
||||
if (editingStatus?.name && editingStatus?.category) {
|
||||
const [prevStatus, nextStatus] = getSiblingsForNewStatus(editingStatus.category)
|
||||
|
||||
if (editingStatus?.name && editingStatus?.category && project) {
|
||||
isSaving = true
|
||||
await client.createDoc(tracker.class.IssueStatus, projectId, {
|
||||
const id = await createState(client, tracker.class.IssueStatus, {
|
||||
ofAttribute: tracker.attribute.IssueStatus,
|
||||
name: editingStatus.name,
|
||||
description: editingStatus.description,
|
||||
color: editingStatus.color,
|
||||
category: editingStatus.category,
|
||||
rank: calcRank(prevStatus, nextStatus)
|
||||
category: editingStatus.category
|
||||
})
|
||||
await client.update(project, { $push: { states: id } })
|
||||
isSaving = false
|
||||
}
|
||||
|
||||
@@ -77,16 +76,13 @@
|
||||
async function editStatus () {
|
||||
if (statusCategories && editingStatus?.name && editingStatus?.category && '_id' in editingStatus) {
|
||||
const statusId = '_id' in editingStatus ? editingStatus._id : undefined
|
||||
const status = statusId && $statusStore.getIdMap().get(statusId)
|
||||
const status = statusId && $statusStore.get(statusId)
|
||||
|
||||
if (!status) {
|
||||
return
|
||||
}
|
||||
|
||||
const updates: Partial<Data<IssueStatus>> = {}
|
||||
if (status.name !== editingStatus.name) {
|
||||
updates.name = editingStatus.name
|
||||
}
|
||||
if (status.description !== editingStatus.description) {
|
||||
updates.description = editingStatus.description
|
||||
}
|
||||
@@ -101,8 +97,29 @@
|
||||
updates.color = editingStatus.color
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
if (status.name !== editingStatus.name) {
|
||||
isSaving = true
|
||||
const newId = await createState(client, tracker.class.IssueStatus, {
|
||||
ofAttribute: tracker.attribute.IssueStatus,
|
||||
name: editingStatus.name,
|
||||
description: editingStatus.description,
|
||||
color: editingStatus.color,
|
||||
category: editingStatus.category
|
||||
})
|
||||
projectStatuses = projectStatuses.map((s) => (s._id === statusId ? { ...s, _id: newId } : s))
|
||||
if (project) {
|
||||
const issues = await client.findAll(tracker.class.Issue, { status: status._id, space: projectId })
|
||||
await Promise.all(
|
||||
issues.map(async (p) => {
|
||||
await client.update(p, {
|
||||
status: newId
|
||||
})
|
||||
})
|
||||
)
|
||||
await client.update(project, { states: projectStatuses.map((s) => s._id) })
|
||||
}
|
||||
isSaving = false
|
||||
} else if (Object.keys(updates).length > 0) {
|
||||
isSaving = true
|
||||
await client.update(status, updates)
|
||||
isSaving = false
|
||||
@@ -136,7 +153,7 @@
|
||||
async (result) => {
|
||||
if (result && project) {
|
||||
isSaving = true
|
||||
await client.removeDoc(status._class, status.space, status._id)
|
||||
await client.update(project, { $pull: { states: status._id } })
|
||||
|
||||
if (project.defaultIssueStatus === status._id) {
|
||||
const newDefaultStatus = projectStatuses.find(
|
||||
@@ -166,23 +183,22 @@
|
||||
}
|
||||
|
||||
function handleDragOver (ev: DragEvent, status: IssueStatus) {
|
||||
hoveringStatus = status
|
||||
ev.preventDefault()
|
||||
if (status.category === draggingStatus?.category) {
|
||||
hoveringStatus = status
|
||||
ev.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDrop (toItem: IssueStatus) {
|
||||
if (draggingStatus != null && draggingStatus?._id !== toItem._id) {
|
||||
const fromIndex = getStatusIndex(draggingStatus)
|
||||
const toIndex = getStatusIndex(toItem)
|
||||
const [prev, next] = [
|
||||
projectStatuses[fromIndex < toIndex ? toIndex : toIndex - 1],
|
||||
projectStatuses[fromIndex < toIndex ? toIndex + 1 : toIndex]
|
||||
]
|
||||
|
||||
const item = projectStatuses.splice(fromIndex, 1)
|
||||
isSaving = true
|
||||
let newCategory = {}
|
||||
if (draggingStatus?.category !== toItem.category) newCategory = { category: toItem.category }
|
||||
await client.update(draggingStatus, { rank: calcRank(prev, next), ...newCategory })
|
||||
projectStatuses = [...projectStatuses.slice(0, toIndex), ...item, ...projectStatuses.slice(toIndex)]
|
||||
if (project) {
|
||||
await client.update(project, { states: projectStatuses.map((s) => s._id) })
|
||||
}
|
||||
isSaving = false
|
||||
}
|
||||
|
||||
@@ -193,33 +209,6 @@
|
||||
return projectStatuses.findIndex(({ _id }) => _id === status._id) ?? -1
|
||||
}
|
||||
|
||||
function getSiblingsForNewStatus (
|
||||
categoryId: StatusCategory['_id']
|
||||
): readonly [] | readonly [IssueStatus | undefined, IssueStatus | undefined] {
|
||||
const categoryStatuses = projectStatuses.filter((s) => s.category === categoryId)
|
||||
if (categoryStatuses.length > 0) {
|
||||
const prev = categoryStatuses[categoryStatuses.length - 1]
|
||||
const next = projectStatuses[getStatusIndex(prev) + 1]
|
||||
|
||||
return [prev, next]
|
||||
}
|
||||
|
||||
const category = statusCategories?.find(({ _id }) => _id === categoryId)
|
||||
if (!category) {
|
||||
return []
|
||||
}
|
||||
|
||||
const { order } = category
|
||||
const prev = projectStatuses.findLast(
|
||||
(s) => s.$lookup?.category?.order !== undefined && s.$lookup.category.order < order
|
||||
)
|
||||
const next = projectStatuses.find(
|
||||
(s) => s.$lookup?.category?.order !== undefined && s.$lookup.category.order > order
|
||||
)
|
||||
|
||||
return [prev, next]
|
||||
}
|
||||
|
||||
function resetDrag () {
|
||||
draggingStatus = null
|
||||
hoveringStatus = null
|
||||
@@ -227,7 +216,7 @@
|
||||
|
||||
$: projectQuery.query(projectClass, { _id: projectId }, (result) => ([project] = result), { limit: 1 })
|
||||
$: updateStatusCategories()
|
||||
$: projectStatuses = $statusStore.getDocs().filter((status) => status.space === projectId)
|
||||
$: projectStatuses = getStates(project, $statusStore)
|
||||
</script>
|
||||
|
||||
<Card
|
||||
@@ -266,15 +255,17 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-col">
|
||||
{#each statuses as status, _ (status._id)}
|
||||
{#each statuses as status (status._id)}
|
||||
<div
|
||||
class="row"
|
||||
class:is-dragged-over-up={draggingStatus &&
|
||||
status._id === hoveringStatus?._id &&
|
||||
status.rank < draggingStatus.rank}
|
||||
draggingStatus.category === status.category &&
|
||||
getStatusIndex(status) < getStatusIndex(draggingStatus)}
|
||||
class:is-dragged-over-down={draggingStatus &&
|
||||
status._id === hoveringStatus?._id &&
|
||||
status.rank > draggingStatus.rank}
|
||||
draggingStatus.category === status.category &&
|
||||
getStatusIndex(status) > getStatusIndex(draggingStatus)}
|
||||
draggable={!isSingle}
|
||||
animate:flip={{ duration: 200 }}
|
||||
on:dragstart={(ev) => handleDragStart(ev, status)}
|
||||
@@ -292,6 +283,7 @@
|
||||
/>
|
||||
{:else}
|
||||
<StatusPresenter
|
||||
space={projectId}
|
||||
value={status}
|
||||
isDefault={status._id === project.defaultIssueStatus}
|
||||
{isSingle}
|
||||
|
||||
Reference in New Issue
Block a user