mirror of
https://github.com/hcengineering/platform.git
synced 2026-10-01 22:15:04 +02:00
TSK-915: Tracker status (#2802)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
@@ -73,8 +73,7 @@ import {
|
||||
Project,
|
||||
TimeReportDayType,
|
||||
TimeSpendReport,
|
||||
trackerId,
|
||||
WorkDayLength
|
||||
trackerId
|
||||
} from '@hcengineering/tracker'
|
||||
import { KeyBinding, ViewOptionsModel } from '@hcengineering/view'
|
||||
import tracker from './plugin'
|
||||
@@ -92,11 +91,14 @@ export const DOMAIN_TRACKER = 'tracker' as Domain
|
||||
*/
|
||||
@Model(tracker.class.IssueStatus, core.class.AttachedDoc, DOMAIN_TRACKER)
|
||||
export class TIssueStatus extends TAttachedDoc implements IssueStatus {
|
||||
name!: string
|
||||
@Index(IndexKind.Indexed)
|
||||
name!: string
|
||||
|
||||
description?: string
|
||||
color?: number
|
||||
|
||||
@Prop(TypeRef(tracker.class.IssueStatusCategory), tracker.string.StatusCategory)
|
||||
@Index(IndexKind.Indexed)
|
||||
category!: Ref<IssueStatusCategory>
|
||||
|
||||
@Prop(TypeString(), tracker.string.Rank)
|
||||
@@ -161,10 +163,6 @@ export class TTypeSprintStatus extends TType {}
|
||||
@Model(tracker.class.Project, core.class.Space, DOMAIN_SPACE)
|
||||
@UX(tracker.string.Project, tracker.icon.Project, 'Project')
|
||||
export class TProject extends TSpace implements Project {
|
||||
@Prop(TypeString(), tracker.string.Title)
|
||||
@Index(IndexKind.FullText)
|
||||
reamLogo!: IntlString
|
||||
|
||||
@Prop(TypeString(), tracker.string.Identifier)
|
||||
@Index(IndexKind.FullText)
|
||||
identifier!: IntlString
|
||||
@@ -182,7 +180,6 @@ export class TProject extends TSpace implements Project {
|
||||
@Prop(TypeRef(contact.class.Employee), tracker.string.DefaultAssignee)
|
||||
defaultAssignee!: Ref<Employee>
|
||||
|
||||
declare workDayLength: WorkDayLength
|
||||
declare defaultTimeReportDay: TimeReportDayType
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,7 @@ import {
|
||||
IssueTemplate,
|
||||
IssueTemplateChild,
|
||||
Project,
|
||||
TimeReportDayType,
|
||||
WorkDayLength
|
||||
TimeReportDayType
|
||||
} from '@hcengineering/tracker'
|
||||
import { DOMAIN_TRACKER } from '.'
|
||||
import tracker from './plugin'
|
||||
@@ -126,8 +125,7 @@ async function createDefaultProject (tx: TxOperations): Promise<void> {
|
||||
issueStatuses: 0,
|
||||
defaultIssueStatus: defaultStatusId,
|
||||
defaultTimeReportDay: TimeReportDayType.PreviousWorkDay,
|
||||
defaultAssignee: undefined,
|
||||
workDayLength: WorkDayLength.EIGHT_HOURS
|
||||
defaultAssignee: undefined
|
||||
},
|
||||
tracker.project.DefaultProject
|
||||
)
|
||||
@@ -157,14 +155,12 @@ async function fixProjectsIssueStatusesOrder (tx: TxOperations): Promise<void> {
|
||||
|
||||
async function upgradeProjectSettings (tx: TxOperations): Promise<void> {
|
||||
const projects = await tx.findAll(tracker.class.Project, {
|
||||
defaultTimeReportDay: { $exists: false },
|
||||
workDayLength: { $exists: false }
|
||||
defaultTimeReportDay: { $exists: false }
|
||||
})
|
||||
await Promise.all(
|
||||
projects.map((project) =>
|
||||
tx.update(project, {
|
||||
defaultTimeReportDay: TimeReportDayType.PreviousWorkDay,
|
||||
workDayLength: WorkDayLength.EIGHT_HOURS
|
||||
defaultTimeReportDay: TimeReportDayType.PreviousWorkDay
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
@@ -280,7 +280,6 @@
|
||||
"DefaultTimeReportDay": "Select default day for time report",
|
||||
"DefaultAssignee": "Select default assignee for issues",
|
||||
|
||||
"WorkDayLength": "Select length of working day",
|
||||
"SevenHoursLength": "Seven Hours",
|
||||
"EightHoursLength": "Eight Hours",
|
||||
"CreatedOn": "Created on",
|
||||
|
||||
@@ -280,7 +280,6 @@
|
||||
"DefaultTimeReportDay": "Выберите день для временного отчета по умолчанию",
|
||||
"DefaultAssignee": "Выберите исполнителя по умолчанию",
|
||||
|
||||
"WorkDayLength": "Выберите длину рабочего дня",
|
||||
"SevenHoursLength": "Семь Часов",
|
||||
"EightHoursLength": "Восемь Часов",
|
||||
"CreatedOn": "Создана",
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
fillDefaults,
|
||||
generateId,
|
||||
Ref,
|
||||
SortingOrder,
|
||||
WithLookup
|
||||
SortingOrder
|
||||
} from '@hcengineering/core'
|
||||
import { getResource, translate } from '@hcengineering/platform'
|
||||
import {
|
||||
@@ -41,15 +40,15 @@
|
||||
import tags, { TagElement, TagReference } from '@hcengineering/tags'
|
||||
import {
|
||||
calcRank,
|
||||
Component as ComponentType,
|
||||
DraftIssueChild,
|
||||
Issue,
|
||||
IssueDraft,
|
||||
IssuePriority,
|
||||
IssueStatus,
|
||||
IssueTemplate,
|
||||
Component as ComponentType,
|
||||
Sprint,
|
||||
Project
|
||||
Project,
|
||||
Sprint
|
||||
} from '@hcengineering/tracker'
|
||||
import {
|
||||
ActionIcon,
|
||||
@@ -62,8 +61,7 @@
|
||||
IconMoreH,
|
||||
Label,
|
||||
Menu,
|
||||
showPopup,
|
||||
Spinner
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
@@ -71,13 +69,13 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { activeComponent, activeSprint, generateIssueShortLink, getIssueId, updateIssueRelation } from '../issues'
|
||||
import tracker from '../plugin'
|
||||
import ComponentSelector from './ComponentSelector.svelte'
|
||||
import AssigneeEditor from './issues/AssigneeEditor.svelte'
|
||||
import IssueNotification from './issues/IssueNotification.svelte'
|
||||
import ParentIssue from './issues/ParentIssue.svelte'
|
||||
import PriorityEditor from './issues/PriorityEditor.svelte'
|
||||
import StatusEditor from './issues/StatusEditor.svelte'
|
||||
import EstimationEditor from './issues/timereport/EstimationEditor.svelte'
|
||||
import ComponentSelector from './ComponentSelector.svelte'
|
||||
import SetDueDateActionPopup from './SetDueDateActionPopup.svelte'
|
||||
import SetParentIssueActionPopup from './SetParentIssueActionPopup.svelte'
|
||||
import SprintSelector from './sprints/SprintSelector.svelte'
|
||||
@@ -101,7 +99,6 @@
|
||||
|
||||
let subIssuesComponent: SubIssues
|
||||
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
let labels: TagReference[] = draft?.labels || []
|
||||
let objectId: Ref<Issue> = draft?.issueId || generateId()
|
||||
let saveTimer: number | undefined
|
||||
@@ -137,6 +134,15 @@
|
||||
createOn: Date.now()
|
||||
}
|
||||
|
||||
$: _space = draft?.project || space
|
||||
$: !originalIssue && !draft && updateIssueStatusId(currentProject, status)
|
||||
$: !originalIssue && !draft && updateAssigneeId(currentProject)
|
||||
$: canSave = getTitle(object.title ?? '').length > 0
|
||||
|
||||
$: if (object.space !== _space) {
|
||||
object.space = _space
|
||||
}
|
||||
|
||||
let object = originalIssue
|
||||
? {
|
||||
...originalIssue,
|
||||
@@ -145,15 +151,16 @@
|
||||
attachments: 0,
|
||||
reportedTime: 0,
|
||||
reports: 0,
|
||||
childInfo: []
|
||||
childInfo: [],
|
||||
space: _space
|
||||
}
|
||||
: toIssue(defaultIssue, draft)
|
||||
: { ...toIssue(defaultIssue, draft), space: _space }
|
||||
fillDefaults(hierarchy, object, tracker.class.Issue)
|
||||
|
||||
function resetObject (): void {
|
||||
templateId = undefined
|
||||
template = undefined
|
||||
object = { ...defaultIssue }
|
||||
object = { ...defaultIssue, space: _space }
|
||||
subIssues = []
|
||||
labels = []
|
||||
if (!originalIssue && !draft) {
|
||||
@@ -228,7 +235,6 @@
|
||||
$: updateTemplate(template)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const statusesQuery = createQuery()
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
@@ -238,22 +244,6 @@
|
||||
attr: client.getHierarchy().getAttribute(tracker.class.Issue, 'labels')
|
||||
}
|
||||
|
||||
$: _space = draft?.project || space
|
||||
$: !originalIssue && !draft && updateIssueStatusId(currentProject, status)
|
||||
$: !originalIssue && !draft && updateAssigneeId(currentProject)
|
||||
$: canSave = getTitle(object.title ?? '').length > 0
|
||||
|
||||
$: statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ attachedTo: _space },
|
||||
(statuses) => {
|
||||
issueStatuses = statuses
|
||||
},
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
$: spaceQuery.query(tracker.class.Project, { _id: _space }, (res) => {
|
||||
currentProject = res.shift()
|
||||
})
|
||||
@@ -706,72 +696,64 @@
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{#if issueStatuses}
|
||||
<SubIssues
|
||||
bind:this={subIssuesComponent}
|
||||
projectId={_space}
|
||||
parent={objectId}
|
||||
statuses={issueStatuses ?? []}
|
||||
project={currentProject}
|
||||
sprint={object.sprint}
|
||||
component={object.component}
|
||||
/>
|
||||
{/if}
|
||||
<SubIssues
|
||||
bind:this={subIssuesComponent}
|
||||
projectId={_space}
|
||||
parent={objectId}
|
||||
project={currentProject}
|
||||
sprint={object.sprint}
|
||||
component={object.component}
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
{#if issueStatuses}
|
||||
<div id="status-editor">
|
||||
<StatusEditor
|
||||
value={object}
|
||||
statuses={issueStatuses}
|
||||
kind="no-border"
|
||||
size="small"
|
||||
shouldShowLabel={true}
|
||||
on:change={({ detail }) => (object.status = detail)}
|
||||
/>
|
||||
</div>
|
||||
<PriorityEditor
|
||||
<div id="status-editor">
|
||||
<StatusEditor
|
||||
value={object}
|
||||
shouldShowLabel
|
||||
isEditable
|
||||
kind="no-border"
|
||||
size="small"
|
||||
justify="center"
|
||||
on:change={({ detail }) => (object.priority = detail)}
|
||||
shouldShowLabel={true}
|
||||
on:change={({ detail }) => (object.status = detail)}
|
||||
/>
|
||||
<AssigneeEditor
|
||||
value={object}
|
||||
size="small"
|
||||
kind="no-border"
|
||||
width={'min-content'}
|
||||
on:change={({ detail }) => (object.assignee = detail)}
|
||||
/>
|
||||
<Component
|
||||
is={tags.component.TagsDropdownEditor}
|
||||
props={{
|
||||
items: labels,
|
||||
key,
|
||||
targetClass: tracker.class.Issue,
|
||||
countLabel: tracker.string.NumberLabels
|
||||
}}
|
||||
on:open={(evt) => {
|
||||
addTagRef(evt.detail)
|
||||
}}
|
||||
on:delete={(evt) => {
|
||||
labels = labels.filter((it) => it._id !== evt.detail)
|
||||
}}
|
||||
/>
|
||||
<EstimationEditor kind={'no-border'} size={'small'} value={object} {currentProject} />
|
||||
<ComponentSelector value={object.component} onChange={handleComponentIdChanged} isEditable={true} />
|
||||
<SprintSelector
|
||||
value={object.sprint}
|
||||
onChange={handleSprintIdChanged}
|
||||
useComponent={(!originalIssue && object.component) || undefined}
|
||||
/>
|
||||
{#if object.dueDate !== null}
|
||||
<DatePresenter bind:value={object.dueDate} editable />
|
||||
{/if}
|
||||
{:else}
|
||||
<Spinner size="small" />
|
||||
</div>
|
||||
<PriorityEditor
|
||||
value={object}
|
||||
shouldShowLabel
|
||||
isEditable
|
||||
kind="no-border"
|
||||
size="small"
|
||||
justify="center"
|
||||
on:change={({ detail }) => (object.priority = detail)}
|
||||
/>
|
||||
<AssigneeEditor
|
||||
value={object}
|
||||
size="small"
|
||||
kind="no-border"
|
||||
width={'min-content'}
|
||||
on:change={({ detail }) => (object.assignee = detail)}
|
||||
/>
|
||||
<Component
|
||||
is={tags.component.TagsDropdownEditor}
|
||||
props={{
|
||||
items: labels,
|
||||
key,
|
||||
targetClass: tracker.class.Issue,
|
||||
countLabel: tracker.string.NumberLabels
|
||||
}}
|
||||
on:open={(evt) => {
|
||||
addTagRef(evt.detail)
|
||||
}}
|
||||
on:delete={(evt) => {
|
||||
labels = labels.filter((it) => it._id !== evt.detail)
|
||||
}}
|
||||
/>
|
||||
<EstimationEditor kind={'no-border'} size={'small'} value={object} {currentProject} />
|
||||
<ComponentSelector value={object.component} onChange={handleComponentIdChanged} isEditable={true} />
|
||||
<SprintSelector
|
||||
value={object.sprint}
|
||||
onChange={handleSprintIdChanged}
|
||||
useComponent={(!originalIssue && object.component) || undefined}
|
||||
/>
|
||||
{#if object.dueDate !== null}
|
||||
<DatePresenter bind:value={object.dueDate} editable />
|
||||
{/if}
|
||||
<ActionIcon icon={IconMoreH} size={'medium'} action={showMoreActions} />
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -15,19 +15,10 @@
|
||||
<script lang="ts">
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import { deleteFile } from '@hcengineering/attachment-resources/src/utils'
|
||||
import core, { AttachedData, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import core, { AttachedData, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { draftStore, getClient, updateDraftStore } from '@hcengineering/presentation'
|
||||
import tags from '@hcengineering/tags'
|
||||
import {
|
||||
calcRank,
|
||||
DraftIssueChild,
|
||||
Issue,
|
||||
IssueParentInfo,
|
||||
IssueStatus,
|
||||
Component,
|
||||
Sprint,
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import { calcRank, Component, DraftIssueChild, Issue, IssueParentInfo, Project, Sprint } from '@hcengineering/tracker'
|
||||
import { Button, closeTooltip, ExpandCollapse, IconAdd, Scroller } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../plugin'
|
||||
@@ -42,7 +33,6 @@
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let component: Ref<Component> | null = null
|
||||
export let subIssues: DraftIssueChild[] = []
|
||||
export let statuses: WithLookup<IssueStatus>[]
|
||||
|
||||
let isCollapsed = false
|
||||
let isCreating = false
|
||||
@@ -208,7 +198,6 @@
|
||||
<div class="flex-col flex-no-shrink max-h-30 list clear-mins" class:collapsed={isCollapsed}>
|
||||
<Scroller>
|
||||
<DraftIssueChildList
|
||||
{statuses}
|
||||
{component}
|
||||
{sprint}
|
||||
bind:issues={subIssues}
|
||||
@@ -224,7 +213,6 @@
|
||||
<ExpandCollapse isExpanded={!isCollapsed} on:changeContent>
|
||||
<DraftIssueChildEditor
|
||||
{project}
|
||||
{statuses}
|
||||
{component}
|
||||
{sprint}
|
||||
on:close={() => {
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
import { Label, resizeObserver, Scroller } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import AssigneeEditor from './AssigneeEditor.svelte'
|
||||
import IssueStatusActivity from './IssueStatusActivity.svelte'
|
||||
import PriorityEditor from './PriorityEditor.svelte'
|
||||
import StatusEditor from './StatusEditor.svelte'
|
||||
import IssueStatusActivity from './IssueStatusActivity.svelte'
|
||||
|
||||
export let object: Issue
|
||||
let issue: Issue | undefined
|
||||
@@ -88,7 +88,7 @@
|
||||
{#if issue}
|
||||
<div class="fs-title text-xl ml-2">{issueName} {issue.title}</div>
|
||||
<div class="flex mt-2">
|
||||
<StatusEditor value={issue} {statuses} shouldShowLabel kind={'transparent'} />
|
||||
<StatusEditor value={issue} shouldShowLabel kind={'transparent'} />
|
||||
<PriorityEditor value={issue} shouldShowLabel />
|
||||
{#if issue.assignee}
|
||||
<AssigneeEditor value={issue} width={'min-content'} />
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
<script lang="ts">
|
||||
import core, {
|
||||
IdMap,
|
||||
Ref,
|
||||
Timestamp,
|
||||
toIdMap,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxUpdateDoc,
|
||||
WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import core, { Ref, Timestamp, Tx, TxCollectionCUD, TxCreateDoc, TxUpdateDoc, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus } from '@hcengineering/tracker'
|
||||
import { Label, ticker } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { statusByIdStore } from '../../utils'
|
||||
import Duration from './Duration.svelte'
|
||||
import StatusPresenter from './StatusPresenter.svelte'
|
||||
|
||||
@@ -28,23 +19,6 @@
|
||||
duration: number
|
||||
}
|
||||
|
||||
const stQuery = createQuery()
|
||||
|
||||
let statuses: IdMap<IssueStatus> = new Map()
|
||||
|
||||
stQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{},
|
||||
(res) => {
|
||||
statuses = toIdMap(res)
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
category: tracker.class.IssueStatusCategory
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
$: query.query(
|
||||
core.class.Tx,
|
||||
{ 'tx.objectId': issue._id },
|
||||
@@ -109,7 +83,7 @@
|
||||
displaySt = result
|
||||
}
|
||||
|
||||
$: updateStatus(txes, statuses, $ticker)
|
||||
$: updateStatus(txes, $statusByIdStore, $ticker)
|
||||
</script>
|
||||
|
||||
<div class="flex-row mt-4 mb-4">
|
||||
|
||||
@@ -13,19 +13,18 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup, SortingOrder } from '@hcengineering/core'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { IssueStatus, IssueStatusCategory } from '@hcengineering/tracker'
|
||||
import { getPlatformColor, IconSize } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { statusStore } from '../../utils'
|
||||
import StatusIcon from '../icons/StatusIcon.svelte'
|
||||
|
||||
export let value: WithLookup<IssueStatus>
|
||||
export let size: IconSize
|
||||
export let fill: string | undefined = undefined
|
||||
|
||||
export let issueStatuses: IssueStatus[] | undefined = undefined
|
||||
|
||||
const dynamicFillCategories = [tracker.issueStatusCategory.Started]
|
||||
|
||||
const client = getClient()
|
||||
@@ -40,19 +39,14 @@
|
||||
const categoriesQuery = createQuery()
|
||||
|
||||
$: if (value.category === tracker.issueStatusCategory.Started) {
|
||||
if (issueStatuses === undefined) {
|
||||
categoriesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ category: tracker.issueStatusCategory.Started },
|
||||
(res) => (statuses = res),
|
||||
{ sort: { rank: SortingOrder.Ascending } }
|
||||
const _s = [
|
||||
...$statusStore.filter(
|
||||
(it) => it.attachedTo === value.attachedTo && it.category === tracker.issueStatusCategory.Started
|
||||
)
|
||||
} else {
|
||||
const _s = [...issueStatuses.filter((it) => it.category === tracker.issueStatusCategory.Started)]
|
||||
_s.sort((a, b) => a.rank.localeCompare(b.rank))
|
||||
statuses = _s
|
||||
categoriesQuery.unsubscribe()
|
||||
}
|
||||
]
|
||||
_s.sort((a, b) => a.rank.localeCompare(b.rank))
|
||||
statuses = _s
|
||||
categoriesQuery.unsubscribe()
|
||||
}
|
||||
|
||||
async function updateCategory (status: WithLookup<IssueStatus>, statuses: IssueStatus[]) {
|
||||
|
||||
@@ -13,18 +13,24 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachedData, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { AttachedData, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { DraftIssueChild, Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { Button, eventToHTMLElement, SelectPopup, showPopup, TooltipAlignment } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { statusStore } from '../../utils'
|
||||
import IssueStatusIcon from './IssueStatusIcon.svelte'
|
||||
import StatusPresenter from './StatusPresenter.svelte'
|
||||
|
||||
export let value: Issue | AttachedData<Issue> | DraftIssueChild
|
||||
export let statuses: WithLookup<IssueStatus>[] | undefined = undefined
|
||||
export let value:
|
||||
| Issue
|
||||
| (AttachedData<Issue> & { space: Ref<Project> })
|
||||
| (DraftIssueChild & { space: Ref<Project> })
|
||||
|
||||
let statuses: WithLookup<IssueStatus>[] | undefined = undefined
|
||||
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = false
|
||||
export let tooltipAlignment: TooltipAlignment | undefined = undefined
|
||||
@@ -34,11 +40,7 @@
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = undefined
|
||||
|
||||
// Extra properties
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]> | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const statusesQuery = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const changeStatus = async (newStatus: Ref<IssueStatus> | undefined) => {
|
||||
@@ -66,6 +68,8 @@
|
||||
)
|
||||
}
|
||||
|
||||
$: statuses = $statusStore.filter((it) => it.attachedTo === value?.space)
|
||||
|
||||
$: selectedStatus = statuses?.find((status) => status._id === value.status) ?? statuses?.[0]
|
||||
$: selectedStatusLabel = shouldShowLabel ? selectedStatus?.name : undefined
|
||||
$: statusesInfo = statuses?.map((s) => {
|
||||
@@ -76,25 +80,6 @@
|
||||
isSelected: selectedStatus?._id === s._id ?? false
|
||||
}
|
||||
})
|
||||
$: if (!statuses) {
|
||||
statuses = '_id' in value ? issueStatuses?.get(value.space) : undefined
|
||||
if (statuses === undefined) {
|
||||
const query = '_id' in value ? { attachedTo: value.space } : {}
|
||||
statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
query,
|
||||
(result) => {
|
||||
statuses = result
|
||||
},
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
} else {
|
||||
statusesQuery.unsubscribe()
|
||||
}
|
||||
}
|
||||
$: smallgap = size === 'inline' || size === 'small'
|
||||
</script>
|
||||
|
||||
@@ -103,11 +88,7 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex-row-center flex-no-shrink" class:cursor-pointer={isEditable} on:click={handleStatusEditorOpened}>
|
||||
<div class="flex-center flex-no-shrink square-4">
|
||||
{#if selectedStatus}<IssueStatusIcon
|
||||
value={selectedStatus}
|
||||
issueStatuses={statuses}
|
||||
size={kind === 'list' ? 'inline' : 'medium'}
|
||||
/>{/if}
|
||||
{#if selectedStatus}<IssueStatusIcon value={selectedStatus} size={kind === 'list' ? 'inline' : 'medium'} />{/if}
|
||||
</div>
|
||||
{#if selectedStatusLabel}
|
||||
<span
|
||||
|
||||
@@ -13,20 +13,21 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { IssueStatus } from '@hcengineering/tracker'
|
||||
import { statusByIdStore } from '../../utils'
|
||||
import IssueStatusIcon from './IssueStatusIcon.svelte'
|
||||
|
||||
export let value: WithLookup<IssueStatus> | undefined
|
||||
export let size: 'small' | 'medium' = 'medium'
|
||||
export let value: IssueStatus | undefined
|
||||
export let size: 'small' | 'medium' = 'small'
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
{@const icon = $statusByIdStore.get(value._id)?.$lookup?.category?.icon}
|
||||
<div class="flex-presenter">
|
||||
{#if value.$lookup?.category?.icon}
|
||||
{#if icon}
|
||||
<IssueStatusIcon {value} {size} />
|
||||
{/if}
|
||||
<span class="overflow-label" class:ml-2={value.$lookup?.category?.icon !== undefined}>
|
||||
<span class="overflow-label" class:ml-2={icon !== undefined}>
|
||||
{value.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -13,20 +13,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import tracker, { IssueStatus } from '@hcengineering/tracker'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IssueStatus } from '@hcengineering/tracker'
|
||||
import { statusByIdStore } from '../../utils'
|
||||
import StatusPresenter from './StatusPresenter.svelte'
|
||||
|
||||
export let value: Ref<IssueStatus> | undefined
|
||||
export let size: 'small' | 'medium' = 'medium'
|
||||
|
||||
let status: WithLookup<IssueStatus> | undefined
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(tracker.class.IssueStatus, { _id: value }, (res) => ([status] = res), {
|
||||
lookup: { category: tracker.class.IssueStatusCategory }
|
||||
})
|
||||
</script>
|
||||
|
||||
<StatusPresenter value={status} {size} />
|
||||
{#if value}
|
||||
<StatusPresenter value={$statusByIdStore.get(value)} {size} />
|
||||
{/if}
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
<script lang="ts">
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { employeeByIdStore, EmployeePresenter } from '@hcengineering/contact-resources'
|
||||
import core, { ClassifierKind, Doc, Mixin, Ref, WithLookup } from '@hcengineering/core'
|
||||
import core, { ClassifierKind, Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import { AttributeBarEditor, createQuery, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
|
||||
import tags from '@hcengineering/tags'
|
||||
import type { Issue, IssueStatus } from '@hcengineering/tracker'
|
||||
import type { Issue } from '@hcengineering/tracker'
|
||||
import { Component, Label } from '@hcengineering/ui'
|
||||
import { getFiltredKeys, isCollectionAttr, ObjectBox } from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
@@ -32,7 +32,6 @@
|
||||
import StatusEditor from '../StatusEditor.svelte'
|
||||
|
||||
export let issue: Issue
|
||||
export let issueStatuses: WithLookup<IssueStatus>[]
|
||||
export let showAllMixins: boolean = false
|
||||
|
||||
const query = createQuery()
|
||||
@@ -116,7 +115,7 @@
|
||||
<Label label={tracker.string.Status} />
|
||||
</span>
|
||||
|
||||
<StatusEditor value={issue} statuses={issueStatuses} shouldShowLabel />
|
||||
<StatusEditor value={issue} shouldShowLabel />
|
||||
|
||||
{#if issue.blockedBy?.length}
|
||||
<span class="labelTop">
|
||||
|
||||
@@ -13,29 +13,28 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { Account, AttachedData, Doc, generateId, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import core, { Account, AttachedData, Doc, generateId, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import presentation, { getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
import { IssueStatus, IssuePriority, Issue, Project, calcRank } from '@hcengineering/tracker'
|
||||
import { addNotification, Button, Component, EditBox } from '@hcengineering/ui'
|
||||
import tags, { TagElement, TagReference } from '@hcengineering/tags'
|
||||
import { calcRank, Issue, IssuePriority, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { addNotification, Button, Component, EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../../plugin'
|
||||
import AssigneeEditor from '../AssigneeEditor.svelte'
|
||||
import StatusEditor from '../StatusEditor.svelte'
|
||||
import PriorityEditor from '../PriorityEditor.svelte'
|
||||
import EstimationEditor from '../timereport/EstimationEditor.svelte'
|
||||
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import IssueNotification from '../IssueNotification.svelte'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import PriorityEditor from '../PriorityEditor.svelte'
|
||||
import StatusEditor from '../StatusEditor.svelte'
|
||||
import EstimationEditor from '../timereport/EstimationEditor.svelte'
|
||||
|
||||
export let parentIssue: Issue
|
||||
export let issueStatuses: WithLookup<IssueStatus>[]
|
||||
export let currentProject: Project
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
let newIssue: AttachedData<Issue> = getIssueDefaults()
|
||||
let newIssue = { ...getIssueDefaults(), space: currentProject._id }
|
||||
let thisRef: HTMLDivElement
|
||||
let focusIssueTitle: () => void
|
||||
let labels: TagReference[] = []
|
||||
@@ -71,7 +70,7 @@
|
||||
}
|
||||
|
||||
function resetToDefaults () {
|
||||
newIssue = getIssueDefaults()
|
||||
newIssue = { ...getIssueDefaults(), space: currentProject._id }
|
||||
labels = []
|
||||
focusIssueTitle?.()
|
||||
objectId = generateId()
|
||||
@@ -173,7 +172,6 @@
|
||||
<div id="status-editor" class="mr-1">
|
||||
<StatusEditor
|
||||
value={newIssue}
|
||||
statuses={issueStatuses}
|
||||
kind="transparent"
|
||||
size="medium"
|
||||
justify="center"
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import { Class, Data, Doc, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { Class, Data, Doc, Ref, WithLookup } from '@hcengineering/core'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import setting, { settingId } from '@hcengineering/setting'
|
||||
import type { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import type { Issue, Project } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
EditBox,
|
||||
@@ -48,13 +48,11 @@
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
const queryClient = createQuery()
|
||||
const statusesQuery = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
let issue: WithLookup<Issue> | undefined
|
||||
let currentProject: Project | undefined
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
let title = ''
|
||||
let description = ''
|
||||
let innerWidth: number
|
||||
@@ -93,20 +91,8 @@
|
||||
{ lookup: { attachedTo: tracker.class.Issue, space: tracker.class.Project } }
|
||||
)
|
||||
|
||||
$: currentProject &&
|
||||
statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ attachedTo: currentProject._id },
|
||||
(statuses) => (issueStatuses = statuses),
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
|
||||
$: issueId = currentProject && issue && getIssueId(currentProject, issue)
|
||||
$: canSave = title.trim().length > 0
|
||||
$: isDescriptionEmpty = !new DOMParser().parseFromString(description, 'text/html').documentElement.innerText?.trim()
|
||||
$: parentIssue = issue?.$lookup?.attachedTo
|
||||
|
||||
let saved = false
|
||||
@@ -190,7 +176,7 @@
|
||||
|
||||
{#if parentIssue}
|
||||
<div class="mb-6">
|
||||
{#if currentProject && issueStatuses}
|
||||
{#if currentProject}
|
||||
<SubIssueSelector {issue} />
|
||||
{:else}
|
||||
<Spinner />
|
||||
@@ -221,12 +207,8 @@
|
||||
|
||||
<div class="mt-6">
|
||||
{#key issue._id && currentProject !== undefined}
|
||||
{#if currentProject !== undefined && issueStatuses !== undefined}
|
||||
<SubIssues
|
||||
{issue}
|
||||
issueStatuses={new Map([[currentProject._id, issueStatuses]])}
|
||||
projects={new Map([[currentProject?._id, currentProject]])}
|
||||
/>
|
||||
{#if currentProject !== undefined}
|
||||
<SubIssues {issue} projects={new Map([[currentProject?._id, currentProject]])} />
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
@@ -270,8 +252,8 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="custom-attributes">
|
||||
{#if issue && currentProject && issueStatuses}
|
||||
<ControlPanel {issue} {issueStatuses} {showAllMixins} />
|
||||
{#if issue && currentProject}
|
||||
<ControlPanel {issue} {showAllMixins} />
|
||||
{/if}
|
||||
|
||||
<div class="divider" />
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Viewlet, ViewOptions } from '@hcengineering/view'
|
||||
import {
|
||||
ActionContext,
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
// Extra properties
|
||||
export let projects: Map<Ref<Project>, Project> | undefined
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]>
|
||||
|
||||
let list: List
|
||||
|
||||
@@ -66,7 +65,7 @@
|
||||
documents={issues}
|
||||
{query}
|
||||
flatHeaders={true}
|
||||
props={{ projects, issueStatuses }}
|
||||
props={{ projects }}
|
||||
{disableHeader}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
on:row-focus={(event) => {
|
||||
|
||||
@@ -13,18 +13,18 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder, toIdMap, WithLookup } from '@hcengineering/core'
|
||||
import { Ref, toIdMap } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Project, trackerId } from '@hcengineering/tracker'
|
||||
import { Issue, Project, trackerId } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
Chevron,
|
||||
closeTooltip,
|
||||
ExpandCollapse,
|
||||
getCurrentLocation,
|
||||
IconScaleFull,
|
||||
IconAdd,
|
||||
IconArrowRight,
|
||||
IconScaleFull,
|
||||
Label,
|
||||
navigate
|
||||
} from '@hcengineering/ui'
|
||||
@@ -33,8 +33,8 @@
|
||||
createFilter,
|
||||
filterStore,
|
||||
getViewOptions,
|
||||
viewOptionStore,
|
||||
ViewletSettingButton
|
||||
ViewletSettingButton,
|
||||
viewOptionStore
|
||||
} from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
import CreateSubIssue from './CreateSubIssue.svelte'
|
||||
@@ -42,7 +42,6 @@
|
||||
|
||||
export let issue: Issue
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]>
|
||||
|
||||
let subIssueEditorRef: HTMLDivElement
|
||||
let isCollapsed = false
|
||||
@@ -58,7 +57,6 @@
|
||||
})
|
||||
|
||||
let _projects = projects
|
||||
let _issueStatuses = issueStatuses
|
||||
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
@@ -70,28 +68,6 @@
|
||||
projectsQuery.unsubscribe()
|
||||
}
|
||||
|
||||
const statusesQuery = createQuery()
|
||||
$: if (issueStatuses === undefined) {
|
||||
statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{},
|
||||
(statuses) => {
|
||||
const st = new Map<Ref<Project>, WithLookup<IssueStatus>[]>()
|
||||
for (const s of statuses) {
|
||||
const id = s.attachedTo as Ref<Project>
|
||||
st.set(id, [...(st.get(id) ?? []), s])
|
||||
}
|
||||
_issueStatuses = st
|
||||
},
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
} else {
|
||||
statusesQuery.unsubscribe()
|
||||
}
|
||||
|
||||
$: viewOptions = viewlet !== undefined ? getViewOptions(viewlet, $viewOptionStore) : undefined
|
||||
</script>
|
||||
|
||||
@@ -158,39 +134,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
{#if issueStatuses}
|
||||
{#if hasSubIssues && viewOptions && viewlet}
|
||||
{#if !isCollapsed}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<div class="list" class:collapsed={isCollapsed}>
|
||||
<SubIssueList
|
||||
projects={_projects}
|
||||
{viewlet}
|
||||
{viewOptions}
|
||||
issueStatuses={_issueStatuses}
|
||||
query={{ attachedTo: issue._id }}
|
||||
/>
|
||||
</div>
|
||||
</ExpandCollapse>
|
||||
{#if hasSubIssues && viewOptions && viewlet}
|
||||
{#if !isCollapsed}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<div class="list" class:collapsed={isCollapsed}>
|
||||
<SubIssueList projects={_projects} {viewlet} {viewOptions} query={{ attachedTo: issue._id }} />
|
||||
</div>
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
{/if}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
{#if isCreating}
|
||||
{@const project = projects.get(issue.space)}
|
||||
{#if project !== undefined}
|
||||
<div class="pt-4" bind:this={subIssueEditorRef}>
|
||||
<CreateSubIssue parentIssue={issue} currentProject={project} on:close={() => (isCreating = false)} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
{#if isCreating}
|
||||
{@const project = projects.get(issue.space)}
|
||||
{@const statuses = issueStatuses.get(issue.space)}
|
||||
{#if project !== undefined && statuses !== undefined}
|
||||
<div class="pt-4" bind:this={subIssueEditorRef}>
|
||||
<CreateSubIssue
|
||||
parentIssue={issue}
|
||||
issueStatuses={statuses}
|
||||
currentProject={project}
|
||||
on:close={() => (isCreating = false)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
</ExpandCollapse>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { getIssueId } from '../../../issues'
|
||||
import tracker from '../../../plugin'
|
||||
import { subIssueListProvider } from '../../../utils'
|
||||
import { statusByIdStore, statusStore, subIssueListProvider } from '../../../utils'
|
||||
|
||||
export let value: WithLookup<Issue>
|
||||
export let currentProject: Project | undefined = undefined
|
||||
@@ -44,7 +44,7 @@
|
||||
$: project = currentProject
|
||||
|
||||
let subIssues: Issue[] = []
|
||||
let countComplate: number = 0
|
||||
let countComplete: number = 0
|
||||
|
||||
const projectQuery = createQuery()
|
||||
$: if (currentProject === undefined) {
|
||||
@@ -59,7 +59,6 @@
|
||||
projectQuery.unsubscribe()
|
||||
}
|
||||
const query = createQuery()
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
$: update(value)
|
||||
|
||||
@@ -75,25 +74,18 @@
|
||||
} else {
|
||||
query.unsubscribe()
|
||||
}
|
||||
if (value.subIssues > 0 || value.$lookup?.subIssues !== undefined) {
|
||||
statusesQuery.query(tracker.class.IssueStatus, {}, (res) => (statuses = res), {
|
||||
lookup: { category: tracker.class.IssueStatusCategory }
|
||||
})
|
||||
} else {
|
||||
statusesQuery.unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
let statuses: WithLookup<IssueStatus>[] = []
|
||||
|
||||
$: if (statuses && subIssues) {
|
||||
const doneStatuses = statuses.filter((s) => s.category === tracker.issueStatusCategory.Completed).map((p) => p._id)
|
||||
countComplate = subIssues.filter((si) => doneStatuses.includes(si.status)).length
|
||||
$: if (subIssues) {
|
||||
const doneStatuses = $statusStore
|
||||
.filter((s) => s.category === tracker.issueStatusCategory.Completed)
|
||||
.map((p) => p._id)
|
||||
countComplete = subIssues.filter((si) => doneStatuses.includes(si.status)).length
|
||||
}
|
||||
$: hasSubIssues = (subIssues?.length ?? 0) > 0
|
||||
|
||||
function getIssueStatusIcon (issue: Issue, statuses: WithLookup<IssueStatus>[] | undefined) {
|
||||
const status = statuses?.find((s) => issue.status === s._id)
|
||||
function getIssueStatusIcon (issue: Issue, statuses: Map<Ref<WithLookup<IssueStatus>>, WithLookup<IssueStatus>>) {
|
||||
const status = statuses.get(issue.status)
|
||||
const category = status?.$lookup?.category
|
||||
const color = status?.color ?? category?.color
|
||||
|
||||
@@ -119,7 +111,12 @@
|
||||
value: subIssues.map((iss) => {
|
||||
const text = project ? `${getIssueId(project, iss)} ${iss.title}` : iss.title
|
||||
|
||||
return { id: iss._id, text, isSelected: iss._id === value._id, ...getIssueStatusIcon(iss, statuses) }
|
||||
return {
|
||||
id: iss._id,
|
||||
text,
|
||||
isSelected: iss._id === value._id,
|
||||
...getIssueStatusIcon(iss, $statusByIdStore)
|
||||
}
|
||||
}),
|
||||
width: 'large'
|
||||
},
|
||||
@@ -156,9 +153,9 @@
|
||||
{#if subIssues}
|
||||
<div class="flex-row-center content-color text-sm pointer-events-none">
|
||||
<div class="mr-1">
|
||||
<ProgressCircle bind:value={countComplate} bind:max={subIssues.length} size={'inline'} primary />
|
||||
<ProgressCircle bind:value={countComplete} bind:max={subIssues.length} size={'inline'} primary />
|
||||
</div>
|
||||
{countComplate}/{subIssues.length}
|
||||
{countComplete}/{subIssues.length}
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
+12
-16
@@ -29,7 +29,7 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { getIssueId } from '../../../issues'
|
||||
import tracker from '../../../plugin'
|
||||
import { subIssueListProvider } from '../../../utils'
|
||||
import { statusByIdStore, statusStore, subIssueListProvider } from '../../../utils'
|
||||
|
||||
export let object: WithLookup<Doc & { related: number }> | undefined
|
||||
export let value: WithLookup<Doc & { related: number }> | undefined
|
||||
@@ -43,10 +43,9 @@
|
||||
let btn: HTMLElement
|
||||
|
||||
let subIssues: Issue[] = []
|
||||
let countComplate: number = 0
|
||||
let countComplete: number = 0
|
||||
|
||||
const query = createQuery()
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
$: _object = object ?? value
|
||||
|
||||
@@ -67,21 +66,18 @@
|
||||
}
|
||||
)
|
||||
}
|
||||
statusesQuery.query(tracker.class.IssueStatus, {}, (res) => (statuses = res), {
|
||||
lookup: { category: tracker.class.IssueStatusCategory }
|
||||
})
|
||||
}
|
||||
|
||||
let statuses: WithLookup<IssueStatus>[] = []
|
||||
|
||||
$: if (statuses && subIssues) {
|
||||
const doneStatuses = statuses.filter((s) => s.category === tracker.issueStatusCategory.Completed).map((p) => p._id)
|
||||
countComplate = subIssues.filter((si) => doneStatuses.includes(si.status)).length
|
||||
$: if (subIssues) {
|
||||
const doneStatuses = $statusStore
|
||||
.filter((s) => s.category === tracker.issueStatusCategory.Completed)
|
||||
.map((p) => p._id)
|
||||
countComplete = subIssues.filter((si) => doneStatuses.includes(si.status)).length
|
||||
}
|
||||
$: hasSubIssues = (subIssues?.length ?? 0) > 0
|
||||
|
||||
function getIssueStatusIcon (issue: Issue, statuses: WithLookup<IssueStatus>[] | undefined) {
|
||||
const status = statuses?.find((s) => issue.status === s._id)
|
||||
function getIssueStatusIcon (issue: Issue, statuses: Map<Ref<WithLookup<IssueStatus>>, WithLookup<IssueStatus>>) {
|
||||
const status = statuses?.get(issue.status)
|
||||
const category = status?.$lookup?.category
|
||||
const color = status?.color ?? category?.color
|
||||
|
||||
@@ -105,7 +101,7 @@
|
||||
value: subIssues.map((iss) => {
|
||||
const text = currentProject ? `${getIssueId(currentProject, iss)} ${iss.title}` : iss.title
|
||||
|
||||
return { id: iss._id, text, isSelected: false, ...getIssueStatusIcon(iss, statuses) }
|
||||
return { id: iss._id, text, isSelected: false, ...getIssueStatusIcon(iss, $statusByIdStore) }
|
||||
}),
|
||||
width: 'large'
|
||||
},
|
||||
@@ -142,9 +138,9 @@
|
||||
{#if subIssues}
|
||||
<div class="flex-row-center content-color text-sm pointer-events-none">
|
||||
<div class="mr-1">
|
||||
<ProgressCircle bind:value={countComplate} bind:max={subIssues.length} size={'inline'} primary />
|
||||
<ProgressCircle bind:value={countComplete} bind:max={subIssues.length} size={'inline'} primary />
|
||||
</div>
|
||||
{countComplate}/{subIssues.length}
|
||||
{countComplete}/{subIssues.length}
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Doc, DocumentQuery, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Doc, DocumentQuery, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Label, Spinner } from '@hcengineering/ui'
|
||||
import { Viewlet, ViewOptions } from '@hcengineering/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../../plugin'
|
||||
import SubIssueList from '../edit/SubIssueList.svelte'
|
||||
import AddIssueDuo from '../../icons/AddIssueDuo.svelte'
|
||||
import contact from '@hcengineering/contact'
|
||||
import SubIssueList from '../edit/SubIssueList.svelte'
|
||||
|
||||
export let object: Doc
|
||||
export let viewlet: Viewlet
|
||||
@@ -52,31 +52,11 @@
|
||||
$: projectsQuery.query(tracker.class.Project, {}, async (result) => {
|
||||
projects = new Map(result.map((it) => [it._id, it]))
|
||||
})
|
||||
|
||||
let issueStatuses = new Map<Ref<Project>, WithLookup<IssueStatus>[]>()
|
||||
|
||||
const statusesQuery = createQuery()
|
||||
$: statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{},
|
||||
(statuses) => {
|
||||
const st = new Map<Ref<Project>, WithLookup<IssueStatus>[]>()
|
||||
for (const s of statuses) {
|
||||
const id = s.attachedTo as Ref<Project>
|
||||
st.set(id, [...(st.get(id) ?? []), s])
|
||||
}
|
||||
issueStatuses = st
|
||||
},
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if subIssues !== undefined && viewlet !== undefined}
|
||||
{#if issueStatuses.size > 0 && projects && subIssues.length > 0}
|
||||
<SubIssueList bind:viewOptions {viewlet} issues={subIssues} {projects} {issueStatuses} {disableHeader} />
|
||||
{#if projects && subIssues.length > 0}
|
||||
<SubIssueList bind:viewOptions {viewlet} issues={subIssues} {projects} {disableHeader} />
|
||||
{:else}
|
||||
<div class="antiSection-empty solid flex-col mt-3">
|
||||
<div class="flex-center content-accent-color">
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import presentation, { Card, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Button, EditStyle, eventToHTMLElement, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||
import { EditBoxPopup } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -41,7 +40,6 @@
|
||||
const query = createQuery()
|
||||
|
||||
let currentProject: Project | undefined
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
|
||||
$: query.query(
|
||||
object._class,
|
||||
@@ -60,19 +58,6 @@
|
||||
}
|
||||
)
|
||||
$: defaultTimeReportDay = currentProject?.defaultTimeReportDay
|
||||
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
$: currentProject &&
|
||||
statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ attachedTo: currentProject._id },
|
||||
(statuses) => (issueStatuses = statuses),
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
@@ -124,12 +109,8 @@
|
||||
<IssuePresenter value={object} disableClick />
|
||||
</svelte:fragment>
|
||||
|
||||
{#if currentProject && issueStatuses}
|
||||
<SubIssuesEstimations
|
||||
issue={object}
|
||||
issueStatuses={new Map([[currentProject._id, issueStatuses]])}
|
||||
projects={new Map([[currentProject?._id, currentProject]])}
|
||||
/>
|
||||
{#if currentProject}
|
||||
<SubIssuesEstimations issue={object} projects={new Map([[currentProject?._id, currentProject]])} />
|
||||
{/if}
|
||||
|
||||
{#if currentProject}
|
||||
|
||||
+3
-4
@@ -13,16 +13,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Expandable, Spinner } from '@hcengineering/ui'
|
||||
import tracker from '../../../plugin'
|
||||
import EstimationSubIssueList from './EstimationSubIssueList.svelte'
|
||||
|
||||
export let issue: Issue
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]>
|
||||
|
||||
const subIssuesQuery = createQuery()
|
||||
|
||||
@@ -35,7 +34,7 @@
|
||||
$: total = (subIssues ?? []).reduce((a, b) => a + b.estimation, 0)
|
||||
</script>
|
||||
|
||||
{#if subIssues && issueStatuses}
|
||||
{#if subIssues}
|
||||
{#if hasSubIssues}
|
||||
<Expandable label={tracker.string.ChildEstimation} contentColor bordered>
|
||||
<svelte:fragment slot="title">: <span class="caption-color">{total}</span></svelte:fragment>
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { IdMap, Ref, toIdMap } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus } from '@hcengineering/tracker'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Issue } from '@hcengineering/tracker'
|
||||
import { floorFractionDigits, Label } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { statusByIdStore } from '../../utils'
|
||||
import EstimationProgressCircle from '../issues/timereport/EstimationProgressCircle.svelte'
|
||||
import TimePresenter from '../issues/timereport/TimePresenter.svelte'
|
||||
export let docs: Issue[] | undefined = undefined
|
||||
@@ -28,23 +28,13 @@
|
||||
$: noParents = docs?.filter((it) => !ids.has(it.attachedTo as Ref<Issue>))
|
||||
|
||||
$: rootNoBacklogIssues = noParents?.filter(
|
||||
(it) => issueStatuses.get(it.status)?.category !== tracker.issueStatusCategory.Backlog
|
||||
(it) => $statusByIdStore.get(it.status)?.category !== tracker.issueStatusCategory.Backlog
|
||||
)
|
||||
|
||||
const statuses = createQuery()
|
||||
let issueStatuses: IdMap<IssueStatus> = new Map()
|
||||
$: if (noParents !== undefined) {
|
||||
statuses.query(tracker.class.IssueStatus, {}, (res) => {
|
||||
issueStatuses = toIdMap(res)
|
||||
})
|
||||
} else {
|
||||
statuses.unsubscribe()
|
||||
}
|
||||
|
||||
$: totalEstimation = floorFractionDigits(
|
||||
(rootNoBacklogIssues ?? [{ estimation: 0, childInfo: [] } as unknown as Issue])
|
||||
.map((it) => {
|
||||
const cat = issueStatuses.get(it.status)?.category
|
||||
const cat = $statusByIdStore.get(it.status)?.category
|
||||
|
||||
let retEst = it.estimation
|
||||
if (it.childInfo?.length > 0) {
|
||||
|
||||
@@ -14,17 +14,16 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import { generateId, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { generateId, Ref } from '@hcengineering/core'
|
||||
import presentation, { createQuery, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
import tags, { TagElement, TagReference } from '@hcengineering/tags'
|
||||
import {
|
||||
Component as ComponentType,
|
||||
DraftIssueChild,
|
||||
IssuePriority,
|
||||
IssueStatus,
|
||||
IssueTemplateChild,
|
||||
Component as ComponentType,
|
||||
Sprint,
|
||||
Project
|
||||
Project,
|
||||
Sprint
|
||||
} from '@hcengineering/tracker'
|
||||
import { Button, Component, EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -39,12 +38,12 @@
|
||||
export let component: Ref<ComponentType> | null = null
|
||||
export let childIssue: DraftIssueChild | undefined = undefined
|
||||
export let showBorder = false
|
||||
export let statuses: WithLookup<IssueStatus>[]
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
let newIssue: DraftIssueChild = childIssue !== undefined ? { ...childIssue } : getIssueDefaults()
|
||||
let newIssue =
|
||||
childIssue !== undefined ? { ...childIssue, space: project._id } : { ...getIssueDefaults(), space: project._id }
|
||||
let thisRef: HTMLDivElement
|
||||
let focusIssueTitle: () => void
|
||||
let labels: TagElement[] = []
|
||||
@@ -75,7 +74,7 @@
|
||||
}
|
||||
|
||||
function resetToDefaults () {
|
||||
newIssue = getIssueDefaults()
|
||||
newIssue = { ...getIssueDefaults(), space: project._id }
|
||||
focusIssueTitle?.()
|
||||
}
|
||||
|
||||
@@ -152,7 +151,6 @@
|
||||
<div class="buttons-group xsmall-gap">
|
||||
<StatusEditor
|
||||
value={newIssue}
|
||||
{statuses}
|
||||
kind="no-border"
|
||||
size="small"
|
||||
shouldShowLabel={true}
|
||||
|
||||
@@ -13,16 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import tracker, {
|
||||
DraftIssueChild,
|
||||
IssueStatus,
|
||||
IssueTemplateChild,
|
||||
Component,
|
||||
Sprint,
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import tracker, { Component, DraftIssueChild, IssueTemplateChild, Project, Sprint } from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { ActionContext, FixedColumn } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -38,7 +31,6 @@
|
||||
export let project: Ref<Project>
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let component: Ref<Component> | null = null
|
||||
export let statuses: WithLookup<IssueStatus>[]
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let draggingIndex: number | null = null
|
||||
@@ -52,7 +44,6 @@
|
||||
project: currentProject,
|
||||
sprint,
|
||||
component,
|
||||
statuses,
|
||||
childIssue: target
|
||||
},
|
||||
eventToHTMLElement(evt),
|
||||
@@ -135,8 +126,7 @@
|
||||
</div>
|
||||
<div class="flex-row-center ml-6 clear-mins gap-2">
|
||||
<StatusEditor
|
||||
value={issue}
|
||||
{statuses}
|
||||
value={{ ...issue, space: project }}
|
||||
kind="list"
|
||||
size="small"
|
||||
shouldShowLabel={true}
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { IssueStatus } from '@hcengineering/tracker'
|
||||
import { Icon, Label, IconEdit, IconClose, tooltip } from '@hcengineering/ui'
|
||||
import { Icon, IconClose, IconEdit, Label, tooltip } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import Circles from '../icons/Circles.svelte'
|
||||
import IssueStatusIcon from '../issues/IssueStatusIcon.svelte'
|
||||
@@ -23,7 +23,6 @@
|
||||
export let value: IssueStatus
|
||||
export let isDefault = false
|
||||
export let isSingle = true
|
||||
export let issueStatuses: IssueStatus[] | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -38,7 +37,7 @@
|
||||
<Circles />
|
||||
</div>
|
||||
<div class="flex-no-shrink ml-2">
|
||||
<IssueStatusIcon {value} size="small" {issueStatuses} />
|
||||
<IssueStatusIcon {value} size="small" />
|
||||
</div>
|
||||
<span class="content-accent-color ml-2">{value.name}</span>
|
||||
{#if value.description}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { flip } from 'svelte/animate'
|
||||
import tracker from '../../plugin'
|
||||
import { statusStore } from '../../utils'
|
||||
import StatusEditor from './StatusEditor.svelte'
|
||||
import StatusPresenter from './StatusPresenter.svelte'
|
||||
|
||||
@@ -40,11 +41,9 @@
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
const projectQuery = createQuery()
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
let project: Project | undefined
|
||||
let statusCategories: IssueStatusCategory[] | undefined
|
||||
let workflowStatuses: IssueStatus[] | undefined
|
||||
|
||||
let editingStatus: IssueStatus | Partial<AttachedData<IssueStatus>> | null = null
|
||||
let draggingStatus: IssueStatus | null = null
|
||||
@@ -67,10 +66,10 @@
|
||||
}
|
||||
|
||||
async function addStatus () {
|
||||
if (editingStatus?.name && editingStatus?.category && workflowStatuses) {
|
||||
const categoryStatuses = workflowStatuses.filter((s) => s.category === editingStatus!.category)
|
||||
if (editingStatus?.name && editingStatus?.category && $statusStore) {
|
||||
const categoryStatuses = $statusStore.filter((s) => s.category === editingStatus!.category)
|
||||
const prevStatus = categoryStatuses[categoryStatuses.length - 1]
|
||||
const nextStatus = workflowStatuses[workflowStatuses.findIndex(({ _id }) => _id === prevStatus._id) + 1]
|
||||
const nextStatus = $statusStore[$statusStore.findIndex(({ _id }) => _id === prevStatus._id) + 1]
|
||||
|
||||
isSaving = true
|
||||
await client.addCollection(
|
||||
@@ -94,15 +93,9 @@
|
||||
}
|
||||
|
||||
async function editStatus () {
|
||||
if (
|
||||
workflowStatuses &&
|
||||
statusCategories &&
|
||||
editingStatus?.name &&
|
||||
editingStatus?.category &&
|
||||
'_id' in editingStatus
|
||||
) {
|
||||
if ($statusStore && statusCategories && editingStatus?.name && editingStatus?.category && '_id' in editingStatus) {
|
||||
const statusId = '_id' in editingStatus ? editingStatus._id : undefined
|
||||
const status = statusId && workflowStatuses.find(({ _id }) => _id === statusId)
|
||||
const status = statusId && $statusStore.find(({ _id }) => _id === statusId)
|
||||
|
||||
if (!status) {
|
||||
return
|
||||
@@ -164,14 +157,12 @@
|
||||
},
|
||||
undefined,
|
||||
async (result) => {
|
||||
if (result && project && workflowStatuses) {
|
||||
if (result && project && $statusStore) {
|
||||
isSaving = true
|
||||
await client.removeDoc(status._class, status.space, status._id)
|
||||
|
||||
if (project.defaultIssueStatus === status._id) {
|
||||
const newDefaultStatus = workflowStatuses.find(
|
||||
(s) => s._id !== status._id && s.category === status.category
|
||||
)
|
||||
const newDefaultStatus = $statusStore.find((s) => s._id !== status._id && s.category === status.category)
|
||||
if (newDefaultStatus?._id) {
|
||||
await updateProjectDefaultStatus(newDefaultStatus._id)
|
||||
}
|
||||
@@ -205,12 +196,12 @@
|
||||
}
|
||||
|
||||
async function handleDrop (toItem: IssueStatus) {
|
||||
if (workflowStatuses && draggingStatus?._id !== toItem._id && draggingStatus?.category === toItem.category) {
|
||||
if ($statusStore && draggingStatus?._id !== toItem._id && draggingStatus?.category === toItem.category) {
|
||||
const fromIndex = getStatusIndex(draggingStatus)
|
||||
const toIndex = getStatusIndex(toItem)
|
||||
const [prev, next] = [
|
||||
workflowStatuses[fromIndex < toIndex ? toIndex : toIndex - 1],
|
||||
workflowStatuses[fromIndex < toIndex ? toIndex + 1 : toIndex]
|
||||
$statusStore[fromIndex < toIndex ? toIndex : toIndex - 1],
|
||||
$statusStore[fromIndex < toIndex ? toIndex + 1 : toIndex]
|
||||
]
|
||||
|
||||
isSaving = true
|
||||
@@ -222,7 +213,7 @@
|
||||
}
|
||||
|
||||
function getStatusIndex (status: IssueStatus) {
|
||||
return workflowStatuses?.findIndex(({ _id }) => _id === status._id) ?? -1
|
||||
return $statusStore?.findIndex(({ _id }) => _id === status._id) ?? -1
|
||||
}
|
||||
|
||||
function resetDrag () {
|
||||
@@ -231,9 +222,6 @@
|
||||
}
|
||||
|
||||
$: projectQuery.query(projectClass, { _id: projectId }, (result) => ([project] = result), { limit: 1 })
|
||||
$: statusesQuery.query(tracker.class.IssueStatus, { attachedTo: projectId }, (res) => (workflowStatuses = res), {
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
})
|
||||
$: updateStatusCategories()
|
||||
</script>
|
||||
|
||||
@@ -254,13 +242,14 @@
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
{#if project === undefined || statusCategories === undefined || workflowStatuses === undefined}
|
||||
{#if project === undefined || statusCategories === undefined || $statusStore === undefined}
|
||||
<Loading />
|
||||
{:else}
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content py-10 clear-mins">
|
||||
{#each statusCategories as category}
|
||||
{@const statuses = workflowStatuses?.filter((s) => s.category === category._id) ?? []}
|
||||
{@const statuses =
|
||||
$statusStore?.filter((s) => s.attachedTo === projectId && s.category === category._id) ?? []}
|
||||
{@const isSingle = statuses.length === 1}
|
||||
<div class="flex-between category-name">
|
||||
<Label label={category.label} />
|
||||
@@ -312,7 +301,6 @@
|
||||
editingStatus = { ...detail, color: detail.color ?? category.color }
|
||||
}}
|
||||
on:delete={deleteStatus}
|
||||
issueStatuses={workflowStatuses}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -301,7 +301,6 @@ export default mergeIds(trackerId, tracker, {
|
||||
DefaultTimeReportDay: '' as IntlString,
|
||||
DefaultAssignee: '' as IntlString,
|
||||
|
||||
WorkDayLength: '' as IntlString,
|
||||
SevenHoursLength: '' as IntlString,
|
||||
EightHoursLength: '' as IntlString,
|
||||
HourLabel: '' as IntlString,
|
||||
|
||||
@@ -19,6 +19,7 @@ import core, {
|
||||
Class,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
IdMap,
|
||||
Ref,
|
||||
SortingOrder,
|
||||
Space,
|
||||
@@ -55,6 +56,7 @@ import {
|
||||
MILLISECONDS_IN_WEEK
|
||||
} from '@hcengineering/ui'
|
||||
import { CategoryQuery, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
|
||||
import { writable } from 'svelte/store'
|
||||
import tracker from './plugin'
|
||||
import { defaultPriorities, defaultComponentStatuses, defaultSprintStatuses, issuePriorities } from './types'
|
||||
|
||||
@@ -655,3 +657,24 @@ export async function removeProject (project: Project): Promise<void> {
|
||||
const client = getClient()
|
||||
await client.removeDoc(tracker.class.Project, core.space.Space, project._id)
|
||||
}
|
||||
|
||||
// Issue status live query
|
||||
export const statusByIdStore = writable<IdMap<WithLookup<IssueStatus>>>(new Map())
|
||||
export const statusStore = writable<Array<WithLookup<IssueStatus>>>([])
|
||||
const query = createQuery(true)
|
||||
query.query(
|
||||
tracker.class.IssueStatus,
|
||||
{},
|
||||
(res) => {
|
||||
statusStore.set(res)
|
||||
statusByIdStore.set(toIdMap(res))
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
category: tracker.class.IssueStatusCategory
|
||||
},
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -54,7 +54,6 @@ export interface Project extends Space {
|
||||
defaultIssueStatus: Ref<IssueStatus>
|
||||
defaultAssignee?: Ref<Employee>
|
||||
icon?: Asset
|
||||
workDayLength?: WorkDayLength
|
||||
defaultTimeReportDay: TimeReportDayType
|
||||
}
|
||||
|
||||
@@ -66,14 +65,6 @@ export enum TimeReportDayType {
|
||||
PreviousWorkDay = 'PreviousWorkDay'
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum WorkDayLength {
|
||||
SEVEN_HOURS = 7,
|
||||
EIGHT_HOURS = 8
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user