mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-22 16:27:22 +00:00
Add "Create Sub-issue" component (#2004)
Signed-off-by: Sergei Ogorelkov <sergei.ogorelkov@xored.com>
This commit is contained in:
parent
d4b9353d29
commit
f029789957
@ -68,11 +68,15 @@
|
|||||||
computeSize(input)
|
computeSize(input)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export function focusInput () {
|
||||||
|
input?.focus()
|
||||||
|
}
|
||||||
|
|
||||||
// Focusable control with index
|
// Focusable control with index
|
||||||
export let focusIndex = -1
|
export let focusIndex = -1
|
||||||
const { idx, focusManager } = registerFocus(focusIndex, {
|
const { idx, focusManager } = registerFocus(focusIndex, {
|
||||||
focus: () => {
|
focus: () => {
|
||||||
input?.focus()
|
focusInput()
|
||||||
return input != null
|
return input != null
|
||||||
},
|
},
|
||||||
isFocus: () => document.activeElement === input
|
isFocus: () => document.activeElement === input
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import core, { AttachedData, Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
import core, { AttachedData, Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
||||||
import presentation, { Card, createQuery, getClient, SpaceSelector, UserBox } from '@anticrm/presentation'
|
import presentation, { Card, createQuery, getClient, SpaceSelector } from '@anticrm/presentation'
|
||||||
import { StyledTextBox } from '@anticrm/text-editor'
|
import { StyledTextBox } from '@anticrm/text-editor'
|
||||||
import { calcRank, Issue, IssuePriority, IssueStatus, Project, Team } from '@anticrm/tracker'
|
import { calcRank, Issue, IssuePriority, IssueStatus, Project, Team } from '@anticrm/tracker'
|
||||||
import {
|
import {
|
||||||
@ -36,7 +36,8 @@
|
|||||||
import PrioritySelector from './PrioritySelector.svelte'
|
import PrioritySelector from './PrioritySelector.svelte'
|
||||||
import ProjectSelector from './ProjectSelector.svelte'
|
import ProjectSelector from './ProjectSelector.svelte'
|
||||||
import SetDueDateActionPopup from './SetDueDateActionPopup.svelte'
|
import SetDueDateActionPopup from './SetDueDateActionPopup.svelte'
|
||||||
import StatusSelector from './StatusSelector.svelte'
|
import AssigneeEditor from './issues/AssigneeEditor.svelte'
|
||||||
|
import StatusEditor from './issues/StatusEditor.svelte'
|
||||||
|
|
||||||
export let space: Ref<Team>
|
export let space: Ref<Team>
|
||||||
export let status: Ref<IssueStatus> | undefined = undefined
|
export let status: Ref<IssueStatus> | undefined = undefined
|
||||||
@ -81,15 +82,10 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const team = await client.findOne(
|
const team = await client.findOne(tracker.class.Team, { _id: teamId })
|
||||||
tracker.class.Team,
|
|
||||||
{ _id: teamId },
|
|
||||||
{ lookup: { defaultIssueStatus: tracker.class.IssueStatus } }
|
|
||||||
)
|
|
||||||
const teamDefaultIssueStatusId = team?.$lookup?.defaultIssueStatus?._id
|
|
||||||
|
|
||||||
if (teamDefaultIssueStatusId) {
|
if (team?.defaultIssueStatus) {
|
||||||
object.status = teamDefaultIssueStatusId
|
object.status = team.defaultIssueStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,14 +196,6 @@
|
|||||||
object.priority = newPriority
|
object.priority = newPriority
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleStatusChanged = (statusId: Ref<IssueStatus> | undefined) => {
|
|
||||||
if (statusId === undefined) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
object.status = statusId
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleProjectIdChanged = (projectId: Ref<Project> | null | undefined) => {
|
const handleProjectIdChanged = (projectId: Ref<Project> | null | undefined) => {
|
||||||
if (projectId === undefined) {
|
if (projectId === undefined) {
|
||||||
return
|
return
|
||||||
@ -258,23 +246,31 @@
|
|||||||
/>
|
/>
|
||||||
<svelte:fragment slot="pool">
|
<svelte:fragment slot="pool">
|
||||||
{#if issueStatuses}
|
{#if issueStatuses}
|
||||||
<StatusSelector selectedStatusId={object.status} statuses={issueStatuses} onStatusChange={handleStatusChanged} />
|
<StatusEditor
|
||||||
<PrioritySelector priority={object.priority} onPriorityChange={handlePriorityChanged} />
|
value={object}
|
||||||
<UserBox
|
statuses={issueStatuses}
|
||||||
_class={contact.class.Employee}
|
kind="no-border"
|
||||||
label={tracker.string.Assignee}
|
width="min-content"
|
||||||
placeholder={tracker.string.AssignTo}
|
size="small"
|
||||||
bind:value={currentAssignee}
|
shouldShowLabel={true}
|
||||||
allowDeselect
|
tooltipFill={false}
|
||||||
titleDeselect={tracker.string.Unassigned}
|
on:change={({ detail }) => (object.status = detail)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<PrioritySelector priority={object.priority} onPriorityChange={handlePriorityChanged} />
|
||||||
|
<AssigneeEditor
|
||||||
|
value={object}
|
||||||
|
size="small"
|
||||||
|
kind="no-border"
|
||||||
|
tooltipFill={false}
|
||||||
|
on:change={({ detail }) => (currentAssignee = detail)}
|
||||||
|
/>
|
||||||
|
<!-- <Button
|
||||||
label={tracker.string.Labels}
|
label={tracker.string.Labels}
|
||||||
icon={tracker.icon.Labels}
|
icon={tracker.icon.Labels}
|
||||||
width="min-content"
|
width="min-content"
|
||||||
size="small"
|
size="small"
|
||||||
kind="no-border"
|
kind="no-border"
|
||||||
/>
|
/> -->
|
||||||
<ProjectSelector value={object.project} onProjectIdChange={handleProjectIdChanged} />
|
<ProjectSelector value={object.project} onProjectIdChange={handleProjectIdChanged} />
|
||||||
{#if object.dueDate !== null}
|
{#if object.dueDate !== null}
|
||||||
<DatePresenter bind:value={object.dueDate} editable />
|
<DatePresenter bind:value={object.dueDate} editable />
|
||||||
|
@ -13,37 +13,39 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import { Ref } from '@anticrm/core'
|
import { AttachedData, Ref } from '@anticrm/core'
|
||||||
import { getClient, UserBox } from '@anticrm/presentation'
|
import { getClient, UserBox } from '@anticrm/presentation'
|
||||||
import { Issue } from '@anticrm/tracker'
|
import { Issue } from '@anticrm/tracker'
|
||||||
import { Tooltip } from '@anticrm/ui'
|
import { ButtonKind, ButtonSize, Tooltip, TooltipAlignment } from '@anticrm/ui'
|
||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
|
|
||||||
export let value: Issue
|
export let value: Issue | AttachedData<Issue>
|
||||||
|
export let size: ButtonSize = 'large'
|
||||||
|
export let kind: ButtonKind = 'link'
|
||||||
|
export let tooltipAlignment: TooltipAlignment | undefined = undefined
|
||||||
|
export let tooltipFill = true
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
const handleAssigneeChanged = async (newAssignee: Ref<Employee> | undefined) => {
|
const handleAssigneeChanged = async (newAssignee: Ref<Employee> | undefined) => {
|
||||||
if (newAssignee === undefined || value.assignee === newAssignee) {
|
if (newAssignee === undefined || value.assignee === newAssignee) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.updateCollection(
|
dispatch('change', newAssignee)
|
||||||
value._class,
|
|
||||||
value.space,
|
if ('_id' in value) {
|
||||||
value._id,
|
await client.update(value, { assignee: newAssignee })
|
||||||
value.attachedTo,
|
}
|
||||||
value.attachedToClass,
|
|
||||||
value.collection,
|
|
||||||
{ assignee: newAssignee }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<Tooltip label={tracker.string.AssignTo} fill>
|
<Tooltip label={tracker.string.AssignTo} direction={tooltipAlignment} fill={tooltipFill}>
|
||||||
<UserBox
|
<UserBox
|
||||||
_class={contact.class.Employee}
|
_class={contact.class.Employee}
|
||||||
label={tracker.string.Assignee}
|
label={tracker.string.Assignee}
|
||||||
@ -51,8 +53,8 @@
|
|||||||
value={value.assignee}
|
value={value.assignee}
|
||||||
allowDeselect
|
allowDeselect
|
||||||
titleDeselect={tracker.string.Unassigned}
|
titleDeselect={tracker.string.Unassigned}
|
||||||
size="large"
|
{size}
|
||||||
kind="link"
|
{kind}
|
||||||
width="100%"
|
width="100%"
|
||||||
justify="left"
|
justify="left"
|
||||||
on:change={({ detail }) => handleAssigneeChanged(detail)}
|
on:change={({ detail }) => handleAssigneeChanged(detail)}
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Ref, WithLookup } from '@anticrm/core'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import { AttachedData, Ref, WithLookup } from '@anticrm/core'
|
||||||
import { Issue, IssueStatus } from '@anticrm/tracker'
|
import { Issue, IssueStatus } from '@anticrm/tracker'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import { Tooltip, TooltipAlignment } from '@anticrm/ui'
|
import { Tooltip, TooltipAlignment } from '@anticrm/ui'
|
||||||
@ -21,11 +22,12 @@
|
|||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import StatusSelector from '../StatusSelector.svelte'
|
import StatusSelector from '../StatusSelector.svelte'
|
||||||
|
|
||||||
export let value: Issue
|
export let value: Issue | AttachedData<Issue>
|
||||||
export let statuses: WithLookup<IssueStatus>[]
|
export let statuses: WithLookup<IssueStatus>[]
|
||||||
export let isEditable: boolean = true
|
export let isEditable: boolean = true
|
||||||
export let shouldShowLabel: boolean = false
|
export let shouldShowLabel: boolean = false
|
||||||
export let tooltipAlignment: TooltipAlignment | undefined = undefined
|
export let tooltipAlignment: TooltipAlignment | undefined = undefined
|
||||||
|
export let tooltipFill = true
|
||||||
|
|
||||||
export let kind: ButtonKind = 'link'
|
export let kind: ButtonKind = 'link'
|
||||||
export let size: ButtonSize = 'large'
|
export let size: ButtonSize = 'large'
|
||||||
@ -33,27 +35,24 @@
|
|||||||
export let width: string | undefined = '100%'
|
export let width: string | undefined = '100%'
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
const handleStatusChanged = async (newStatus: Ref<IssueStatus> | undefined) => {
|
const handleStatusChanged = async (newStatus: Ref<IssueStatus> | undefined) => {
|
||||||
if (!isEditable || newStatus === undefined || value.status === newStatus) {
|
if (!isEditable || newStatus === undefined || value.status === newStatus) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.updateCollection(
|
dispatch('change', newStatus)
|
||||||
value._class,
|
|
||||||
value.space,
|
if ('_id' in value) {
|
||||||
value._id,
|
await client.update(value, { status: newStatus })
|
||||||
value.attachedTo,
|
}
|
||||||
value.attachedToClass,
|
|
||||||
value.collection,
|
|
||||||
{ status: newStatus }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
{#if isEditable}
|
{#if isEditable}
|
||||||
<Tooltip label={tracker.string.SetStatus} direction={tooltipAlignment} fill>
|
<Tooltip label={tracker.string.SetStatus} direction={tooltipAlignment} fill={tooltipFill}>
|
||||||
<StatusSelector
|
<StatusSelector
|
||||||
{kind}
|
{kind}
|
||||||
{size}
|
{size}
|
||||||
|
@ -0,0 +1,164 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { createEventDispatcher } from 'svelte'
|
||||||
|
import core, { AttachedData, Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
||||||
|
import presentation, { getClient } from '@anticrm/presentation'
|
||||||
|
import { StyledTextArea } from '@anticrm/text-editor'
|
||||||
|
import { IssueStatus, IssuePriority, Issue, Team, calcRank } from '@anticrm/tracker'
|
||||||
|
import { Button, EditBox } from '@anticrm/ui'
|
||||||
|
import tracker from '../../../plugin'
|
||||||
|
import AssigneeEditor from '../AssigneeEditor.svelte'
|
||||||
|
import StatusEditor from '../StatusEditor.svelte'
|
||||||
|
|
||||||
|
export let parentIssue: Issue
|
||||||
|
export let issueStatuses: WithLookup<IssueStatus>[]
|
||||||
|
export let currentTeam: Team
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const client = getClient()
|
||||||
|
|
||||||
|
let newIssue: AttachedData<Issue> = getIssueDefaults()
|
||||||
|
let thisRef: HTMLDivElement
|
||||||
|
let focusIssueTitle: () => void
|
||||||
|
|
||||||
|
function getIssueDefaults (): AttachedData<Issue> {
|
||||||
|
return {
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
assignee: null,
|
||||||
|
project: null,
|
||||||
|
number: 0,
|
||||||
|
rank: '',
|
||||||
|
status: '' as Ref<IssueStatus>,
|
||||||
|
priority: IssuePriority.NoPriority,
|
||||||
|
dueDate: null,
|
||||||
|
comments: 0,
|
||||||
|
subIssues: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetToDefaults () {
|
||||||
|
newIssue = getIssueDefaults()
|
||||||
|
focusIssueTitle?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTitle (value: string) {
|
||||||
|
return value.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function close () {
|
||||||
|
dispatch('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createIssue () {
|
||||||
|
if (!canSave) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const space = currentTeam._id
|
||||||
|
const lastOne = await client.findOne<Issue>(tracker.class.Issue, {}, { sort: { rank: SortingOrder.Descending } })
|
||||||
|
const incResult = await client.updateDoc(
|
||||||
|
tracker.class.Team,
|
||||||
|
core.space.Space,
|
||||||
|
space,
|
||||||
|
{ $inc: { sequence: 1 } },
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
const value: AttachedData<Issue> = {
|
||||||
|
...newIssue,
|
||||||
|
title: getTitle(newIssue.title),
|
||||||
|
number: (incResult as any).object.sequence,
|
||||||
|
rank: calcRank(lastOne, undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.addCollection(tracker.class.Issue, space, parentIssue._id, parentIssue._class, 'subIssues', value)
|
||||||
|
resetToDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
$: thisRef && thisRef.scrollIntoView({ behavior: 'smooth' })
|
||||||
|
$: canSave = getTitle(newIssue.title ?? '').length > 0
|
||||||
|
$: if (!newIssue.status && currentTeam?.defaultIssueStatus) {
|
||||||
|
newIssue.status = currentTeam.defaultIssueStatus
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={thisRef} class="flex-col root">
|
||||||
|
<div class="flex-row-top">
|
||||||
|
<StatusEditor
|
||||||
|
value={newIssue}
|
||||||
|
statuses={issueStatuses}
|
||||||
|
kind="transparent"
|
||||||
|
width="min-content"
|
||||||
|
size="medium"
|
||||||
|
tooltipFill={false}
|
||||||
|
tooltipAlignment="bottom"
|
||||||
|
on:change={({ detail }) => (newIssue.status = detail)}
|
||||||
|
/>
|
||||||
|
<div class="w-full flex-col content">
|
||||||
|
<EditBox
|
||||||
|
bind:value={newIssue.title}
|
||||||
|
bind:focusInput={focusIssueTitle}
|
||||||
|
maxWidth="33rem"
|
||||||
|
placeholder={tracker.string.IssueTitlePlaceholder}
|
||||||
|
focus
|
||||||
|
/>
|
||||||
|
<div class="mt-4">
|
||||||
|
<StyledTextArea
|
||||||
|
bind:content={newIssue.description}
|
||||||
|
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||||
|
showButtons={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 flex-between">
|
||||||
|
<div class="buttons-group xsmall-gap">
|
||||||
|
<!-- <SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space /> -->
|
||||||
|
<AssigneeEditor
|
||||||
|
value={newIssue}
|
||||||
|
size="small"
|
||||||
|
kind="no-border"
|
||||||
|
tooltipFill={false}
|
||||||
|
on:change={({ detail }) => (newIssue.assignee = detail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="buttons-group small-gap">
|
||||||
|
<Button label={presentation.string.Cancel} size="small" kind="transparent" on:click={close} />
|
||||||
|
<Button
|
||||||
|
disabled={!canSave}
|
||||||
|
label={presentation.string.Save}
|
||||||
|
size="small"
|
||||||
|
kind="no-border"
|
||||||
|
on:click={createIssue}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.root {
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
background-color: var(--theme-bg-accent-color);
|
||||||
|
border: 1px solid var(--theme-button-border-enabled);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 0.3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -239,7 +239,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<SubIssues {issue} {issueStatuses} />
|
{#key issue._id}
|
||||||
|
<SubIssues {issue} {issueStatuses} {currentTeam} />
|
||||||
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<AttachmentDocList value={issue} />
|
<AttachmentDocList value={issue} />
|
||||||
|
@ -15,14 +15,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { SortingOrder, WithLookup } from '@anticrm/core'
|
import { SortingOrder, WithLookup } from '@anticrm/core'
|
||||||
import { createQuery, getClient } from '@anticrm/presentation'
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
import { calcRank, Issue, IssueStatus } from '@anticrm/tracker'
|
import { calcRank, Issue, IssueStatus, Team } from '@anticrm/tracker'
|
||||||
import { Button, Spinner, ExpandCollapse } from '@anticrm/ui'
|
import { Button, Spinner, ExpandCollapse, Tooltip, closeTooltip, IconAdd } from '@anticrm/ui'
|
||||||
import tracker from '../../../plugin'
|
import tracker from '../../../plugin'
|
||||||
import Collapsed from '../../icons/Collapsed.svelte'
|
import Collapsed from '../../icons/Collapsed.svelte'
|
||||||
import Expanded from '../../icons/Expanded.svelte'
|
import Expanded from '../../icons/Expanded.svelte'
|
||||||
|
import CreateSubIssue from './CreateSubIssue.svelte'
|
||||||
import SubIssueList from './SubIssueList.svelte'
|
import SubIssueList from './SubIssueList.svelte'
|
||||||
|
|
||||||
export let issue: Issue
|
export let issue: Issue
|
||||||
|
export let currentTeam: Team | undefined
|
||||||
export let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
export let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||||
|
|
||||||
const subIssuesQuery = createQuery()
|
const subIssuesQuery = createQuery()
|
||||||
@ -30,7 +32,7 @@
|
|||||||
|
|
||||||
let subIssues: Issue[] | undefined
|
let subIssues: Issue[] | undefined
|
||||||
let isCollapsed = false
|
let isCollapsed = false
|
||||||
// let isCreating = false
|
let isCreating = false
|
||||||
|
|
||||||
async function handleIssueSwap (ev: CustomEvent<{ fromIndex: number; toIndex: number }>) {
|
async function handleIssueSwap (ev: CustomEvent<{ fromIndex: number; toIndex: number }>) {
|
||||||
if (subIssues) {
|
if (subIssues) {
|
||||||
@ -60,11 +62,14 @@
|
|||||||
kind="transparent"
|
kind="transparent"
|
||||||
label={tracker.string.SubIssuesList}
|
label={tracker.string.SubIssuesList}
|
||||||
labelParams={{ subIssues: issue.subIssues }}
|
labelParams={{ subIssues: issue.subIssues }}
|
||||||
on:click={() => (isCollapsed = !isCollapsed)}
|
on:click={() => {
|
||||||
|
isCollapsed = !isCollapsed
|
||||||
|
isCreating = false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- <Tooltip label={tracker.string.AddSubIssues} props={{ subIssues: 1 }} direction="bottom">
|
<Tooltip label={tracker.string.AddSubIssues} props={{ subIssues: 1 }} direction="bottom">
|
||||||
<Button
|
<Button
|
||||||
width="min-content"
|
width="min-content"
|
||||||
icon={hasSubIssues ? IconAdd : undefined}
|
icon={hasSubIssues ? IconAdd : undefined}
|
||||||
@ -75,25 +80,33 @@
|
|||||||
on:click={() => {
|
on:click={() => {
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
isCreating = true
|
isCreating = true
|
||||||
|
isCollapsed = false
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip> -->
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
{#if hasSubIssues}
|
<div class="mt-1">
|
||||||
<div class="mt-1">
|
{#if subIssues && issueStatuses && currentTeam}
|
||||||
{#if subIssues && issueStatuses}
|
<ExpandCollapse isExpanded={!isCollapsed} duration={400}>
|
||||||
<div class="list" class:collapsed={isCollapsed}>
|
{#if hasSubIssues}
|
||||||
<ExpandCollapse isExpanded={!isCollapsed} duration={400}>
|
<div class="list" class:collapsed={isCollapsed}>
|
||||||
<SubIssueList issues={subIssues} {issueStatuses} on:move={handleIssueSwap} />
|
<SubIssueList issues={subIssues} {issueStatuses} on:move={handleIssueSwap} />
|
||||||
</ExpandCollapse>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{:else}
|
</ExpandCollapse>
|
||||||
<div class="flex-center pt-3">
|
<ExpandCollapse isExpanded={!isCollapsed} duration={400}>
|
||||||
<Spinner />
|
{#if isCreating}
|
||||||
</div>
|
<div class="pt-4">
|
||||||
{/if}
|
<CreateSubIssue parentIssue={issue} {issueStatuses} {currentTeam} on:close={() => (isCreating = false)} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
</ExpandCollapse>
|
||||||
|
{:else}
|
||||||
|
<div class="flex-center pt-3">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.list {
|
.list {
|
||||||
|
Loading…
Reference in New Issue
Block a user