mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-23 18:15:03 +02:00
TSK-915: Tracker status (#2802)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user