mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-23 18:15:03 +02:00
TSK-810 Rename Team -> Project, Project -> Component (#2756)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
+32
-29
@@ -16,16 +16,16 @@
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel, IntlString, translate } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { Button, ButtonShape, eventToHTMLElement, SelectPopup, showPopup, Label } from '@hcengineering/ui'
|
||||
import tracker from '../plugin'
|
||||
|
||||
export let value: Ref<Project> | null | undefined
|
||||
export let value: Ref<Component> | null | undefined
|
||||
export let shouldShowLabel: boolean = true
|
||||
export let isEditable: boolean = true
|
||||
export let onChange: ((newProjectId: Ref<Project> | undefined) => void) | undefined = undefined
|
||||
export let popupPlaceholder: IntlString = tracker.string.AddToProject
|
||||
export let onChange: ((newComponentId: Ref<Component> | undefined) => void) | undefined = undefined
|
||||
export let popupPlaceholder: IntlString = tracker.string.AddToComponent
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let shape: ButtonShape = undefined
|
||||
@@ -34,17 +34,17 @@
|
||||
export let onlyIcon: boolean = false
|
||||
export let enlargedText = false
|
||||
|
||||
let selectedProject: Project | undefined
|
||||
let defaultProjectLabel = ''
|
||||
let selectedComponent: Component | undefined
|
||||
let defaultComponentLabel = ''
|
||||
|
||||
const query = createQuery()
|
||||
let rawProjects: Project[] = []
|
||||
let rawComponents: Component[] = []
|
||||
let loading = true
|
||||
query.query(
|
||||
tracker.class.Project,
|
||||
tracker.class.Component,
|
||||
{},
|
||||
(res) => {
|
||||
rawProjects = res
|
||||
rawComponents = res
|
||||
loading = false
|
||||
},
|
||||
{
|
||||
@@ -52,58 +52,61 @@
|
||||
}
|
||||
)
|
||||
|
||||
$: handleSelectedProjectIdUpdated(value, rawProjects)
|
||||
$: handleSelectedComponentIdUpdated(value, rawComponents)
|
||||
|
||||
$: translate(tracker.string.Project, {}).then((result) => (defaultProjectLabel = result))
|
||||
$: projectIcon = selectedProject?.icon ?? tracker.icon.Projects
|
||||
$: projectText = shouldShowLabel ? selectedProject?.label ?? defaultProjectLabel : undefined
|
||||
$: translate(tracker.string.Component, {}).then((result) => (defaultComponentLabel = result))
|
||||
$: componentIcon = selectedComponent?.icon ?? tracker.icon.Components
|
||||
$: componentText = shouldShowLabel ? selectedComponent?.label ?? defaultComponentLabel : undefined
|
||||
|
||||
const handleSelectedProjectIdUpdated = async (newProjectId: Ref<Project> | null | undefined, projects: Project[]) => {
|
||||
if (newProjectId === null || newProjectId === undefined) {
|
||||
selectedProject = undefined
|
||||
const handleSelectedComponentIdUpdated = async (
|
||||
newComponentId: Ref<Component> | null | undefined,
|
||||
components: Component[]
|
||||
) => {
|
||||
if (newComponentId === null || newComponentId === undefined) {
|
||||
selectedComponent = undefined
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
selectedProject = projects.find((it) => it._id === newProjectId)
|
||||
selectedComponent = components.find((it) => it._id === newComponentId)
|
||||
}
|
||||
|
||||
const handleProjectEditorOpened = async (event: MouseEvent): Promise<void> => {
|
||||
const handleComponentEditorOpened = async (event: MouseEvent): Promise<void> => {
|
||||
event.stopPropagation()
|
||||
if (!isEditable) {
|
||||
return
|
||||
}
|
||||
|
||||
const projectsInfo = [
|
||||
{ id: null, icon: tracker.icon.Projects, label: tracker.string.NoProject, isSelected: !selectedProject },
|
||||
...rawProjects.map((p) => ({
|
||||
const componentsInfo = [
|
||||
{ id: null, icon: tracker.icon.Components, label: tracker.string.NoComponent, isSelected: !selectedComponent },
|
||||
...rawComponents.map((p) => ({
|
||||
id: p._id,
|
||||
icon: p.icon,
|
||||
text: p.label,
|
||||
isSelected: selectedProject ? p._id === selectedProject._id : false
|
||||
isSelected: selectedComponent ? p._id === selectedComponent._id : false
|
||||
}))
|
||||
]
|
||||
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{ value: projectsInfo, placeholder: popupPlaceholder, searchable: true },
|
||||
{ value: componentsInfo, placeholder: popupPlaceholder, searchable: true },
|
||||
eventToHTMLElement(event),
|
||||
onChange
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if onlyIcon || projectText === undefined}
|
||||
{#if onlyIcon || componentText === undefined}
|
||||
<Button
|
||||
{kind}
|
||||
{size}
|
||||
{shape}
|
||||
{width}
|
||||
{justify}
|
||||
icon={projectIcon}
|
||||
icon={componentIcon}
|
||||
disabled={!isEditable}
|
||||
{loading}
|
||||
on:click={handleProjectEditorOpened}
|
||||
on:click={handleComponentEditorOpened}
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
@@ -112,17 +115,17 @@
|
||||
{shape}
|
||||
{width}
|
||||
{justify}
|
||||
icon={projectIcon}
|
||||
icon={componentIcon}
|
||||
disabled={!isEditable}
|
||||
{loading}
|
||||
on:click={handleProjectEditorOpened}
|
||||
on:click={handleComponentEditorOpened}
|
||||
><svelte:fragment slot="content">
|
||||
<span
|
||||
class="{enlargedText ? 'ml-1 text-base fs-bold' : 'text-md'} overflow-label {!value
|
||||
? 'content-color'
|
||||
: 'content-accent-color'} pointer-events-none"
|
||||
>
|
||||
<Label label={getEmbeddedLabel(projectText)} />
|
||||
<Label label={getEmbeddedLabel(componentText)} />
|
||||
</span>
|
||||
</svelte:fragment></Button
|
||||
>
|
||||
@@ -46,9 +46,9 @@
|
||||
IssuePriority,
|
||||
IssueStatus,
|
||||
IssueTemplate,
|
||||
Project,
|
||||
Component as ComponentType,
|
||||
Sprint,
|
||||
Team
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import {
|
||||
ActionIcon,
|
||||
@@ -68,7 +68,7 @@
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { activeProject, activeSprint, generateIssueShortLink, getIssueId, updateIssueRelation } from '../issues'
|
||||
import { activeComponent, activeSprint, generateIssueShortLink, getIssueId, updateIssueRelation } from '../issues'
|
||||
import tracker from '../plugin'
|
||||
import AssigneeEditor from './issues/AssigneeEditor.svelte'
|
||||
import IssueNotification from './issues/IssueNotification.svelte'
|
||||
@@ -76,17 +76,17 @@
|
||||
import PriorityEditor from './issues/PriorityEditor.svelte'
|
||||
import StatusEditor from './issues/StatusEditor.svelte'
|
||||
import EstimationEditor from './issues/timereport/EstimationEditor.svelte'
|
||||
import ProjectSelector from './ProjectSelector.svelte'
|
||||
import ComponentSelector from './ComponentSelector.svelte'
|
||||
import SetDueDateActionPopup from './SetDueDateActionPopup.svelte'
|
||||
import SetParentIssueActionPopup from './SetParentIssueActionPopup.svelte'
|
||||
import SprintSelector from './sprints/SprintSelector.svelte'
|
||||
import SubIssues from './SubIssues.svelte'
|
||||
|
||||
export let space: Ref<Team>
|
||||
export let space: Ref<Project>
|
||||
export let status: Ref<IssueStatus> | undefined = undefined
|
||||
export let priority: IssuePriority = IssuePriority.NoPriority
|
||||
export let assignee: Ref<Employee> | null = null
|
||||
export let project: Ref<Project> | null = $activeProject ?? null
|
||||
export let component: Ref<ComponentType> | null = $activeComponent ?? null
|
||||
export let sprint: Ref<Sprint> | null = $activeSprint ?? null
|
||||
export let relatedTo: Doc | undefined
|
||||
export let shouldSaveDraft: boolean = false
|
||||
@@ -102,7 +102,7 @@
|
||||
let labels: TagReference[] = draft?.labels || []
|
||||
let objectId: Ref<Issue> = draft?.issueId || generateId()
|
||||
let saveTimer: number | undefined
|
||||
let currentTeam: Team | undefined
|
||||
let currentProject: Project | undefined
|
||||
|
||||
function toIssue (initials: AttachedData<Issue>, draft: IssueDraft | undefined): AttachedData<Issue> {
|
||||
if (draft === undefined) {
|
||||
@@ -116,7 +116,7 @@
|
||||
title: '',
|
||||
description: '',
|
||||
assignee: '' as Ref<Employee>,
|
||||
project,
|
||||
component,
|
||||
sprint,
|
||||
number: 0,
|
||||
rank: '',
|
||||
@@ -152,8 +152,8 @@
|
||||
subIssues = []
|
||||
labels = []
|
||||
if (!originalIssue && !draft) {
|
||||
updateIssueStatusId(currentTeam, status)
|
||||
updateAssigneeId(currentTeam)
|
||||
updateIssueStatusId(currentProject, status)
|
||||
updateAssigneeId(currentProject)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
const { _class, _id, space, children, comments, attachments, labels: labels_, ...templBase } = template
|
||||
|
||||
subIssues = template.children.map((p) => {
|
||||
return { ...p, status: currentTeam?.defaultIssueStatus ?? ('' as Ref<IssueStatus>) }
|
||||
return { ...p, status: currentProject?.defaultIssueStatus ?? ('' as Ref<IssueStatus>) }
|
||||
})
|
||||
|
||||
object = {
|
||||
@@ -231,9 +231,9 @@
|
||||
attr: client.getHierarchy().getAttribute(tracker.class.Issue, 'labels')
|
||||
}
|
||||
|
||||
$: _space = draft?.team || space
|
||||
$: !originalIssue && !draft && updateIssueStatusId(currentTeam, status)
|
||||
$: !originalIssue && !draft && updateAssigneeId(currentTeam)
|
||||
$: _space = draft?.project || space
|
||||
$: !originalIssue && !draft && updateIssueStatusId(currentProject, status)
|
||||
$: !originalIssue && !draft && updateAssigneeId(currentProject)
|
||||
$: canSave = getTitle(object.title ?? '').length > 0
|
||||
|
||||
$: statusesQuery.query(
|
||||
@@ -247,8 +247,8 @@
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: _space }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
$: spaceQuery.query(tracker.class.Project, { _id: _space }, (res) => {
|
||||
currentProject = res.shift()
|
||||
})
|
||||
|
||||
async function setPropsFromOriginalIssue () {
|
||||
@@ -307,20 +307,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function updateIssueStatusId (currentTeam: Team | undefined, issueStatusId?: Ref<IssueStatus>) {
|
||||
async function updateIssueStatusId (currentProject: Project | undefined, issueStatusId?: Ref<IssueStatus>) {
|
||||
if (issueStatusId !== undefined) {
|
||||
object.status = issueStatusId
|
||||
return
|
||||
}
|
||||
|
||||
if (currentTeam?.defaultIssueStatus) {
|
||||
object.status = currentTeam.defaultIssueStatus
|
||||
if (currentProject?.defaultIssueStatus) {
|
||||
object.status = currentProject.defaultIssueStatus
|
||||
}
|
||||
}
|
||||
|
||||
function updateAssigneeId (currentTeam: Team | undefined) {
|
||||
if (currentTeam?.defaultAssignee !== undefined) {
|
||||
object.assignee = currentTeam.defaultAssignee
|
||||
function updateAssigneeId (currentProject: Project | undefined) {
|
||||
if (currentProject?.defaultAssignee !== undefined) {
|
||||
object.assignee = currentProject.defaultAssignee
|
||||
} else {
|
||||
object.assignee = null
|
||||
}
|
||||
@@ -357,7 +357,7 @@
|
||||
return false
|
||||
}
|
||||
|
||||
if (draft.project && draft.project !== defaultIssue.project) {
|
||||
if (draft.component && draft.component !== defaultIssue.component) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -369,16 +369,16 @@
|
||||
return true
|
||||
}
|
||||
|
||||
if (currentTeam?.defaultIssueStatus) {
|
||||
return draft.status === currentTeam.defaultIssueStatus
|
||||
if (currentProject?.defaultIssueStatus) {
|
||||
return draft.status === currentProject.defaultIssueStatus
|
||||
}
|
||||
|
||||
if (draft.assignee === null) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (currentTeam?.defaultAssignee) {
|
||||
return draft.assignee === currentTeam.defaultAssignee
|
||||
if (currentProject?.defaultAssignee) {
|
||||
return draft.assignee === currentProject.defaultAssignee
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -394,7 +394,7 @@
|
||||
title: getTitle(object.title),
|
||||
description: (object.description as string).replaceAll('<p></p>', ''),
|
||||
assignee: object.assignee,
|
||||
project: object.project,
|
||||
component: object.component,
|
||||
sprint: object.sprint,
|
||||
status: object.status,
|
||||
priority: object.priority,
|
||||
@@ -404,7 +404,7 @@
|
||||
attachments: object.attachments,
|
||||
labels,
|
||||
parentIssue: parentIssue?._id,
|
||||
team: _space,
|
||||
project: _space,
|
||||
subIssues
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@
|
||||
|
||||
const lastOne = await client.findOne<Issue>(tracker.class.Issue, {}, { sort: { rank: SortingOrder.Descending } })
|
||||
const incResult = await client.updateDoc(
|
||||
tracker.class.Team,
|
||||
tracker.class.Project,
|
||||
core.space.Space,
|
||||
_space,
|
||||
{
|
||||
@@ -439,7 +439,7 @@
|
||||
title: getTitle(object.title),
|
||||
description: object.description,
|
||||
assignee: object.assignee,
|
||||
project: object.project,
|
||||
component: object.component,
|
||||
sprint: object.sprint,
|
||||
number: (incResult as any).object.sequence,
|
||||
status: object.status,
|
||||
@@ -498,7 +498,7 @@
|
||||
addNotification(await translate(tracker.string.IssueCreated, {}), getTitle(object.title), IssueNotification, {
|
||||
issueId: objectId,
|
||||
subTitlePostfix: (await translate(tracker.string.Created, { value: 1 })).toLowerCase(),
|
||||
issueUrl: currentTeam && generateIssueShortLink(getIssueId(currentTeam, value as Issue))
|
||||
issueUrl: currentProject && generateIssueShortLink(getIssueId(currentProject, value as Issue))
|
||||
})
|
||||
|
||||
objectId = generateId()
|
||||
@@ -550,25 +550,25 @@
|
||||
)
|
||||
}
|
||||
|
||||
const handleProjectIdChanged = (projectId: Ref<Project> | null | undefined) => {
|
||||
if (projectId === undefined) {
|
||||
const handleComponentIdChanged = (componentId: Ref<ComponentType> | null | undefined) => {
|
||||
if (componentId === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
object = { ...object, project: projectId }
|
||||
object = { ...object, component: componentId }
|
||||
}
|
||||
|
||||
const handleSprintIdChanged = async (sprintId: Ref<Sprint> | null | undefined) => {
|
||||
if (sprintId === undefined) {
|
||||
return
|
||||
}
|
||||
let projectSprintId: Ref<Project> | null
|
||||
let componentSprintId: Ref<ComponentType> | null
|
||||
if (sprintId != null) {
|
||||
const sprint = await client.findOne(tracker.class.Sprint, { _id: sprintId })
|
||||
projectSprintId = sprint && sprint.project ? sprint.project : null
|
||||
} else projectSprintId = null
|
||||
componentSprintId = sprint && sprint.component ? sprint.component : null
|
||||
} else componentSprintId = null
|
||||
|
||||
object = { ...object, sprint: sprintId, project: projectSprintId }
|
||||
object = { ...object, sprint: sprintId, component: componentSprintId }
|
||||
}
|
||||
|
||||
function addTagRef (tag: TagElement): void {
|
||||
@@ -640,7 +640,7 @@
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<div class="flex-row-center">
|
||||
<SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space={_space} />
|
||||
<SpaceSelector _class={tracker.class.Project} label={tracker.string.Project} bind:space={_space} />
|
||||
</div>
|
||||
<ObjectBox
|
||||
_class={tracker.class.IssueTemplate}
|
||||
@@ -701,12 +701,12 @@
|
||||
{#if issueStatuses}
|
||||
<SubIssues
|
||||
bind:this={subIssuesComponent}
|
||||
teamId={_space}
|
||||
projectId={_space}
|
||||
parent={objectId}
|
||||
statuses={issueStatuses ?? []}
|
||||
team={currentTeam}
|
||||
project={currentProject}
|
||||
sprint={object.sprint}
|
||||
project={object.project}
|
||||
component={object.component}
|
||||
/>
|
||||
{/if}
|
||||
<svelte:fragment slot="pool">
|
||||
@@ -752,12 +752,12 @@
|
||||
labels = labels.filter((it) => it._id !== evt.detail)
|
||||
}}
|
||||
/>
|
||||
<EstimationEditor kind={'no-border'} size={'small'} value={object} {currentTeam} />
|
||||
<ProjectSelector value={object.project} onChange={handleProjectIdChanged} />
|
||||
<EstimationEditor kind={'no-border'} size={'small'} value={object} {currentProject} />
|
||||
<ComponentSelector value={object.component} onChange={handleComponentIdChanged} />
|
||||
<SprintSelector
|
||||
value={object.sprint}
|
||||
onChange={handleSprintIdChanged}
|
||||
useProject={(!originalIssue && object.project) || undefined}
|
||||
useComponent={(!originalIssue && object.component) || undefined}
|
||||
/>
|
||||
{#if object.dueDate !== null}
|
||||
<DatePresenter bind:value={object.dueDate} editable />
|
||||
|
||||
@@ -38,14 +38,14 @@
|
||||
return
|
||||
}
|
||||
|
||||
const team = await client.findOne(tracker.class.Team, {})
|
||||
space = team?._id
|
||||
const project = await client.findOne(tracker.class.Project, {})
|
||||
space = project?._id
|
||||
}
|
||||
|
||||
async function newIssue (): Promise<void> {
|
||||
if (!space) {
|
||||
const team = await client.findOne(tracker.class.Team, {})
|
||||
space = team?._id
|
||||
const project = await client.findOne(tracker.class.Project, {})
|
||||
space = project?._id
|
||||
}
|
||||
|
||||
showPopup(CreateIssue, { space, shouldSaveDraft: true, onDraftChanged: handleDraftChanged }, 'top')
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
const options: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
space: tracker.class.Team,
|
||||
space: tracker.class.Project,
|
||||
status: [tracker.class.IssueStatus, { category: tracker.class.IssueStatusCategory }]
|
||||
},
|
||||
sort: { modifiedOn: SortingOrder.Descending }
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
Issue,
|
||||
IssueParentInfo,
|
||||
IssueStatus,
|
||||
Project,
|
||||
Component,
|
||||
Sprint,
|
||||
Team
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import { Button, closeTooltip, ExpandCollapse, IconAdd, Scroller } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
@@ -37,10 +37,10 @@
|
||||
import DraftIssueChildList from './templates/DraftIssueChildList.svelte'
|
||||
|
||||
export let parent: Ref<Issue>
|
||||
export let teamId: Ref<Team>
|
||||
export let team: Team | undefined
|
||||
export let projectId: Ref<Project>
|
||||
export let project: Project | undefined
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let project: Ref<Project> | null = null
|
||||
export let component: Ref<Component> | null = null
|
||||
export let subIssues: DraftIssueChild[] = []
|
||||
export let statuses: WithLookup<IssueStatus>[]
|
||||
|
||||
@@ -60,15 +60,15 @@
|
||||
const client = getClient()
|
||||
|
||||
export async function save (parents: IssueParentInfo[]) {
|
||||
if (team === undefined) return
|
||||
if (project === undefined) return
|
||||
saved = true
|
||||
|
||||
for (const subIssue of subIssues) {
|
||||
const lastOne = await client.findOne<Issue>(tracker.class.Issue, {}, { sort: { rank: SortingOrder.Descending } })
|
||||
const incResult = await client.updateDoc(
|
||||
tracker.class.Team,
|
||||
tracker.class.Project,
|
||||
core.space.Space,
|
||||
team._id,
|
||||
project._id,
|
||||
{
|
||||
$inc: { sequence: 1 }
|
||||
},
|
||||
@@ -79,7 +79,7 @@
|
||||
title: subIssue.title.trim(),
|
||||
description: subIssue.description,
|
||||
assignee: subIssue.assignee,
|
||||
project: subIssue.project,
|
||||
component: subIssue.component,
|
||||
sprint: subIssue.sprint,
|
||||
number: (incResult as any).object.sequence,
|
||||
status: subIssue.status,
|
||||
@@ -98,7 +98,7 @@
|
||||
|
||||
await client.addCollection(
|
||||
tracker.class.Issue,
|
||||
team._id,
|
||||
project._id,
|
||||
parent,
|
||||
tracker.class.Issue,
|
||||
'subIssues',
|
||||
@@ -109,7 +109,7 @@
|
||||
if ((subIssue.labels?.length ?? 0) > 0) {
|
||||
const tagElements = await client.findAll(tags.class.TagElement, { _id: { $in: subIssue.labels } })
|
||||
for (const label of tagElements) {
|
||||
await client.addCollection(tags.class.TagReference, team._id, childId, tracker.class.Issue, 'labels', {
|
||||
await client.addCollection(tags.class.TagReference, project._id, childId, tracker.class.Issue, 'labels', {
|
||||
title: label.title,
|
||||
color: label.color,
|
||||
tag: label._id
|
||||
@@ -133,7 +133,7 @@
|
||||
async function saveAttachment (doc: Attachment, issue: Ref<Issue>): Promise<void> {
|
||||
await client.addCollection(
|
||||
attachment.class.Attachment,
|
||||
teamId,
|
||||
projectId,
|
||||
issue,
|
||||
tracker.class.Issue,
|
||||
'attachments',
|
||||
@@ -208,10 +208,10 @@
|
||||
<Scroller>
|
||||
<DraftIssueChildList
|
||||
{statuses}
|
||||
{project}
|
||||
{component}
|
||||
{sprint}
|
||||
bind:issues={subIssues}
|
||||
team={teamId}
|
||||
project={projectId}
|
||||
on:move={handleIssueSwap}
|
||||
on:update-issue
|
||||
/>
|
||||
@@ -219,12 +219,12 @@
|
||||
</div>
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
{#if isCreating && team}
|
||||
{#if isCreating && project}
|
||||
<ExpandCollapse isExpanded={!isCollapsed} on:changeContent>
|
||||
<DraftIssueChildEditor
|
||||
{team}
|
||||
{statuses}
|
||||
{project}
|
||||
{statuses}
|
||||
{component}
|
||||
{sprint}
|
||||
on:close={() => {
|
||||
isCreating = false
|
||||
|
||||
+38
-43
@@ -17,58 +17,58 @@
|
||||
import { DocumentQuery, FindOptions, SortingOrder } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Button, IconAdd, Label, showPopup, TabList } from '@hcengineering/ui'
|
||||
import type { TabItem } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import view from '@hcengineering/view'
|
||||
import { getIncludedProjectStatuses, projectsTitleMap, ProjectsViewMode } from '../../utils'
|
||||
import NewProject from './NewProject.svelte'
|
||||
import ProjectsListBrowser from './ProjectsListBrowser.svelte'
|
||||
import { getIncludedComponentStatuses, componentsTitleMap, ComponentsViewMode } from '../../utils'
|
||||
import NewComponent from './NewComponent.svelte'
|
||||
import ComponentsListBrowser from './ComponentsListBrowser.svelte'
|
||||
|
||||
export let label: IntlString
|
||||
export let query: DocumentQuery<Project> = {}
|
||||
export let query: DocumentQuery<Component> = {}
|
||||
export let search: string = ''
|
||||
export let mode: ProjectsViewMode = 'all'
|
||||
export let mode: ComponentsViewMode = 'all'
|
||||
export let viewMode: 'list' | 'timeline' = 'list'
|
||||
|
||||
const ENTRIES_LIMIT = 200
|
||||
const resultProjectsQuery = createQuery()
|
||||
const resultComponentsQuery = createQuery()
|
||||
|
||||
const projectOptions: FindOptions<Project> = {
|
||||
const componentOptions: FindOptions<Component> = {
|
||||
sort: { modifiedOn: SortingOrder.Descending },
|
||||
limit: ENTRIES_LIMIT,
|
||||
lookup: { lead: contact.class.Employee, members: contact.class.Employee }
|
||||
}
|
||||
|
||||
let resultProjects: Project[] = []
|
||||
let resultComponents: Component[] = []
|
||||
|
||||
$: includedProjectStatuses = getIncludedProjectStatuses(mode)
|
||||
$: title = projectsTitleMap[mode]
|
||||
$: includedProjectsQuery = { status: { $in: includedProjectStatuses } }
|
||||
$: includedComponentStatuses = getIncludedComponentStatuses(mode)
|
||||
$: title = componentsTitleMap[mode]
|
||||
$: includedComponentsQuery = { status: { $in: includedComponentStatuses } }
|
||||
|
||||
$: baseQuery = {
|
||||
...includedProjectsQuery,
|
||||
...includedComponentsQuery,
|
||||
...query
|
||||
}
|
||||
|
||||
$: resultQuery = search === '' ? baseQuery : { $search: search, ...baseQuery }
|
||||
|
||||
$: resultProjectsQuery.query<Project>(
|
||||
tracker.class.Project,
|
||||
$: resultComponentsQuery.query<Component>(
|
||||
tracker.class.Component,
|
||||
{ ...resultQuery },
|
||||
(result) => {
|
||||
resultProjects = result
|
||||
resultComponents = result
|
||||
},
|
||||
projectOptions
|
||||
componentOptions
|
||||
)
|
||||
|
||||
const space = typeof query.space === 'string' ? query.space : tracker.team.DefaultTeam
|
||||
const space = typeof query.space === 'string' ? query.space : tracker.project.DefaultProject
|
||||
const showCreateDialog = async () => {
|
||||
showPopup(NewProject, { space, targetElement: null }, 'top')
|
||||
showPopup(NewComponent, { space, targetElement: null }, 'top')
|
||||
}
|
||||
|
||||
const handleViewModeChanged = (newMode: ProjectsViewMode) => {
|
||||
const handleViewModeChanged = (newMode: ComponentsViewMode) => {
|
||||
if (newMode === undefined || newMode === mode) {
|
||||
return
|
||||
}
|
||||
@@ -77,27 +77,27 @@
|
||||
}
|
||||
|
||||
const modeList: TabItem[] = [
|
||||
{ id: 'all', labelIntl: tracker.string.AllProjects, action: () => handleViewModeChanged('all') },
|
||||
{ id: 'backlog', labelIntl: tracker.string.BacklogProjects, action: () => handleViewModeChanged('backlog') },
|
||||
{ id: 'active', labelIntl: tracker.string.ActiveProjects, action: () => handleViewModeChanged('active') },
|
||||
{ id: 'closed', labelIntl: tracker.string.ClosedProjects, action: () => handleViewModeChanged('closed') }
|
||||
{ id: 'all', labelIntl: tracker.string.AllComponents, action: () => handleViewModeChanged('all') },
|
||||
{ id: 'backlog', labelIntl: tracker.string.BacklogComponents, action: () => handleViewModeChanged('backlog') },
|
||||
{ id: 'active', labelIntl: tracker.string.ActiveComponents, action: () => handleViewModeChanged('active') },
|
||||
{ id: 'closed', labelIntl: tracker.string.ClosedComponents, action: () => handleViewModeChanged('closed') }
|
||||
]
|
||||
const viewList: TabItem[] = [
|
||||
{ id: 'list', icon: view.icon.List, tooltip: view.string.List },
|
||||
{ id: 'timeline', icon: view.icon.Timeline, tooltip: view.string.Timeline }
|
||||
]
|
||||
|
||||
const retrieveMembers = (p: Project) => p.members
|
||||
const retrieveMembers = (p: Component) => p.members
|
||||
</script>
|
||||
|
||||
<div class="fs-title flex-between header">
|
||||
<div class="flex-center">
|
||||
<Label {label} />
|
||||
<div class="projectTitle">
|
||||
<div class="componentTitle">
|
||||
› <Label label={title} />
|
||||
</div>
|
||||
</div>
|
||||
<Button size="small" icon={IconAdd} label={tracker.string.Project} kind={'primary'} on:click={showCreateDialog} />
|
||||
<Button size="small" icon={IconAdd} label={tracker.string.Component} kind={'primary'} on:click={showCreateDialog} />
|
||||
</div>
|
||||
<div class="itemsContainer">
|
||||
<div class="flex-row-center">
|
||||
@@ -110,8 +110,7 @@
|
||||
}}
|
||||
/>
|
||||
<!-- <div class="ml-3 filterButton">
|
||||
<Button
|
||||
size="small"
|
||||
<BuComponet size="small"
|
||||
icon={IconAdd}
|
||||
kind={'link-bordered'}
|
||||
borderStyle={'dashed'}
|
||||
@@ -130,31 +129,31 @@
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ProjectsListBrowser
|
||||
_class={tracker.class.Project}
|
||||
<ComponentsListBrowser
|
||||
_class={tracker.class.Component}
|
||||
itemsConfig={[
|
||||
{ key: '', presenter: tracker.component.IconPresenter },
|
||||
{ key: '', presenter: tracker.component.ProjectPresenter, props: { kind: 'list' } },
|
||||
{ key: '', presenter: tracker.component.ComponentPresenter, props: { kind: 'list' } },
|
||||
{
|
||||
key: '$lookup.lead',
|
||||
presenter: tracker.component.LeadPresenter,
|
||||
props: { _class: tracker.class.Project, defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
props: { _class: tracker.class.Component, defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
presenter: contact.component.MembersPresenter,
|
||||
props: {
|
||||
kind: 'link',
|
||||
intlTitle: tracker.string.ProjectMembersTitle,
|
||||
intlSearchPh: tracker.string.ProjectMembersSearchPlaceholder,
|
||||
intlTitle: tracker.string.ComponentMembersTitle,
|
||||
intlSearchPh: tracker.string.ComponentMembersSearchPlaceholder,
|
||||
retrieveMembers
|
||||
}
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TargetDatePresenter },
|
||||
{ key: '', presenter: tracker.component.ProjectStatusPresenter },
|
||||
{ key: '', presenter: tracker.component.DeleteProjectPresenter, props: { space } }
|
||||
{ key: '', presenter: tracker.component.ComponentStatusPresenter },
|
||||
{ key: '', presenter: tracker.component.DeleteComponentPresenter, props: { space } }
|
||||
]}
|
||||
projects={resultProjects}
|
||||
components={resultComponents}
|
||||
{viewMode}
|
||||
/>
|
||||
|
||||
@@ -163,7 +162,7 @@
|
||||
padding: 0.5rem 0.75rem 0.5rem 2.25rem;
|
||||
}
|
||||
|
||||
.projectTitle {
|
||||
.componentTitle {
|
||||
display: flex;
|
||||
margin-left: 0.25rem;
|
||||
color: var(--content-color);
|
||||
@@ -180,8 +179,4 @@
|
||||
border-top: 1px solid var(--divider-color);
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
|
||||
// .filterButton {
|
||||
// color: var(--caption-color);
|
||||
// }
|
||||
</style>
|
||||
+14
-14
@@ -14,18 +14,18 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Issue, IssueTemplate, Project } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ButtonKind, ButtonShape, ButtonSize, tooltip } from '@hcengineering/ui'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Component, Issue, IssueTemplate } from '@hcengineering/tracker'
|
||||
import { ButtonKind, ButtonShape, ButtonSize, tooltip } from '@hcengineering/ui'
|
||||
import { activeComponent } from '../../issues'
|
||||
import tracker from '../../plugin'
|
||||
import ProjectSelector from '../ProjectSelector.svelte'
|
||||
import { activeProject } from '../../issues'
|
||||
import ComponentSelector from '../ComponentSelector.svelte'
|
||||
|
||||
export let value: Issue | IssueTemplate
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = true
|
||||
export let popupPlaceholder: IntlString = tracker.string.MoveToProject
|
||||
export let popupPlaceholder: IntlString = tracker.string.MoveToComponent
|
||||
export let shouldShowPlaceholder = true
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'large'
|
||||
@@ -38,21 +38,21 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const handleProjectIdChanged = async (newProjectId: Ref<Project> | null | undefined) => {
|
||||
if (!isEditable || newProjectId === undefined || value.project === newProjectId) {
|
||||
const handleComponentIdChanged = async (newComponentId: Ref<Component> | null | undefined) => {
|
||||
if (!isEditable || newComponentId === undefined || value.component === newComponentId) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.update(value, { project: newProjectId })
|
||||
await client.update(value, { component: newComponentId })
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if (value.project && value.project !== $activeProject && groupBy !== 'project') || shouldShowPlaceholder}
|
||||
{#if (value.component && value.component !== $activeComponent && groupBy !== 'component') || shouldShowPlaceholder}
|
||||
<div
|
||||
class:minus-margin={kind === 'list-header'}
|
||||
use:tooltip={{ label: value.project ? tracker.string.MoveToProject : tracker.string.AddToProject }}
|
||||
use:tooltip={{ label: value.component ? tracker.string.MoveToComponent : tracker.string.AddToComponent }}
|
||||
>
|
||||
<ProjectSelector
|
||||
<ComponentSelector
|
||||
{kind}
|
||||
{size}
|
||||
{shape}
|
||||
@@ -63,8 +63,8 @@
|
||||
{popupPlaceholder}
|
||||
{onlyIcon}
|
||||
{enlargedText}
|
||||
value={value.project}
|
||||
onChange={handleProjectIdChanged}
|
||||
value={value.component}
|
||||
onChange={handleComponentIdChanged}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
+7
-7
@@ -15,12 +15,12 @@
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { ObjectCreate, ObjectPopup } from '@hcengineering/presentation'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import ProjectTitlePresenter from './ProjectTitlePresenter.svelte'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import ComponentTitlePresenter from './ComponentTitlePresenter.svelte'
|
||||
|
||||
export let _class: Ref<Class<Project>>
|
||||
export let selected: Ref<Project> | undefined
|
||||
export let sprintQuery: DocumentQuery<Project> = {}
|
||||
export let _class: Ref<Class<Component>>
|
||||
export let selected: Ref<Component> | undefined
|
||||
export let sprintQuery: DocumentQuery<Component> = {}
|
||||
export let create: ObjectCreate | undefined = undefined
|
||||
export let allowDeselect = false
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
create !== undefined
|
||||
? {
|
||||
...create,
|
||||
update: (doc: Doc) => (doc as Project).label
|
||||
update: (doc: Doc) => (doc as Component).label
|
||||
}
|
||||
: undefined
|
||||
</script>
|
||||
@@ -46,6 +46,6 @@
|
||||
on:close
|
||||
>
|
||||
<svelte:fragment slot="item" let:item={sprint}>
|
||||
<ProjectTitlePresenter value={sprint} />
|
||||
<ComponentTitlePresenter value={sprint} />
|
||||
</svelte:fragment>
|
||||
</ObjectPopup>
|
||||
@@ -0,0 +1,55 @@
|
||||
<!--
|
||||
// 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 { WithLookup } from '@hcengineering/core'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { getCurrentLocation, Icon, navigate, tooltip } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let value: WithLookup<Component>
|
||||
export let withIcon = false
|
||||
export let onClick: () => void | undefined
|
||||
export let isInteractive = true
|
||||
|
||||
function navigateToComponent () {
|
||||
if (!isInteractive) {
|
||||
return
|
||||
}
|
||||
if (onClick) {
|
||||
onClick()
|
||||
}
|
||||
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[4] = 'components'
|
||||
loc.path[5] = value._id
|
||||
loc.path.length = 6
|
||||
loc.fragment = undefined
|
||||
navigate(loc)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex" on:click={navigateToComponent}>
|
||||
{#if withIcon}
|
||||
<div class="mr-2" use:tooltip={{ label: tracker.string.Component }}>
|
||||
<Icon icon={tracker.icon.Components} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<span title={value.label} class="fs-bold cursor-pointer caption-color overflow-label clear-mins">
|
||||
{value.label}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import ComponentStatusPresenter from './ComponentStatusPresenter.svelte'
|
||||
|
||||
export let object: Component
|
||||
</script>
|
||||
|
||||
<ComponentStatusPresenter value={object} shouldShowLabel />
|
||||
+7
-7
@@ -13,14 +13,14 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Project, ProjectStatus } from '@hcengineering/tracker'
|
||||
import { Component, ComponentStatus } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import type { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
import ProjectStatusSelector from './ProjectStatusSelector.svelte'
|
||||
import ComponentStatusSelector from './ComponentStatusSelector.svelte'
|
||||
|
||||
export let value: Project
|
||||
export let value: Component
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = false
|
||||
export let kind: ButtonKind = 'link'
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const handleProjectStatusChanged = async (newStatus: ProjectStatus | undefined) => {
|
||||
const handleComponentStatusChanged = async (newStatus: ComponentStatus | undefined) => {
|
||||
if (!isEditable || newStatus === undefined || value.status === newStatus) {
|
||||
return
|
||||
}
|
||||
@@ -40,7 +40,7 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<ProjectStatusSelector
|
||||
<ComponentStatusSelector
|
||||
{kind}
|
||||
{size}
|
||||
{width}
|
||||
@@ -48,7 +48,7 @@
|
||||
{isEditable}
|
||||
{shouldShowLabel}
|
||||
showTooltip={isEditable ? { label: tracker.string.SetStatus } : undefined}
|
||||
selectedProjectStatus={value.status}
|
||||
onProjectStatusChange={handleProjectStatusChanged}
|
||||
selectedComponentStatus={value.status}
|
||||
onComponentStatusChange={handleComponentStatusChanged}
|
||||
/>
|
||||
{/if}
|
||||
+14
-13
@@ -13,15 +13,16 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { ProjectStatus } from '@hcengineering/tracker'
|
||||
import { ComponentStatus } from '@hcengineering/tracker'
|
||||
import { Button, showPopup, SelectPopup, eventToHTMLElement } from '@hcengineering/ui'
|
||||
import type { ButtonKind, ButtonSize, LabelAndProps } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { defaultProjectStatuses, projectStatusAssets } from '../../utils'
|
||||
import { defaultComponentStatuses, componentStatusAssets } from '../../utils'
|
||||
|
||||
export let selectedProjectStatus: ProjectStatus | undefined
|
||||
export let selectedComponentStatus: ComponentStatus | undefined
|
||||
export let shouldShowLabel: boolean = true
|
||||
export let onProjectStatusChange: ((newProjectStatus: ProjectStatus | undefined) => void) | undefined = undefined
|
||||
export let onComponentStatusChange: ((newComponentStatus: ComponentStatus | undefined) => void) | undefined =
|
||||
undefined
|
||||
export let isEditable: boolean = true
|
||||
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
@@ -30,19 +31,19 @@
|
||||
export let width: string | undefined = 'min-content'
|
||||
export let showTooltip: LabelAndProps | undefined = undefined
|
||||
|
||||
$: selectedStatusIcon = selectedProjectStatus
|
||||
? projectStatusAssets[selectedProjectStatus].icon
|
||||
: tracker.icon.ProjectStatusBacklog
|
||||
$: selectedStatusIcon = selectedComponentStatus
|
||||
? componentStatusAssets[selectedComponentStatus].icon
|
||||
: tracker.icon.ComponentStatusBacklog
|
||||
|
||||
$: selectedStatusLabel = shouldShowLabel
|
||||
? selectedProjectStatus
|
||||
? projectStatusAssets[selectedProjectStatus].label
|
||||
? selectedComponentStatus
|
||||
? componentStatusAssets[selectedComponentStatus].label
|
||||
: tracker.string.Backlog
|
||||
: undefined
|
||||
|
||||
$: statusesInfo = defaultProjectStatuses.map((s) => ({ id: s, ...projectStatusAssets[s] }))
|
||||
$: statusesInfo = defaultComponentStatuses.map((s) => ({ id: s, ...componentStatusAssets[s] }))
|
||||
|
||||
const handleProjectStatusEditorOpened = (event: MouseEvent) => {
|
||||
const handleComponentStatusEditorOpened = (event: MouseEvent) => {
|
||||
if (!isEditable) {
|
||||
return
|
||||
}
|
||||
@@ -50,7 +51,7 @@
|
||||
SelectPopup,
|
||||
{ value: statusesInfo, placeholder: tracker.string.SetStatus, searchable: true },
|
||||
eventToHTMLElement(event),
|
||||
onProjectStatusChange
|
||||
onComponentStatusChange
|
||||
)
|
||||
}
|
||||
</script>
|
||||
@@ -64,5 +65,5 @@
|
||||
icon={selectedStatusIcon}
|
||||
label={selectedStatusLabel}
|
||||
{showTooltip}
|
||||
on:click={handleProjectStatusEditorOpened}
|
||||
on:click={handleComponentStatusEditorOpened}
|
||||
/>
|
||||
+23
-21
@@ -16,19 +16,19 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Class, Doc, FindOptions, getObjectValue, Ref, Timestamp } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Issue, Component } from '@hcengineering/tracker'
|
||||
import { CheckBox, Spinner, Timeline, TimelineRow } from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey } from '@hcengineering/view'
|
||||
import { buildModel, LoadingProps } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import ProjectPresenter from './ProjectPresenter.svelte'
|
||||
import ComponentPresenter from './ComponentPresenter.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let itemsConfig: (BuildModelKey | string)[]
|
||||
export let selectedObjectIds: Doc[] = []
|
||||
export let selectedRowIndex: number | undefined = undefined
|
||||
export let projects: Project[] | undefined = undefined
|
||||
export let components: Component[] | undefined = undefined
|
||||
export let loadingProps: LoadingProps | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -42,13 +42,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: options = { ...baseOptions } as FindOptions<Project>
|
||||
$: options = { ...baseOptions } as FindOptions<Component>
|
||||
$: selectedObjectIdsSet = new Set<Ref<Doc>>(selectedObjectIds.map((it) => it._id))
|
||||
let selectedRows: number[] = []
|
||||
$: if (selectedObjectIdsSet.size > 0 && projects !== undefined) {
|
||||
$: if (selectedObjectIdsSet.size > 0 && components !== undefined) {
|
||||
const tRows: number[] = []
|
||||
selectedObjectIdsSet.forEach((it) => {
|
||||
const index = projects?.findIndex((f) => f._id === it)
|
||||
const index = components?.findIndex((f) => f._id === it)
|
||||
if (index !== undefined) tRows.push(index)
|
||||
})
|
||||
selectedRows = tRows
|
||||
@@ -63,16 +63,16 @@
|
||||
}
|
||||
|
||||
export const onElementSelected = (offset: 1 | -1 | 0, docObject?: Doc) => {
|
||||
if (!projects) return
|
||||
if (!components) return
|
||||
|
||||
let position =
|
||||
(docObject !== undefined ? projects?.findIndex((x) => x._id === docObject?._id) : selectedRowIndex) ?? -1
|
||||
(docObject !== undefined ? components?.findIndex((x) => x._id === docObject?._id) : selectedRowIndex) ?? -1
|
||||
|
||||
position += offset
|
||||
if (position < 0) position = 0
|
||||
if (position >= projects.length) position = projects.length - 1
|
||||
if (position >= components.length) position = components.length - 1
|
||||
selectedRowIndex = position
|
||||
handleRowFocused(projects[position])
|
||||
handleRowFocused(components[position])
|
||||
|
||||
// if (objectRef) {
|
||||
// objectRef.scrollIntoView({ behavior: 'auto', block: 'nearest' })
|
||||
@@ -91,12 +91,12 @@
|
||||
$: buildModel({ client, _class, keys: itemsConfig, lookup: options.lookup }).then((res) => (itemModels = res))
|
||||
|
||||
let lines: TimelineRow[] | undefined
|
||||
$: lines = projects?.map((proj) => {
|
||||
$: lines = components?.map((proj) => {
|
||||
const tR: TimelineRow = { items: [] }
|
||||
tR.items = [
|
||||
{
|
||||
icon: proj.icon,
|
||||
presenter: ProjectPresenter,
|
||||
presenter: ComponentPresenter,
|
||||
props: { value: proj },
|
||||
startDate: proj.startDate as Timestamp,
|
||||
targetDate: proj.targetDate as Timestamp
|
||||
@@ -106,16 +106,18 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if projects && itemModels && lines}
|
||||
{#if components && itemModels && lines}
|
||||
<Timeline
|
||||
{lines}
|
||||
{selectedRows}
|
||||
selectedRow={selectedRowIndex}
|
||||
on:row-focus={(ev) => {
|
||||
if (ev.detail !== undefined && projects !== undefined) handleRowFocused(projects[ev.detail])
|
||||
if (ev.detail !== undefined && components !== undefined) handleRowFocused(components[ev.detail])
|
||||
}}
|
||||
on:check={(ev) => {
|
||||
if (ev.detail !== undefined && projects !== undefined) onObjectChecked([projects[ev.detail.row]], ev.detail.value)
|
||||
if (ev.detail !== undefined && components !== undefined) {
|
||||
onObjectChecked([components[ev.detail.row]], ev.detail.value)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svelte:fragment let:row>
|
||||
@@ -125,16 +127,16 @@
|
||||
<div class="iconPresenter">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, projects[row]) ?? ''}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if attributeModelIndex === 1}
|
||||
<div class="projectPresenter flex-grow">
|
||||
<div class="componentPresenter flex-grow">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, projects[row]) ?? ''}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
@@ -143,8 +145,8 @@
|
||||
<div class="gridElement">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, projects[row]) ?? ''}
|
||||
parentId={projects[row]._id}
|
||||
value={getObjectValue(attributeModel.key, components[row]) ?? ''}
|
||||
parentId={components[row]._id}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
@@ -500,7 +502,7 @@
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
.projectPresenter {
|
||||
.componentPresenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
+3
-3
@@ -13,16 +13,16 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Icon } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let value: Project | undefined
|
||||
export let value: Component | undefined
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<span class="overflow-label flex">
|
||||
<Icon icon={value.icon ?? tracker.icon.Project} size={'small'} />
|
||||
<Icon icon={value.icon ?? tracker.icon.Component} size={'small'} />
|
||||
<div class="ml-2 mr-2">
|
||||
{value.label}
|
||||
</div></span
|
||||
+21
-21
@@ -16,51 +16,51 @@
|
||||
import { DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { closePopup, closeTooltip, getCurrentLocation, location, navigate } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { ProjectsViewMode } from '../../utils'
|
||||
import EditProject from './EditProject.svelte'
|
||||
import ProjectBrowser from './ProjectBrowser.svelte'
|
||||
import { ComponentsViewMode } from '../../utils'
|
||||
import EditComponent from './EditComponent.svelte'
|
||||
import ComponentBrowser from './ComponentBrowser.svelte'
|
||||
|
||||
export let label: IntlString = tracker.string.Projects
|
||||
export let query: DocumentQuery<Project> = {}
|
||||
export let label: IntlString = tracker.string.Components
|
||||
export let query: DocumentQuery<Component> = {}
|
||||
export let search: string = ''
|
||||
export let mode: ProjectsViewMode = 'all'
|
||||
export let mode: ComponentsViewMode = 'all'
|
||||
|
||||
let projectId: Ref<Project> | undefined
|
||||
let project: Project | undefined
|
||||
let componentId: Ref<Component> | undefined
|
||||
let component: Component | undefined
|
||||
|
||||
onDestroy(
|
||||
location.subscribe(async (loc) => {
|
||||
closeTooltip()
|
||||
closePopup()
|
||||
|
||||
projectId = loc.path[5] as Ref<Project>
|
||||
componentId = loc.path[5] as Ref<Component>
|
||||
})
|
||||
)
|
||||
|
||||
const projectQuery = createQuery()
|
||||
$: if (projectId !== undefined) {
|
||||
projectQuery.query(tracker.class.Project, { _id: projectId }, (result) => {
|
||||
project = result.shift()
|
||||
const componentQuery = createQuery()
|
||||
$: if (componentId !== undefined) {
|
||||
componentQuery.query(tracker.class.Component, { _id: componentId }, (result) => {
|
||||
component = result.shift()
|
||||
})
|
||||
} else {
|
||||
projectQuery.unsubscribe()
|
||||
project = undefined
|
||||
componentQuery.unsubscribe()
|
||||
component = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if project}
|
||||
<EditProject
|
||||
{project}
|
||||
on:project={(evt) => {
|
||||
{#if component}
|
||||
<EditComponent
|
||||
{component}
|
||||
on:component={(evt) => {
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[5] = evt.detail
|
||||
navigate(loc)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<ProjectBrowser {label} {query} {search} {mode} />
|
||||
<ComponentBrowser {label} {query} {search} {mode} />
|
||||
{/if}
|
||||
+16
-16
@@ -16,7 +16,7 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Class, Doc, FindOptions, getObjectValue, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Issue, Component } from '@hcengineering/tracker'
|
||||
import { CheckBox, Spinner, tooltip } from '@hcengineering/ui'
|
||||
import { BuildModelKey } from '@hcengineering/view'
|
||||
import { buildModel, LoadingProps } from '@hcengineering/view-resources'
|
||||
@@ -27,7 +27,7 @@
|
||||
export let itemsConfig: (BuildModelKey | string)[]
|
||||
export let selectedObjectIds: Doc[] = []
|
||||
export let selectedRowIndex: number | undefined = undefined
|
||||
export let projects: Project[] | undefined = undefined
|
||||
export let components: Component[] | undefined = undefined
|
||||
export let loadingProps: LoadingProps | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -42,9 +42,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: options = { ...baseOptions } as FindOptions<Project>
|
||||
$: options = { ...baseOptions } as FindOptions<Component>
|
||||
$: selectedObjectIdsSet = new Set<Ref<Doc>>(selectedObjectIds.map((it) => it._id))
|
||||
$: objectRefs.length = projects?.length ?? 0
|
||||
$: objectRefs.length = components?.length ?? 0
|
||||
|
||||
export const onObjectChecked = (docs: Doc[], value: boolean) => {
|
||||
dispatch('check', { docs, value })
|
||||
@@ -55,12 +55,12 @@
|
||||
}
|
||||
|
||||
export const onElementSelected = (offset: 1 | -1 | 0, docObject?: Doc) => {
|
||||
if (!projects) {
|
||||
if (!components) {
|
||||
return
|
||||
}
|
||||
|
||||
let position =
|
||||
(docObject !== undefined ? projects?.findIndex((x) => x._id === docObject?._id) : selectedRowIndex) ?? -1
|
||||
(docObject !== undefined ? components?.findIndex((x) => x._id === docObject?._id) : selectedRowIndex) ?? -1
|
||||
|
||||
position += offset
|
||||
|
||||
@@ -68,15 +68,15 @@
|
||||
position = 0
|
||||
}
|
||||
|
||||
if (position >= projects.length) {
|
||||
position = projects.length - 1
|
||||
if (position >= components.length) {
|
||||
position = components.length - 1
|
||||
}
|
||||
|
||||
const objectRef = objectRefs[position]
|
||||
|
||||
selectedRowIndex = position
|
||||
|
||||
handleRowFocused(projects[position])
|
||||
handleRowFocused(components[position])
|
||||
|
||||
if (objectRef) {
|
||||
objectRef.scrollIntoView({ behavior: 'auto', block: 'nearest' })
|
||||
@@ -94,14 +94,14 @@
|
||||
|
||||
{#await buildModel({ client, _class, keys: itemsConfig, lookup: options.lookup }) then itemModels}
|
||||
<div class="listRoot">
|
||||
{#if projects}
|
||||
{#each projects as docObject (docObject._id)}
|
||||
{#if components}
|
||||
{#each components as docObject (docObject._id)}
|
||||
<div
|
||||
bind:this={objectRefs[projects.findIndex((x) => x === docObject)]}
|
||||
bind:this={objectRefs[components.findIndex((x) => x === docObject)]}
|
||||
class="listGrid"
|
||||
class:mListGridChecked={selectedObjectIdsSet.has(docObject._id)}
|
||||
class:mListGridFixed={selectedRowIndex === projects.findIndex((x) => x === docObject)}
|
||||
class:mListGridSelected={selectedRowIndex === projects.findIndex((x) => x === docObject)}
|
||||
class:mListGridFixed={selectedRowIndex === components.findIndex((x) => x === docObject)}
|
||||
class:mListGridSelected={selectedRowIndex === components.findIndex((x) => x === docObject)}
|
||||
on:focus={() => {}}
|
||||
on:mouseover={() => handleRowFocused(docObject)}
|
||||
>
|
||||
@@ -129,7 +129,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else if attributeModelIndex === 1}
|
||||
<div class="projectPresenter flex-grow">
|
||||
<div class="componentPresenter flex-grow">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
@@ -235,7 +235,7 @@
|
||||
padding-left: 0.45rem;
|
||||
}
|
||||
|
||||
.projectPresenter {
|
||||
.componentPresenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
+16
-16
@@ -23,29 +23,29 @@
|
||||
selectionStore,
|
||||
LoadingProps
|
||||
} from '@hcengineering/view-resources'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { onMount } from 'svelte'
|
||||
import ProjectsList from './ProjectsList.svelte'
|
||||
import ProjectsTimeline from './ProjectsTimeline.svelte'
|
||||
import ComponentsList from './ComponentsList.svelte'
|
||||
import ComponentTimeline from './ComponentTimeline.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let itemsConfig: (BuildModelKey | string)[]
|
||||
export let loadingProps: LoadingProps | undefined = undefined
|
||||
export let projects: Project[] = []
|
||||
export let components: Component[] = []
|
||||
export let viewMode: 'list' | 'timeline' = 'list'
|
||||
|
||||
const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {
|
||||
if (dir === 'vertical') {
|
||||
if (viewMode === 'list') projectsList.onElementSelected(offset, of)
|
||||
else projectsTimeline.onElementSelected(offset, of)
|
||||
if (viewMode === 'list') componentsList.onElementSelected(offset, of)
|
||||
else componentTimeline.onElementSelected(offset, of)
|
||||
}
|
||||
})
|
||||
|
||||
let projectsList: ProjectsList
|
||||
let projectsTimeline: ProjectsTimeline
|
||||
let componentsList: ComponentsList
|
||||
let componentTimeline: ComponentTimeline
|
||||
|
||||
$: if (projectsList !== undefined) {
|
||||
listProvider.update(projects)
|
||||
$: if (componentsList !== undefined) {
|
||||
listProvider.update(components)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@@ -60,12 +60,12 @@
|
||||
/>
|
||||
|
||||
{#if viewMode === 'list'}
|
||||
<ProjectsList
|
||||
bind:this={projectsList}
|
||||
<ComponentsList
|
||||
bind:this={componentsList}
|
||||
{_class}
|
||||
{itemsConfig}
|
||||
{loadingProps}
|
||||
{projects}
|
||||
{components}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
selectedRowIndex={listProvider.current($focusStore)}
|
||||
on:row-focus={(event) => {
|
||||
@@ -76,12 +76,12 @@
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<ProjectsTimeline
|
||||
bind:this={projectsTimeline}
|
||||
<ComponentTimeline
|
||||
bind:this={componentTimeline}
|
||||
{_class}
|
||||
{itemsConfig}
|
||||
{loadingProps}
|
||||
{projects}
|
||||
{components}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
selectedRowIndex={listProvider.current($focusStore)}
|
||||
on:row-focus={(event) => {
|
||||
+8
-7
@@ -16,17 +16,18 @@
|
||||
import view from '@hcengineering/view'
|
||||
import { Button, ButtonSize, LabelAndProps, showPopup } from '@hcengineering/ui'
|
||||
import { getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import type { Project } from '@hcengineering/tracker'
|
||||
import type { Component } from '@hcengineering/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let space: Ref<Space>
|
||||
export let value: Project
|
||||
export let value: Component
|
||||
export let size: ButtonSize = 'medium'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = 'min-content'
|
||||
export let showTooltip: LabelAndProps | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -34,21 +35,21 @@
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: tracker.string.RemoveProjectDialogClose,
|
||||
message: tracker.string.RemoveProjectDialogCloseNote
|
||||
label: tracker.string.RemoveComponentDialogClose,
|
||||
message: tracker.string.RemoveComponentDialogCloseNote
|
||||
},
|
||||
'top',
|
||||
(result?: boolean) => {
|
||||
if (result === true) {
|
||||
dispatch('close')
|
||||
removeProject()
|
||||
removeComponent()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function removeProject () {
|
||||
await client.removeDoc(tracker.class.Project, space, value._id)
|
||||
async function removeComponent () {
|
||||
await client.removeDoc(tracker.class.Component, space, value._id)
|
||||
}
|
||||
</script>
|
||||
|
||||
+21
-17
@@ -1,63 +1,67 @@
|
||||
<script lang="ts">
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Button, EditBox, Icon, showPopup } from '@hcengineering/ui'
|
||||
import { DocAttributeBar } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { activeProject } from '../../issues'
|
||||
import { activeComponent } from '../../issues'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesView from '../issues/IssuesView.svelte'
|
||||
import ProjectPopup from './ProjectPopup.svelte'
|
||||
import ComponentPopup from './ComponentPopup.svelte'
|
||||
|
||||
export let project: Project
|
||||
export let component: Component
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function change (field: string, value: any) {
|
||||
await client.update(project, { [field]: value })
|
||||
await client.update(component, { [field]: value })
|
||||
}
|
||||
function selectProject (evt: MouseEvent): void {
|
||||
showPopup(ProjectPopup, { _class: tracker.class.Project }, evt.target as HTMLElement, (value) => {
|
||||
function selectComponent (evt: MouseEvent): void {
|
||||
showPopup(ComponentPopup, { _class: tracker.class.Component }, evt.target as HTMLElement, (value) => {
|
||||
if (value != null) {
|
||||
project = value
|
||||
dispatch('project', project._id)
|
||||
component = value
|
||||
dispatch('component', component._id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$: $activeProject = project?._id
|
||||
$: $activeComponent = component?._id
|
||||
|
||||
onDestroy(() => {
|
||||
$activeProject = undefined
|
||||
$activeComponent = undefined
|
||||
})
|
||||
</script>
|
||||
|
||||
<IssuesView query={{ project: project._id, space: project.space }} space={project.space} label={project.label}>
|
||||
<IssuesView
|
||||
query={{ component: component._id, space: component.space }}
|
||||
space={component.space}
|
||||
label={component.label}
|
||||
>
|
||||
<svelte:fragment slot="label_selector">
|
||||
<Button size={'small'} kind={'link'} on:click={selectProject}>
|
||||
<Button size={'small'} kind={'link'} on:click={selectComponent}>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="ac-header__icon"><Icon icon={tracker.icon.Issues} size={'small'} /></div>
|
||||
<span class="ac-header__title">{project.label}</span>
|
||||
<span class="ac-header__title">{component.label}</span>
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="aside">
|
||||
<div class="flex-row p-4 w-60 left-divider">
|
||||
<div class="fs-title text-xl">
|
||||
<EditBox bind:value={project.label} on:change={() => change('label', project.label)} />
|
||||
<EditBox bind:value={component.label} on:change={() => change('label', component.label)} />
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<StyledTextBox
|
||||
alwaysEdit={true}
|
||||
showButtons={false}
|
||||
placeholder={tracker.string.Description}
|
||||
content={project.description ?? ''}
|
||||
content={component.description ?? ''}
|
||||
on:value={(evt) => change('description', evt.detail)}
|
||||
/>
|
||||
</div>
|
||||
<DocAttributeBar object={project} mixins={[]} ignoreKeys={['icon', 'label', 'description']} />
|
||||
<DocAttributeBar object={component} mixins={[]} ignoreKeys={['icon', 'label', 'description']} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</IssuesView>
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { Button } from '@hcengineering/ui'
|
||||
|
||||
export let value: WithLookup<Project>
|
||||
export let value: WithLookup<Component>
|
||||
</script>
|
||||
|
||||
<Button size="small" kind="link" icon={value.icon} />
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
<div class="textContainer">
|
||||
<div class="title">
|
||||
<Label label={tracker.string.ProjectLeadTitle} />
|
||||
<Label label={tracker.string.ComponentLeadTitle} />
|
||||
</div>
|
||||
<div class="description">
|
||||
{lead.name}
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@hcengineering/contact'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { Project, Sprint } from '@hcengineering/tracker'
|
||||
import { Component, Sprint } from '@hcengineering/tracker'
|
||||
import { UsersPopup, getClient } from '@hcengineering/presentation'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { eventToHTMLElement, IconSize, showPopup } from '@hcengineering/ui'
|
||||
@@ -25,7 +25,7 @@
|
||||
import LeadPopup from './LeadPopup.svelte'
|
||||
|
||||
export let value: Employee | null
|
||||
export let _class: Ref<Class<Project | Sprint>>
|
||||
export let _class: Ref<Class<Component | Sprint>>
|
||||
export let size: IconSize = 'x-small'
|
||||
export let parentId: Ref<Doc>
|
||||
export let defaultClass: Ref<Class<Doc>> | undefined = undefined
|
||||
@@ -54,7 +54,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
const currentParent = await client.findOne(_class, { _id: parentId as Ref<Project> })
|
||||
const currentParent = await client.findOne(_class, { _id: parentId as Ref<Component> })
|
||||
|
||||
if (currentParent === undefined) {
|
||||
return
|
||||
@@ -78,7 +78,7 @@
|
||||
active: true
|
||||
},
|
||||
allowDeselect: true,
|
||||
placeholder: tracker.string.ProjectLeadSearchPlaceholder
|
||||
placeholder: tracker.string.ComponentLeadSearchPlaceholder
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
handleLeadChanged
|
||||
+21
-18
@@ -16,22 +16,22 @@
|
||||
import { Data, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { Card, getClient, SpaceSelector, EmployeeBox, UserBoxList } from '@hcengineering/presentation'
|
||||
import { Project, ProjectStatus, Team } from '@hcengineering/tracker'
|
||||
import { Component, ComponentStatus, Project } from '@hcengineering/tracker'
|
||||
import { DatePresenter, EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import ProjectStatusSelector from './ProjectStatusSelector.svelte'
|
||||
import ComponentStatusSelector from './ComponentStatusSelector.svelte'
|
||||
import { StyledTextArea } from '@hcengineering/text-editor'
|
||||
|
||||
export let space: Ref<Team>
|
||||
export let space: Ref<Project>
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
const object: Data<Project> = {
|
||||
const object: Data<Component> = {
|
||||
label: '' as IntlString,
|
||||
description: '',
|
||||
icon: tracker.icon.Projects,
|
||||
status: ProjectStatus.Backlog,
|
||||
icon: tracker.icon.Components,
|
||||
status: ComponentStatus.Backlog,
|
||||
lead: null,
|
||||
members: [],
|
||||
comments: 0,
|
||||
@@ -41,45 +41,48 @@
|
||||
}
|
||||
|
||||
async function onSave () {
|
||||
await client.createDoc(tracker.class.Project, space, object)
|
||||
await client.createDoc(tracker.class.Component, space, object)
|
||||
}
|
||||
|
||||
const handleProjectStatusChanged = (newProjectStatus: ProjectStatus | undefined) => {
|
||||
if (newProjectStatus === undefined) {
|
||||
const handleComponentStatusChanged = (newComponentStatus: ComponentStatus | undefined) => {
|
||||
if (newComponentStatus === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
object.status = newProjectStatus
|
||||
object.status = newComponentStatus
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={tracker.string.NewProject}
|
||||
label={tracker.string.NewComponent}
|
||||
okAction={onSave}
|
||||
canSave={object.label !== ''}
|
||||
okLabel={tracker.string.CreateProject}
|
||||
okLabel={tracker.string.CreateComponent}
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space />
|
||||
<SpaceSelector _class={tracker.class.Project} label={tracker.string.Project} bind:space />
|
||||
</svelte:fragment>
|
||||
<EditBox bind:value={object.label} placeholder={tracker.string.ProjectNamePlaceholder} kind={'large-style'} focus />
|
||||
<EditBox bind:value={object.label} placeholder={tracker.string.ComponentNamePlaceholder} kind={'large-style'} focus />
|
||||
<StyledTextArea
|
||||
bind:content={object.description}
|
||||
placeholder={tracker.string.ProjectDescriptionPlaceholder}
|
||||
placeholder={tracker.string.ComponentDescriptionPlaceholder}
|
||||
emphasized
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
<ProjectStatusSelector selectedProjectStatus={object.status} onProjectStatusChange={handleProjectStatusChanged} />
|
||||
<ComponentStatusSelector
|
||||
selectedComponentStatus={object.status}
|
||||
onComponentStatusChange={handleComponentStatusChanged}
|
||||
/>
|
||||
<EmployeeBox
|
||||
label={tracker.string.ProjectLead}
|
||||
label={tracker.string.ComponentLead}
|
||||
placeholder={tracker.string.AssignTo}
|
||||
bind:value={object.lead}
|
||||
allowDeselect
|
||||
titleDeselect={tracker.string.Unassigned}
|
||||
showNavigate={false}
|
||||
/>
|
||||
<UserBoxList bind:items={object.members} label={tracker.string.ProjectMembersSearchPlaceholder} />
|
||||
<UserBoxList bind:items={object.members} label={tracker.string.ComponentMembersSearchPlaceholder} />
|
||||
<!-- TODO: add labels after customize IssueNeedsToBeCompletedByThisDate -->
|
||||
<DatePresenter bind:value={object.startDate} labelNull={tracker.string.StartDate} editable />
|
||||
<DatePresenter bind:value={object.targetDate} labelNull={tracker.string.TargetDate} editable />
|
||||
+4
-4
@@ -14,10 +14,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Team } from '@hcengineering/tracker'
|
||||
import Projects from './Projects.svelte'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import Components from './Components.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
</script>
|
||||
|
||||
<Projects query={{ space: currentSpace }} />
|
||||
<Components query={{ space: currentSpace }} />
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import Projects from './Projects.svelte'
|
||||
import Components from './Components.svelte'
|
||||
import tracker from '../../plugin'
|
||||
</script>
|
||||
|
||||
<Projects label={tracker.string.Roadmap} />
|
||||
<Components label={tracker.string.Roadmap} />
|
||||
+2
-2
@@ -13,11 +13,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { Component } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import CommonTrackerDatePresenter from '../CommonTrackerDatePresenter.svelte'
|
||||
|
||||
export let value: Project
|
||||
export let value: Component
|
||||
|
||||
const client = getClient()
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
<script lang="ts">
|
||||
import { DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, Team } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesView from './IssuesView.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
|
||||
const statusQuery = createQuery()
|
||||
let query: DocumentQuery<Issue>
|
||||
|
||||
@@ -44,20 +44,20 @@
|
||||
return (issue as Issue).space !== undefined
|
||||
}
|
||||
|
||||
async function updateProjectMembers (issue: Issue | AttachedData<Issue> | IssueTemplateData) {
|
||||
if (issue.project) {
|
||||
const project = await client.findOne(tracker.class.Project, { _id: issue.project })
|
||||
projectLead = project?.lead || undefined
|
||||
projectMembers = project?.members || []
|
||||
async function updateComponentMembers (issue: Issue | AttachedData<Issue> | IssueTemplateData) {
|
||||
if (issue.component) {
|
||||
const component = await client.findOne(tracker.class.Component, { _id: issue.component })
|
||||
projectLead = component?.lead || undefined
|
||||
projectMembers = component?.members || []
|
||||
} else {
|
||||
projectLead = undefined
|
||||
projectMembers = []
|
||||
}
|
||||
if (hasSpace(issue)) {
|
||||
const team = await client.findOne(tracker.class.Team, { _id: issue.space })
|
||||
if (team !== undefined) {
|
||||
const project = await client.findOne(tracker.class.Project, { _id: issue.space })
|
||||
if (project !== undefined) {
|
||||
const accounts = await client.findAll(contact.class.EmployeeAccount, {
|
||||
_id: { $in: team.members as Ref<EmployeeAccount>[] }
|
||||
_id: { $in: project.members as Ref<EmployeeAccount>[] }
|
||||
})
|
||||
members = accounts.map((p) => p.employee)
|
||||
} else {
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: updateProjectMembers(value)
|
||||
$: updateComponentMembers(value)
|
||||
|
||||
const handleAssigneeChanged = async (newAssignee: Ref<Employee> | undefined) => {
|
||||
if (newAssignee === undefined || value.assignee === newAssignee) {
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
<script lang="ts">
|
||||
import { DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, Team } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesView from './IssuesView.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
|
||||
const statusQuery = createQuery()
|
||||
let query: DocumentQuery<Issue> = {}
|
||||
|
||||
+9
-9
@@ -30,22 +30,22 @@
|
||||
const elements: FilterSectionElement[] = []
|
||||
|
||||
const client = getClient()
|
||||
const projects = await client.findAll(tracker.class.Project, {})
|
||||
const components = await client.findAll(tracker.class.Component, {})
|
||||
for (const [key, value] of Object.entries(groups)) {
|
||||
const project = key === 'null' ? null : key
|
||||
const label = project
|
||||
? projects.find(({ _id }) => _id === project)?.label
|
||||
: await translate(tracker.string.NoProject, {})
|
||||
const component = key === 'null' ? null : key
|
||||
const label = component
|
||||
? components.find(({ _id }) => _id === component)?.label
|
||||
: await translate(tracker.string.NoComponent, {})
|
||||
|
||||
if (!label) {
|
||||
continue
|
||||
}
|
||||
elements.splice(project ? 1 : 0, 0, {
|
||||
icon: tracker.icon.Project,
|
||||
elements.splice(component ? 1 : 0, 0, {
|
||||
icon: tracker.icon.Component,
|
||||
title: label,
|
||||
count: value,
|
||||
isSelected: selected.includes(project),
|
||||
onSelect: () => onUpdate({ project }, index)
|
||||
isSelected: selected.includes(component),
|
||||
onSelect: () => onUpdate({ component }, index)
|
||||
})
|
||||
}
|
||||
return onBack
|
||||
@@ -15,13 +15,13 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import type { Issue, Team } from '@hcengineering/tracker'
|
||||
import type { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Icon } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { getIssueId } from '../../issues'
|
||||
|
||||
export let value: WithLookup<Issue>
|
||||
$: title = getIssueId(value.$lookup?.space as Team, value)
|
||||
$: title = getIssueId(value.$lookup?.space as Project, value)
|
||||
</script>
|
||||
|
||||
<div class="flex item">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import type { Issue, Team } from '@hcengineering/tracker'
|
||||
import type { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Icon, tooltip } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
@@ -28,22 +28,22 @@
|
||||
export let inline = false
|
||||
|
||||
// Extra properties
|
||||
export let teams: Map<Ref<Team>, Team> | undefined = undefined
|
||||
export let projects: Map<Ref<Project>, Project> | undefined = undefined
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
let currentTeam: Team | undefined = value?.$lookup?.space
|
||||
let currentProject: Project | undefined = value?.$lookup?.space
|
||||
|
||||
$: if (teams === undefined) {
|
||||
$: if (projects === undefined) {
|
||||
if (value && value?.$lookup?.space === undefined) {
|
||||
spaceQuery.query(tracker.class.Team, { _id: value.space }, (res) => ([currentTeam] = res))
|
||||
spaceQuery.query(tracker.class.Project, { _id: value.space }, (res) => ([currentProject] = res))
|
||||
} else {
|
||||
spaceQuery.unsubscribe()
|
||||
}
|
||||
} else {
|
||||
currentTeam = teams.get(value.space)
|
||||
currentProject = projects.get(value.space)
|
||||
}
|
||||
|
||||
$: title = currentTeam ? `${currentTeam.identifier}-${value?.number}` : `${value?.number}`
|
||||
$: title = currentProject ? `${currentProject.identifier}-${value?.number}` : `${value?.number}`
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import { CommentPopup } from '@hcengineering/chunter-resources'
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Label, resizeObserver, Scroller } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import AssigneeEditor from './AssigneeEditor.svelte'
|
||||
@@ -57,10 +57,10 @@
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
let currentTeam: Team | undefined
|
||||
let currentProject: Project | undefined
|
||||
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: space }, (res) => ([currentTeam] = res))
|
||||
$: issueName = currentTeam && issue && `${currentTeam.identifier}-${issue.number}`
|
||||
$: spaceQuery.query(tracker.class.Project, { _id: space }, (res) => ([currentProject] = res))
|
||||
$: issueName = currentProject && issue && `${currentProject.identifier}-${issue.number}`
|
||||
|
||||
const limit: number = 350
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Team } from '@hcengineering/tracker'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesView from './IssuesView.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
|
||||
$: query = { space: currentSpace }
|
||||
</script>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
} from '../../utils'
|
||||
import FilterMenu from '../FilterMenu.svelte'
|
||||
import PriorityFilterMenuSection from './PriorityFilterMenuSection.svelte'
|
||||
import ProjectFilterMenuSection from './ProjectFilterMenuSection.svelte'
|
||||
import ComponentFilterMenuSection from './ComponentFilterMenuSection.svelte'
|
||||
import SprintFilterMenuSection from './SprintFilterMenuSection.svelte'
|
||||
import StatusFilterMenuSection from './StatusFilterMenuSection.svelte'
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
$: defaultStatusIds = defaultStatuses.map((x) => x._id)
|
||||
$: groupedByStatus = getGroupedIssues('status', issues, defaultStatusIds)
|
||||
$: groupedByPriority = getGroupedIssues('priority', issues, defaultPriorities)
|
||||
$: groupedByProject = getGroupedIssues('project', issues)
|
||||
$: groupedByComponent = getGroupedIssues('component', issues)
|
||||
$: groupedBySprint = getGroupedIssues('sprint', issues)
|
||||
|
||||
const handleStatusFilterMenuSectionOpened = () => {
|
||||
@@ -86,17 +86,17 @@
|
||||
)
|
||||
}
|
||||
|
||||
const handleProjectFilterMenuSectionOpened = () => {
|
||||
const projectGroups: { [key: string]: number } = {}
|
||||
const handleComponentFilterMenuSectionOpened = () => {
|
||||
const componentGroups: { [key: string]: number } = {}
|
||||
|
||||
for (const [project, value] of Object.entries(groupedByProject)) {
|
||||
projectGroups[project] = value?.length ?? 0
|
||||
for (const [component, value] of Object.entries(groupedByComponent)) {
|
||||
componentGroups[component] = value?.length ?? 0
|
||||
}
|
||||
showPopup(
|
||||
ProjectFilterMenuSection,
|
||||
ComponentFilterMenuSection,
|
||||
{
|
||||
groups: projectGroups,
|
||||
selectedElements: currentFilterQuery?.project?.[currentFilterMode] ?? [],
|
||||
groups: componentGroups,
|
||||
selectedElements: currentFilterQuery?.component?.[currentFilterMode] ?? [],
|
||||
index,
|
||||
onUpdate,
|
||||
onBack
|
||||
@@ -108,8 +108,8 @@
|
||||
const handleSprintFilterMenuSectionOpened = () => {
|
||||
const sprintGroups: { [key: string]: number } = {}
|
||||
|
||||
for (const [project, value] of Object.entries(groupedBySprint)) {
|
||||
sprintGroups[project] = value?.length ?? 0
|
||||
for (const [sprint, value] of Object.entries(groupedBySprint)) {
|
||||
sprintGroups[sprint] = value?.length ?? 0
|
||||
}
|
||||
showPopup(
|
||||
SprintFilterMenuSection,
|
||||
@@ -134,8 +134,8 @@
|
||||
onSelect: handlePriorityFilterMenuSectionOpened
|
||||
},
|
||||
{
|
||||
...getIssueFilterAssetsByType('project'),
|
||||
onSelect: handleProjectFilterMenuSectionOpened
|
||||
...getIssueFilterAssetsByType('component'),
|
||||
onSelect: handleComponentFilterMenuSectionOpened
|
||||
},
|
||||
{
|
||||
...getIssueFilterAssetsByType('sprint'),
|
||||
|
||||
@@ -20,7 +20,15 @@
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import tags from '@hcengineering/tags'
|
||||
import { Issue, IssuesGrouping, IssuesOrdering, IssueStatus, Project, Sprint, Team } from '@hcengineering/tracker'
|
||||
import {
|
||||
Issue,
|
||||
IssuesGrouping,
|
||||
IssuesOrdering,
|
||||
IssueStatus,
|
||||
Component as ComponentType,
|
||||
Sprint,
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
Component,
|
||||
@@ -47,7 +55,7 @@
|
||||
import tracker from '../../plugin'
|
||||
import { issuesGroupBySorting, mapKanbanCategories } from '../../utils'
|
||||
import CreateIssue from '../CreateIssue.svelte'
|
||||
import ProjectEditor from '../projects/ProjectEditor.svelte'
|
||||
import ComponentEditor from '../components/ComponentEditor.svelte'
|
||||
import AssigneePresenter from './AssigneePresenter.svelte'
|
||||
import SubIssuesSelector from './edit/SubIssuesSelector.svelte'
|
||||
import IssuePresenter from './IssuePresenter.svelte'
|
||||
@@ -57,13 +65,13 @@
|
||||
import StatusEditor from './StatusEditor.svelte'
|
||||
import EstimationEditor from './timereport/EstimationEditor.svelte'
|
||||
|
||||
export let space: Ref<Team> | undefined = undefined
|
||||
export let space: Ref<Project> | undefined = undefined
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let query: DocumentQuery<Issue> = {}
|
||||
export let viewOptionsConfig: ViewOptionModel[] | undefined
|
||||
export let viewOptions: ViewOptions
|
||||
|
||||
$: currentSpace = space || tracker.team.DefaultTeam
|
||||
$: currentSpace = space || tracker.project.DefaultProject
|
||||
$: groupBy = (viewOptions.groupBy[0] ?? noCategory) as IssuesGrouping
|
||||
$: orderBy = viewOptions.orderBy
|
||||
$: sort = { [orderBy[0]]: orderBy[1] }
|
||||
@@ -71,9 +79,9 @@
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
let currentTeam: Team | undefined
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
let currentProject: Project | undefined
|
||||
$: spaceQuery.query(tracker.class.Project, { _id: currentSpace }, (res) => {
|
||||
currentProject = res.shift()
|
||||
})
|
||||
|
||||
let resultQuery: DocumentQuery<any> = query
|
||||
@@ -104,7 +112,7 @@
|
||||
|
||||
const lookup: Lookup<Issue> = {
|
||||
assignee: contact.class.Employee,
|
||||
space: tracker.class.Team,
|
||||
space: tracker.class.Project,
|
||||
_id: {
|
||||
subIssues: tracker.class.Issue
|
||||
}
|
||||
@@ -128,7 +136,7 @@
|
||||
let issues: Issue[] = []
|
||||
const lookupIssue: Lookup<Issue> = {
|
||||
status: tracker.class.IssueStatus,
|
||||
project: tracker.class.Project,
|
||||
component: tracker.class.Component,
|
||||
sprint: tracker.class.Sprint,
|
||||
assignee: contact.class.Employee
|
||||
}
|
||||
@@ -167,15 +175,15 @@
|
||||
}
|
||||
)
|
||||
|
||||
const projectsQuery = createQuery()
|
||||
let projects: Project[] = []
|
||||
$: projectsQuery.query(
|
||||
tracker.class.Project,
|
||||
const componentsQuery = createQuery()
|
||||
let components: ComponentType[] = []
|
||||
$: componentsQuery.query(
|
||||
tracker.class.Component,
|
||||
{
|
||||
space: currentSpace
|
||||
},
|
||||
(result) => {
|
||||
projects = result
|
||||
components = result
|
||||
}
|
||||
)
|
||||
|
||||
@@ -202,7 +210,7 @@
|
||||
viewOptions,
|
||||
viewOptionsConfig,
|
||||
statuses,
|
||||
projects,
|
||||
components,
|
||||
sprints,
|
||||
assignee
|
||||
)
|
||||
@@ -215,7 +223,7 @@
|
||||
viewOptions,
|
||||
viewOptionsConfig,
|
||||
statuses,
|
||||
projects,
|
||||
components,
|
||||
sprints,
|
||||
assignee
|
||||
)
|
||||
@@ -228,7 +236,7 @@
|
||||
viewOptions: ViewOptions,
|
||||
viewOptionsModel: ViewOptionModel[] | undefined,
|
||||
statuses: WithLookup<IssueStatus>[],
|
||||
projects: Project[],
|
||||
components: ComponentType[],
|
||||
sprints: Sprint[],
|
||||
assignee: Employee[]
|
||||
) {
|
||||
@@ -251,7 +259,7 @@
|
||||
}
|
||||
}
|
||||
const indexes = new Map(categories.map((p, i) => [p, i]))
|
||||
const res = await mapKanbanCategories(groupByKey, categories, statuses, projects, sprints, assignee)
|
||||
const res = await mapKanbanCategories(groupByKey, categories, statuses, components, sprints, assignee)
|
||||
res.sort((a, b) => {
|
||||
const aIndex = indexes.get(a._id ?? undefined) ?? -1
|
||||
const bIndex = indexes.get(b._id ?? undefined) ?? -1
|
||||
@@ -357,10 +365,10 @@
|
||||
</div>
|
||||
<div class="buttons-group xsmall-gap states-bar">
|
||||
{#if issue && issue.subIssues > 0}
|
||||
<SubIssuesSelector value={issue} {currentTeam} />
|
||||
<SubIssuesSelector value={issue} {currentProject} />
|
||||
{/if}
|
||||
<PriorityEditor value={issue} isEditable={true} kind={'link-bordered'} size={'inline'} justify={'center'} />
|
||||
<ProjectEditor
|
||||
<ComponentEditor
|
||||
value={issue}
|
||||
isEditable={true}
|
||||
kind={'link-bordered'}
|
||||
@@ -369,7 +377,7 @@
|
||||
width={''}
|
||||
bind:onlyIcon={fullFilled[issueId]}
|
||||
/>
|
||||
<EstimationEditor kind={'list'} size={'small'} value={issue} {currentTeam} />
|
||||
<EstimationEditor kind={'list'} size={'small'} value={issue} {currentProject} />
|
||||
<div
|
||||
class="clear-mins"
|
||||
use:tooltip={{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Team, Issue } from '@hcengineering/tracker'
|
||||
import { Project, Issue } from '@hcengineering/tracker'
|
||||
import { Spinner, IconClose, tooltip } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
@@ -25,10 +25,10 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
let team: Team | undefined
|
||||
let project: Project | undefined
|
||||
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: issue.space }, (res) => ([team] = res))
|
||||
$: issueId = team && getIssueId(team, issue)
|
||||
$: spaceQuery.query(tracker.class.Project, { _id: issue.space }, (res) => ([project] = res))
|
||||
$: issueId = project && getIssueId(project, issue)
|
||||
</script>
|
||||
|
||||
<div class="flex-center root">
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
(result) => {
|
||||
documents = result
|
||||
},
|
||||
{ lookup: isIssue ? { space: tracker.class.Team } : {} }
|
||||
{ lookup: isIssue ? { space: tracker.class.Project } : {} }
|
||||
)
|
||||
|
||||
async function handleClick (issue: RelatedDocument) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { AttachedData, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { DraftIssueChild, Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
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'
|
||||
@@ -35,7 +35,7 @@
|
||||
export let width: string | undefined = undefined
|
||||
|
||||
// Extra properties
|
||||
export let issueStatuses: Map<Ref<Team>, WithLookup<IssueStatus>[]> | undefined = undefined
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]> | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import { getFiltredKeys, isCollectionAttr } from '@hcengineering/view-resources/src/utils'
|
||||
import tracker from '../../../plugin'
|
||||
import ProjectEditor from '../../projects/ProjectEditor.svelte'
|
||||
import ComponentEditor from '../../components/ComponentEditor.svelte'
|
||||
import SprintEditor from '../../sprints/SprintEditor.svelte'
|
||||
import AssigneeEditor from '../AssigneeEditor.svelte'
|
||||
import DueDateEditor from '../DueDateEditor.svelte'
|
||||
@@ -75,7 +75,7 @@
|
||||
return res
|
||||
}
|
||||
|
||||
$: updateKeys(['title', 'description', 'priority', 'status', 'number', 'assignee', 'project', 'dueDate', 'sprint'])
|
||||
$: updateKeys(['title', 'description', 'priority', 'status', 'number', 'assignee', 'component', 'dueDate', 'sprint'])
|
||||
|
||||
const employeeAccountQuery = createQuery()
|
||||
const employeeQuery = createQuery()
|
||||
@@ -174,9 +174,9 @@
|
||||
<div class="divider" />
|
||||
|
||||
<span class="label">
|
||||
<Label label={tracker.string.Project} />
|
||||
<Label label={tracker.string.Component} />
|
||||
</span>
|
||||
<ProjectEditor value={issue} />
|
||||
<ComponentEditor value={issue} />
|
||||
|
||||
{#if issue.sprint}
|
||||
<span class="label">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { Account, AttachedData, Doc, generateId, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import presentation, { getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
import { IssueStatus, IssuePriority, Issue, Team, calcRank } from '@hcengineering/tracker'
|
||||
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 tracker from '../../../plugin'
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
export let parentIssue: Issue
|
||||
export let issueStatuses: WithLookup<IssueStatus>[]
|
||||
export let currentTeam: Team
|
||||
export let currentProject: Project
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@@ -52,7 +52,7 @@
|
||||
title: '',
|
||||
description: '',
|
||||
assignee: null,
|
||||
project: null,
|
||||
component: null,
|
||||
number: 0,
|
||||
rank: '',
|
||||
status: '' as Ref<IssueStatus>,
|
||||
@@ -90,10 +90,10 @@
|
||||
}
|
||||
loading = true
|
||||
try {
|
||||
const space = currentTeam._id
|
||||
const space = currentProject._id
|
||||
const lastOne = await client.findOne<Issue>(tracker.class.Issue, {}, { sort: { rank: SortingOrder.Descending } })
|
||||
const incResult = await client.updateDoc(
|
||||
tracker.class.Team,
|
||||
tracker.class.Project,
|
||||
core.space.Space,
|
||||
space,
|
||||
{ $inc: { sequence: 1 } },
|
||||
@@ -105,7 +105,7 @@
|
||||
title: getTitle(newIssue.title),
|
||||
number: (incResult as any).object.sequence,
|
||||
rank: calcRank(lastOne, undefined),
|
||||
project: parentIssue.project,
|
||||
component: parentIssue.component,
|
||||
parents: [{ parentId: parentIssue._id, parentTitle: parentIssue.title }, ...parentIssue.parents]
|
||||
}
|
||||
|
||||
@@ -162,8 +162,8 @@
|
||||
|
||||
$: thisRef && thisRef.scrollIntoView({ behavior: 'smooth' })
|
||||
$: canSave = getTitle(newIssue.title ?? '').length > 0
|
||||
$: if (!newIssue.status && currentTeam?.defaultIssueStatus) {
|
||||
newIssue.status = currentTeam.defaultIssueStatus
|
||||
$: if (!newIssue.status && currentProject?.defaultIssueStatus) {
|
||||
newIssue.status = currentProject.defaultIssueStatus
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
{objectId}
|
||||
refContainer={thisRef}
|
||||
_class={tracker.class.Issue}
|
||||
space={currentTeam._id}
|
||||
space={currentProject._id}
|
||||
alwaysEdit
|
||||
showButtons
|
||||
maxHeight={'20vh'}
|
||||
@@ -208,7 +208,7 @@
|
||||
</div>
|
||||
<div class="mt-4 flex-between">
|
||||
<div class="buttons-group xsmall-gap">
|
||||
<!-- <SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space /> -->
|
||||
<!-- <SpaceSelector _class={tracker.class.Project} label={tracker.string.Project} bind:space /> -->
|
||||
<PriorityEditor
|
||||
value={newIssue}
|
||||
shouldShowLabel
|
||||
@@ -241,7 +241,7 @@
|
||||
labels = labels.filter((it) => it._id !== evt.detail)
|
||||
}}
|
||||
/>
|
||||
<EstimationEditor kind={'no-border'} size={'small'} value={newIssue} {currentTeam} />
|
||||
<EstimationEditor kind={'no-border'} size={'small'} value={newIssue} {currentProject} />
|
||||
</div>
|
||||
<div class="buttons-group small-gap">
|
||||
<Button label={presentation.string.Cancel} size="small" kind="transparent" on:click={close} />
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import presentation, { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
|
||||
import setting, { settingId } from '@hcengineering/setting'
|
||||
import type { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import type { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
EditBox,
|
||||
@@ -55,7 +55,7 @@
|
||||
const client = getClient()
|
||||
|
||||
let issue: WithLookup<Issue> | undefined
|
||||
let currentTeam: Team | undefined
|
||||
let currentProject: Project | undefined
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
let title = ''
|
||||
let description = ''
|
||||
@@ -90,15 +90,15 @@
|
||||
;[issue] = result
|
||||
title = issue.title
|
||||
description = issue.description
|
||||
currentTeam = issue.$lookup?.space
|
||||
currentProject = issue.$lookup?.space
|
||||
},
|
||||
{ lookup: { attachedTo: tracker.class.Issue, space: tracker.class.Team } }
|
||||
{ lookup: { attachedTo: tracker.class.Issue, space: tracker.class.Project } }
|
||||
)
|
||||
|
||||
$: currentTeam &&
|
||||
$: currentProject &&
|
||||
statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ attachedTo: currentTeam._id },
|
||||
{ attachedTo: currentProject._id },
|
||||
(statuses) => (issueStatuses = statuses),
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
@@ -106,7 +106,7 @@
|
||||
}
|
||||
)
|
||||
|
||||
$: issueId = currentTeam && issue && getIssueId(currentTeam, issue)
|
||||
$: 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
|
||||
@@ -206,7 +206,7 @@
|
||||
<div class="popupPanel-body__main-content py-10 clear-mins content">
|
||||
{#if parentIssue}
|
||||
<div class="mb-6">
|
||||
{#if currentTeam && issueStatuses}
|
||||
{#if currentProject && issueStatuses}
|
||||
<SubIssueSelector {issue} />
|
||||
{:else}
|
||||
<Spinner />
|
||||
@@ -233,7 +233,7 @@
|
||||
{:else}
|
||||
{#if parentIssue}
|
||||
<div class="mb-6">
|
||||
{#if currentTeam && issueStatuses}
|
||||
{#if currentProject && issueStatuses}
|
||||
<SubIssueSelector {issue} />
|
||||
{:else}
|
||||
<Spinner />
|
||||
@@ -252,12 +252,12 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
{#key issue._id && currentTeam !== undefined}
|
||||
{#if currentTeam !== undefined && issueStatuses !== undefined}
|
||||
{#key issue._id && currentProject !== undefined}
|
||||
{#if currentProject !== undefined && issueStatuses !== undefined}
|
||||
<SubIssues
|
||||
{issue}
|
||||
issueStatuses={new Map([[currentTeam._id, issueStatuses]])}
|
||||
teams={new Map([[currentTeam?._id, currentTeam]])}
|
||||
issueStatuses={new Map([[currentProject._id, issueStatuses]])}
|
||||
projects={new Map([[currentProject?._id, currentProject]])}
|
||||
/>
|
||||
{/if}
|
||||
{/key}
|
||||
@@ -306,7 +306,7 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="custom-attributes">
|
||||
{#if issue && currentTeam && issueStatuses}
|
||||
{#if issue && currentProject && issueStatuses}
|
||||
<ControlPanel {issue} {issueStatuses} {showAllMixins} />
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Viewlet, ViewOptions } from '@hcengineering/view'
|
||||
import {
|
||||
ActionContext,
|
||||
@@ -33,8 +33,8 @@
|
||||
export let disableHeader = false
|
||||
|
||||
// Extra properties
|
||||
export let teams: Map<Ref<Team>, Team> | undefined
|
||||
export let issueStatuses: Map<Ref<Team>, WithLookup<IssueStatus>[]>
|
||||
export let projects: Map<Ref<Project>, Project> | undefined
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]>
|
||||
|
||||
let list: List
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
documents={issues}
|
||||
{query}
|
||||
flatHeaders={true}
|
||||
props={{ teams, issueStatuses }}
|
||||
props={{ projects, issueStatuses }}
|
||||
{disableHeader}
|
||||
selectedObjectIds={$selectionStore ?? []}
|
||||
on:row-focus={(event) => {
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
SelectPopup,
|
||||
{
|
||||
value: subIssues.map((iss) => {
|
||||
const team = iss.$lookup?.space
|
||||
const project = iss.$lookup?.space
|
||||
const status = iss.$lookup?.status as WithLookup<IssueStatus>
|
||||
const icon = status.$lookup?.category?.icon
|
||||
const color = status.color ?? status.$lookup?.category?.color
|
||||
@@ -70,7 +70,7 @@
|
||||
id: iss._id,
|
||||
icon,
|
||||
isSelected: iss._id === issue._id,
|
||||
...(team !== undefined ? { text: `${getIssueId(team, iss)} ${iss.title}` } : undefined),
|
||||
...(project !== undefined ? { text: `${getIssueId(project, iss)} ${iss.title}` } : undefined),
|
||||
...(color !== undefined ? { iconColor: getPlatformColor(color) } : undefined)
|
||||
}
|
||||
}),
|
||||
@@ -100,7 +100,7 @@
|
||||
{
|
||||
sort: { modifiedOn: SortingOrder.Descending },
|
||||
lookup: {
|
||||
space: tracker.class.Team,
|
||||
space: tracker.class.Project,
|
||||
status: [tracker.class.IssueStatus, { category: tracker.class.IssueStatusCategory }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder, toIdMap, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team, trackerId } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project, trackerId } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
Chevron,
|
||||
@@ -41,8 +41,8 @@
|
||||
import SubIssueList from './SubIssueList.svelte'
|
||||
|
||||
export let issue: Issue
|
||||
export let teams: Map<Ref<Team>, Team>
|
||||
export let issueStatuses: Map<Ref<Team>, WithLookup<IssueStatus>[]>
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]>
|
||||
|
||||
let subIssueEditorRef: HTMLDivElement
|
||||
let isCollapsed = false
|
||||
@@ -57,17 +57,17 @@
|
||||
;[viewlet] = res
|
||||
})
|
||||
|
||||
let _teams = teams
|
||||
let _projects = projects
|
||||
let _issueStatuses = issueStatuses
|
||||
|
||||
const teamsQuery = createQuery()
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
$: if (teams === undefined) {
|
||||
teamsQuery.query(tracker.class.Team, {}, async (result) => {
|
||||
_teams = toIdMap(result)
|
||||
$: if (projects === undefined) {
|
||||
projectsQuery.query(tracker.class.Project, {}, async (result) => {
|
||||
_projects = toIdMap(result)
|
||||
})
|
||||
} else {
|
||||
teamsQuery.unsubscribe()
|
||||
projectsQuery.unsubscribe()
|
||||
}
|
||||
|
||||
const statusesQuery = createQuery()
|
||||
@@ -76,9 +76,9 @@
|
||||
tracker.class.IssueStatus,
|
||||
{},
|
||||
(statuses) => {
|
||||
const st = new Map<Ref<Team>, WithLookup<IssueStatus>[]>()
|
||||
const st = new Map<Ref<Project>, WithLookup<IssueStatus>[]>()
|
||||
for (const s of statuses) {
|
||||
const id = s.attachedTo as Ref<Team>
|
||||
const id = s.attachedTo as Ref<Project>
|
||||
st.set(id, [...(st.get(id) ?? []), s])
|
||||
}
|
||||
_issueStatuses = st
|
||||
@@ -160,30 +160,30 @@
|
||||
<div class="mt-1">
|
||||
{#if issueStatuses}
|
||||
{#if hasSubIssues && viewOptions && viewlet}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
{#if !isCollapsed}
|
||||
{#if !isCollapsed}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<div class="list" class:collapsed={isCollapsed}>
|
||||
<SubIssueList
|
||||
teams={_teams}
|
||||
projects={_projects}
|
||||
{viewlet}
|
||||
{viewOptions}
|
||||
issueStatuses={_issueStatuses}
|
||||
query={{ attachedTo: issue._id }}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</ExpandCollapse>
|
||||
</ExpandCollapse>
|
||||
{/if}
|
||||
{/if}
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
{#if isCreating}
|
||||
{@const team = teams.get(issue.space)}
|
||||
{@const project = projects.get(issue.space)}
|
||||
{@const statuses = issueStatuses.get(issue.space)}
|
||||
{#if team !== undefined && statuses !== undefined}
|
||||
{#if project !== undefined && statuses !== undefined}
|
||||
<div class="pt-4" bind:this={subIssueEditorRef}>
|
||||
<CreateSubIssue
|
||||
parentIssue={issue}
|
||||
issueStatuses={statuses}
|
||||
currentTeam={team}
|
||||
currentProject={project}
|
||||
on:close={() => (isCreating = false)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
ButtonKind,
|
||||
@@ -32,7 +32,7 @@
|
||||
import { subIssueListProvider } from '../../../utils'
|
||||
|
||||
export let value: WithLookup<Issue>
|
||||
export let currentTeam: Team | undefined = undefined
|
||||
export let currentProject: Project | undefined = undefined
|
||||
|
||||
export let kind: ButtonKind = 'link-bordered'
|
||||
export let size: ButtonSize = 'inline'
|
||||
@@ -41,22 +41,22 @@
|
||||
|
||||
let btn: HTMLElement
|
||||
|
||||
$: team = currentTeam
|
||||
$: project = currentProject
|
||||
|
||||
let subIssues: Issue[] = []
|
||||
let countComplate: number = 0
|
||||
|
||||
const teamQuery = createQuery()
|
||||
$: if (currentTeam === undefined) {
|
||||
teamQuery.query(
|
||||
tracker.class.Team,
|
||||
const projectQuery = createQuery()
|
||||
$: if (currentProject === undefined) {
|
||||
projectQuery.query(
|
||||
tracker.class.Project,
|
||||
{
|
||||
_id: value.space
|
||||
},
|
||||
(res) => ([team] = res)
|
||||
(res) => ([project] = res)
|
||||
)
|
||||
} else {
|
||||
teamQuery.unsubscribe()
|
||||
projectQuery.unsubscribe()
|
||||
}
|
||||
const query = createQuery()
|
||||
const statusesQuery = createQuery()
|
||||
@@ -117,7 +117,7 @@
|
||||
SelectPopup,
|
||||
{
|
||||
value: subIssues.map((iss) => {
|
||||
const text = team ? `${getIssueId(team, iss)} ${iss.title}` : iss.title
|
||||
const text = project ? `${getIssueId(project, iss)} ${iss.title}` : iss.title
|
||||
|
||||
return { id: iss._id, text, isSelected: iss._id === value._id, ...getIssueStatusIcon(iss, statuses) }
|
||||
}),
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Doc, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
ButtonKind,
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
export let object: WithLookup<Doc & { related: number }> | undefined
|
||||
export let value: WithLookup<Doc & { related: number }> | undefined
|
||||
export let currentTeam: Team | undefined
|
||||
export let currentProject: Project | undefined
|
||||
|
||||
export let kind: ButtonKind = 'link-bordered'
|
||||
export let size: ButtonSize = 'inline'
|
||||
@@ -103,7 +103,7 @@
|
||||
SelectPopup,
|
||||
{
|
||||
value: subIssues.map((iss) => {
|
||||
const text = currentTeam ? `${getIssueId(currentTeam, iss)} ${iss.title}` : iss.title
|
||||
const text = currentProject ? `${getIssueId(currentProject, iss)} ${iss.title}` : iss.title
|
||||
|
||||
return { id: iss._id, text, isSelected: false, ...getIssueStatusIcon(iss, statuses) }
|
||||
}),
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Doc, DocumentQuery, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Label, Spinner } from '@hcengineering/ui'
|
||||
import { Viewlet, ViewOptions } from '@hcengineering/view'
|
||||
import tracker from '../../../plugin'
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
let subIssues: Issue[] = []
|
||||
|
||||
let teams: Map<Ref<Team>, Team> | undefined
|
||||
let projects: Map<Ref<Project>, Project> | undefined
|
||||
|
||||
$: subIssuesQuery.query(tracker.class.Issue, query, async (result) => (subIssues = result), {
|
||||
sort: { rank: SortingOrder.Ascending },
|
||||
@@ -47,22 +47,22 @@
|
||||
}
|
||||
})
|
||||
|
||||
const teamsQuery = createQuery()
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
$: teamsQuery.query(tracker.class.Team, {}, async (result) => {
|
||||
teams = new Map(result.map((it) => [it._id, it]))
|
||||
$: projectsQuery.query(tracker.class.Project, {}, async (result) => {
|
||||
projects = new Map(result.map((it) => [it._id, it]))
|
||||
})
|
||||
|
||||
let issueStatuses = new Map<Ref<Team>, WithLookup<IssueStatus>[]>()
|
||||
let issueStatuses = new Map<Ref<Project>, WithLookup<IssueStatus>[]>()
|
||||
|
||||
const statusesQuery = createQuery()
|
||||
$: statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{},
|
||||
(statuses) => {
|
||||
const st = new Map<Ref<Team>, WithLookup<IssueStatus>[]>()
|
||||
const st = new Map<Ref<Project>, WithLookup<IssueStatus>[]>()
|
||||
for (const s of statuses) {
|
||||
const id = s.attachedTo as Ref<Team>
|
||||
const id = s.attachedTo as Ref<Project>
|
||||
st.set(id, [...(st.get(id) ?? []), s])
|
||||
}
|
||||
issueStatuses = st
|
||||
@@ -75,8 +75,8 @@
|
||||
</script>
|
||||
|
||||
{#if subIssues !== undefined && viewlet !== undefined}
|
||||
{#if issueStatuses.size > 0 && teams && subIssues.length > 0}
|
||||
<SubIssueList bind:viewOptions {viewlet} issues={subIssues} {teams} {issueStatuses} {disableHeader} />
|
||||
{#if issueStatuses.size > 0 && projects && subIssues.length > 0}
|
||||
<SubIssueList bind:viewOptions {viewlet} issues={subIssues} {projects} {issueStatuses} {disableHeader} />
|
||||
{:else}
|
||||
<div class="antiSection-empty solid flex-col mt-3">
|
||||
<div class="flex-center content-accent-color">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { AttachedData } from '@hcengineering/core'
|
||||
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Team } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { Button, ButtonKind, ButtonSize, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import EditBoxPopup from '@hcengineering/view-resources/src/components/EditBoxPopup.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -31,7 +31,7 @@
|
||||
export let size: ButtonSize = 'large'
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = undefined
|
||||
export let currentTeam: Team | undefined = undefined
|
||||
export let currentProject: Project | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
{#if value}
|
||||
{#if kind === 'list'}
|
||||
<EstimationStatsPresenter {value} on:click={handleestimationEditorOpened} {currentTeam} />
|
||||
<EstimationStatsPresenter {value} on:click={handleestimationEditorOpened} {currentProject} />
|
||||
{:else}
|
||||
<Button
|
||||
showTooltip={isEditable ? { label: tracker.string.Estimation } : undefined}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import presentation, { Card, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Button, EditStyle, eventToHTMLElement, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||
import EditBoxPopup from '@hcengineering/view-resources/src/components/EditBoxPopup.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let currentTeam: Team | undefined
|
||||
let currentProject: Project | undefined
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
|
||||
$: query.query(
|
||||
@@ -50,23 +50,23 @@
|
||||
const r = res.shift()
|
||||
if (r !== undefined) {
|
||||
object = r
|
||||
currentTeam = r.$lookup?.space
|
||||
currentProject = r.$lookup?.space
|
||||
}
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
space: tracker.class.Team
|
||||
space: tracker.class.Project
|
||||
}
|
||||
}
|
||||
)
|
||||
$: defaultTimeReportDay = currentTeam?.defaultTimeReportDay
|
||||
$: defaultTimeReportDay = currentProject?.defaultTimeReportDay
|
||||
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
$: currentTeam &&
|
||||
$: currentProject &&
|
||||
statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ attachedTo: currentTeam._id },
|
||||
{ attachedTo: currentProject._id },
|
||||
(statuses) => (issueStatuses = statuses),
|
||||
{
|
||||
lookup: { category: tracker.class.IssueStatusCategory },
|
||||
@@ -115,7 +115,7 @@
|
||||
)
|
||||
}}
|
||||
>
|
||||
<EstimationStatsPresenter value={object} estimation={_value} {currentTeam} />
|
||||
<EstimationStatsPresenter value={object} estimation={_value} {currentProject} />
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
@@ -124,18 +124,18 @@
|
||||
<IssuePresenter value={object} disableClick />
|
||||
</svelte:fragment>
|
||||
|
||||
{#if currentTeam && issueStatuses}
|
||||
{#if currentProject && issueStatuses}
|
||||
<SubIssuesEstimations
|
||||
issue={object}
|
||||
issueStatuses={new Map([[currentTeam._id, issueStatuses]])}
|
||||
teams={new Map([[currentTeam?._id, currentTeam]])}
|
||||
issueStatuses={new Map([[currentProject._id, issueStatuses]])}
|
||||
projects={new Map([[currentProject?._id, currentProject]])}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if currentTeam}
|
||||
{#if currentProject}
|
||||
<TimeSpendReports
|
||||
issue={object}
|
||||
teams={new Map([[currentTeam?._id, currentTeam]])}
|
||||
projects={new Map([[currentProject?._id, currentProject]])}
|
||||
query={{ attachedTo: { $in: [object._id, ...childIds] } }}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
+3
-3
@@ -15,18 +15,18 @@
|
||||
<script lang="ts">
|
||||
import { AttachedData } from '@hcengineering/core'
|
||||
|
||||
import { Issue, Team } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { floorFractionDigits } from '@hcengineering/ui'
|
||||
import EstimationProgressCircle from './EstimationProgressCircle.svelte'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
|
||||
export let value: Issue | AttachedData<Issue>
|
||||
export let estimation: number | undefined = undefined
|
||||
export let currentTeam: Team | undefined
|
||||
export let currentProject: Project | undefined
|
||||
|
||||
$: _estimation = estimation ?? value.estimation
|
||||
|
||||
$: workDayLength = currentTeam?.workDayLength
|
||||
$: workDayLength = currentProject?.workDayLength
|
||||
$: childReportTime = floorFractionDigits(
|
||||
value.reportedTime + (value.childInfo ?? []).map((it) => it.reportedTime).reduce((a, b) => a + b, 0),
|
||||
3
|
||||
|
||||
+6
-6
@@ -16,7 +16,7 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { AssigneeBox } from '@hcengineering/presentation'
|
||||
import { Issue, Team } from '@hcengineering/tracker'
|
||||
import { Issue, Project } from '@hcengineering/tracker'
|
||||
import { deviceOptionsStore as deviceInfo, getEventPositionElement, ListView, showPopup } from '@hcengineering/ui'
|
||||
import { ContextMenu, FixedColumn, ListSelectionProvider } from '@hcengineering/view-resources'
|
||||
import { getIssueId } from '../../../issues'
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
export let issues: Issue[]
|
||||
|
||||
export let teams: Map<Ref<Team>, Team>
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
|
||||
function showContextMenu (ev: MouseEvent, object: Issue) {
|
||||
showPopup(ContextMenu, { object }, $deviceInfo.isMobile ? 'top' : getEventPositionElement(ev))
|
||||
@@ -38,7 +38,7 @@
|
||||
<ListView count={issues.length} addClass={'step-tb-2-accent'}>
|
||||
<svelte:fragment slot="item" let:item>
|
||||
{@const issue = issues[item]}
|
||||
{@const currentTeam = teams.get(issue.space)}
|
||||
{@const currentProject = projects.get(issue.space)}
|
||||
<div
|
||||
class="{twoRows ? 'flex-col' : 'flex-between'} p-text-2"
|
||||
on:contextmenu|preventDefault={(ev) => showContextMenu(ev, issue)}
|
||||
@@ -51,8 +51,8 @@
|
||||
>
|
||||
<div class="flex-row-center clear-mins gap-2 flex-grow mr-4" class:p-text={twoRows}>
|
||||
<FixedColumn key={'estimation_issue'} justify={'left'} addClass={'fs-bold'}>
|
||||
{#if currentTeam}
|
||||
{getIssueId(currentTeam, issue)}
|
||||
{#if currentProject}
|
||||
{getIssueId(currentProject, issue)}
|
||||
{/if}
|
||||
</FixedColumn>
|
||||
<span class="overflow-label fs-bold caption-color" title={issue.title}>
|
||||
@@ -71,7 +71,7 @@
|
||||
/>
|
||||
</FixedColumn>
|
||||
<FixedColumn key={'estimation'} justify={'left'}>
|
||||
<EstimationEditor value={issue} kind={'list'} {currentTeam} />
|
||||
<EstimationEditor value={issue} kind={'list'} {currentProject} />
|
||||
</FixedColumn>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
+8
-8
@@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import tracker, { Issue, Team } from '@hcengineering/tracker'
|
||||
import tracker, { Issue, Project } from '@hcengineering/tracker'
|
||||
import { ActionIcon, eventToHTMLElement, floorFractionDigits, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||
import ReportsPopup from './ReportsPopup.svelte'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
@@ -27,19 +27,19 @@
|
||||
export let object: Issue
|
||||
export let value: number
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
export let currentTeam: Team | undefined
|
||||
export let currentProject: Project | undefined
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
$: if (currentTeam === undefined) {
|
||||
spaceQuery.query(tracker.class.Team, { _id: object.space }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
$: if (currentProject === undefined) {
|
||||
spaceQuery.query(tracker.class.Project, { _id: object.space }, (res) => {
|
||||
currentProject = res.shift()
|
||||
})
|
||||
} else {
|
||||
spaceQuery.unsubscribe()
|
||||
}
|
||||
|
||||
$: defaultTimeReportDay = currentTeam?.defaultTimeReportDay
|
||||
$: workDayLength = currentTeam?.workDayLength
|
||||
$: defaultTimeReportDay = currentProject?.defaultTimeReportDay
|
||||
$: workDayLength = currentProject?.workDayLength
|
||||
|
||||
function addTimeReport (event: MouseEvent): void {
|
||||
showPopup(
|
||||
@@ -51,7 +51,7 @@
|
||||
issueClass: object._class,
|
||||
space: object.space,
|
||||
assignee: object.assignee,
|
||||
currentTeam
|
||||
currentProject
|
||||
},
|
||||
eventToHTMLElement(event)
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { FindOptions } from '@hcengineering/core'
|
||||
import presentation, { Card } from '@hcengineering/presentation'
|
||||
import { Issue, Team, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { Issue, Project, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { Button, eventToHTMLElement, IconAdd, Scroller, showPopup, tableSP } from '@hcengineering/ui'
|
||||
import { TableBrowser } from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
@@ -24,9 +24,9 @@
|
||||
import ParentNamesPresenter from '../ParentNamesPresenter.svelte'
|
||||
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
||||
export let issue: Issue
|
||||
export let currentTeam: Team | undefined
|
||||
export let currentProject: Project | undefined
|
||||
|
||||
$: defaultTimeReportDay = currentTeam?.defaultTimeReportDay
|
||||
$: defaultTimeReportDay = currentProject?.defaultTimeReportDay
|
||||
|
||||
export function canClose (): boolean {
|
||||
return true
|
||||
|
||||
+4
-4
@@ -15,15 +15,15 @@
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
|
||||
import { Spinner } from '@hcengineering/ui'
|
||||
import Expandable from '@hcengineering/ui/src/components/Expandable.svelte'
|
||||
import tracker from '../../../plugin'
|
||||
import EstimationSubIssueList from './EstimationSubIssueList.svelte'
|
||||
|
||||
export let issue: Issue
|
||||
export let teams: Map<Ref<Team>, Team>
|
||||
export let issueStatuses: Map<Ref<Team>, WithLookup<IssueStatus>[]>
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
export let issueStatuses: Map<Ref<Project>, WithLookup<IssueStatus>[]>
|
||||
|
||||
const subIssuesQuery = createQuery()
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
{#if hasSubIssues}
|
||||
<Expandable label={tracker.string.ChildEstimation} contentColor bordered>
|
||||
<svelte:fragment slot="title">: <span class="caption-color">{total}</span></svelte:fragment>
|
||||
<EstimationSubIssueList issues={subIssues} {teams} />
|
||||
<EstimationSubIssueList issues={subIssues} {projects} />
|
||||
</Expandable>
|
||||
{/if}
|
||||
{:else}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Team, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { Issue, Project, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
||||
|
||||
export let value: WithLookup<TimeSpendReport>
|
||||
export let currentTeam: Team | undefined
|
||||
export let currentProject: Project | undefined
|
||||
const client = getClient()
|
||||
|
||||
$: issue = value.$lookup?.attachedTo
|
||||
@@ -30,8 +30,8 @@
|
||||
issue = r as Issue
|
||||
})
|
||||
}
|
||||
$: workDayLength = currentTeam?.workDayLength
|
||||
$: defaultTimeReportDay = currentTeam?.defaultTimeReportDay
|
||||
$: workDayLength = currentProject?.workDayLength
|
||||
$: defaultTimeReportDay = currentProject?.defaultTimeReportDay
|
||||
|
||||
function editSpendReport (event: MouseEvent): void {
|
||||
showPopup(
|
||||
|
||||
@@ -15,21 +15,21 @@
|
||||
<script lang="ts">
|
||||
import { DocumentQuery, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, Team, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { Issue, Project, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { Expandable, floorFractionDigits, Label, Spinner } from '@hcengineering/ui'
|
||||
import tracker from '../../../plugin'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
import TimeSpendReportsList from './TimeSpendReportsList.svelte'
|
||||
|
||||
export let issue: Issue
|
||||
export let teams: Map<Ref<Team>, Team>
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
export let query: DocumentQuery<TimeSpendReport>
|
||||
|
||||
const subIssuesQuery = createQuery()
|
||||
|
||||
let reports: TimeSpendReport[] | undefined
|
||||
|
||||
$: workDayLength = teams.get(issue.space)?.workDayLength
|
||||
$: workDayLength = projects.get(issue.space)?.workDayLength
|
||||
$: subIssuesQuery.query(tracker.class.TimeSpendReport, query, async (result) => (reports = result), {
|
||||
sort: { modifiedOn: SortingOrder.Descending },
|
||||
lookup: {
|
||||
@@ -51,7 +51,7 @@
|
||||
<span class="caption-color"><TimePresenter value={total} {workDayLength} /></span>
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
<TimeSpendReportsList {reports} {teams} />
|
||||
<TimeSpendReportsList {reports} {projects} />
|
||||
</Expandable>
|
||||
{:else}
|
||||
<div class="flex-center">
|
||||
|
||||
+8
-8
@@ -16,7 +16,7 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import UserBox from '@hcengineering/presentation/src/components/UserBox.svelte'
|
||||
import { Team, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { Project, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import {
|
||||
deviceOptionsStore as deviceInfo,
|
||||
eventToHTMLElement,
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
export let reports: WithLookup<TimeSpendReport>[]
|
||||
|
||||
export let teams: Map<Ref<Team>, Team>
|
||||
export let projects: Map<Ref<Project>, Project>
|
||||
|
||||
function showContextMenu (ev: MouseEvent, object: TimeSpendReport) {
|
||||
showPopup(ContextMenu, { object }, getEventPositionElement(ev))
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
const listProvider = new ListSelectionProvider(() => {})
|
||||
|
||||
const toTeamId = (ref: Ref<Space>) => ref as Ref<Team>
|
||||
const toProjectId = (ref: Ref<Space>) => ref as Ref<Project>
|
||||
|
||||
function editSpendReport (
|
||||
event: MouseEvent,
|
||||
@@ -68,7 +68,7 @@
|
||||
<ListView count={reports.length} addClass={'step-tb-2-accent'}>
|
||||
<svelte:fragment slot="item" let:item>
|
||||
{@const report = reports[item]}
|
||||
{@const currentTeam = teams.get(toTeamId(report.space))}
|
||||
{@const currentProject = projects.get(toProjectId(report.space))}
|
||||
<div
|
||||
class="{twoRows ? 'flex-col' : 'flex-between'} p-text-2"
|
||||
on:contextmenu|preventDefault={(ev) => showContextMenu(ev, report)}
|
||||
@@ -78,12 +78,12 @@
|
||||
on:focus={() => {
|
||||
listProvider.updateFocus(report)
|
||||
}}
|
||||
on:click={(evt) => editSpendReport(evt, report, currentTeam?.defaultTimeReportDay)}
|
||||
on:click={(evt) => editSpendReport(evt, report, currentProject?.defaultTimeReportDay)}
|
||||
>
|
||||
<div class="flex-row-center clear-mins gap-2 flex-grow mr-4" class:p-text={twoRows}>
|
||||
<FixedColumn key={'tmiespend_issue'} justify={'left'} addClass={'fs-bold'}>
|
||||
{#if currentTeam && report.$lookup?.attachedTo}
|
||||
{getIssueId(currentTeam, report.$lookup?.attachedTo)}
|
||||
{#if currentProject && report.$lookup?.attachedTo}
|
||||
{getIssueId(currentProject, report.$lookup?.attachedTo)}
|
||||
{/if}
|
||||
</FixedColumn>
|
||||
{#if report.$lookup?.attachedTo?.title}
|
||||
@@ -104,7 +104,7 @@
|
||||
/>
|
||||
</FixedColumn>
|
||||
<FixedColumn key={'timespend_reported'} justify={'center'}>
|
||||
<TimePresenter value={report.value} workDayLength={currentTeam?.workDayLength} />
|
||||
<TimePresenter value={report.value} workDayLength={currentProject?.workDayLength} />
|
||||
</FixedColumn>
|
||||
<FixedColumn key={'timespend_date'} justify={'left'}>
|
||||
<DatePresenter value={report.date} />
|
||||
|
||||
+28
-29
@@ -19,7 +19,7 @@
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
import presentation, { AssigneeBox, Card, getClient } from '@hcengineering/presentation'
|
||||
import { StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { genRanks, IssueStatus, Team, TimeReportDayType, WorkDayLength } from '@hcengineering/tracker'
|
||||
import { genRanks, IssueStatus, Project, TimeReportDayType, WorkDayLength } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
DropdownIntlItem,
|
||||
@@ -33,19 +33,19 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import TimeReportDayDropdown from '../issues/timereport/TimeReportDayDropdown.svelte'
|
||||
import TeamIconChooser from './TeamIconChooser.svelte'
|
||||
import ProjectIconChooser from './ProjectIconChooser.svelte'
|
||||
|
||||
export let team: Team | undefined = undefined
|
||||
export let project: Project | undefined = undefined
|
||||
|
||||
let name: string = team?.name ?? ''
|
||||
let description: string = team?.description ?? ''
|
||||
let isPrivate: boolean = team?.private ?? false
|
||||
let icon: Asset | undefined = team?.icon ?? undefined
|
||||
let name: string = project?.name ?? ''
|
||||
let description: string = project?.description ?? ''
|
||||
let isPrivate: boolean = project?.private ?? false
|
||||
let icon: Asset | undefined = project?.icon ?? undefined
|
||||
let selectedWorkDayType: TimeReportDayType | undefined =
|
||||
team?.defaultTimeReportDay ?? TimeReportDayType.PreviousWorkDay
|
||||
let selectedWorkDayLength: WorkDayLength | undefined = team?.workDayLength ?? WorkDayLength.EIGHT_HOURS
|
||||
project?.defaultTimeReportDay ?? TimeReportDayType.PreviousWorkDay
|
||||
let selectedWorkDayLength: WorkDayLength | undefined = project?.workDayLength ?? WorkDayLength.EIGHT_HOURS
|
||||
let defaultAssignee: Ref<Employee> | null | undefined = null
|
||||
let members: Ref<Account>[] = team?.members ?? [getCurrentAccount()._id]
|
||||
let members: Ref<Account>[] = project?.members ?? [getCurrentAccount()._id]
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@@ -60,17 +60,17 @@
|
||||
}
|
||||
]
|
||||
|
||||
$: isNew = !team
|
||||
$: isNew = !project
|
||||
|
||||
async function handleSave () {
|
||||
isNew ? createTeam() : updateTeam()
|
||||
isNew ? createProject() : updateProject()
|
||||
}
|
||||
|
||||
let identifier: string = 'TSK'
|
||||
|
||||
const defaultStatusId: Ref<IssueStatus> = generateId()
|
||||
|
||||
function getTeamData () {
|
||||
function getProjectData () {
|
||||
return {
|
||||
name,
|
||||
description,
|
||||
@@ -88,19 +88,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTeam () {
|
||||
const { sequence, issueStatuses, defaultIssueStatus, identifier, ...teamData } = getTeamData()
|
||||
// update team doc
|
||||
await client.update(team!, teamData)
|
||||
async function updateProject () {
|
||||
const { sequence, issueStatuses, defaultIssueStatus, identifier, ...projectData } = getProjectData()
|
||||
await client.update(project!, projectData)
|
||||
}
|
||||
|
||||
async function createTeam () {
|
||||
const id = await client.createDoc(tracker.class.Team, core.space.Space, getTeamData())
|
||||
await createTeamIssueStatuses(id, defaultStatusId)
|
||||
async function createProject () {
|
||||
const id = await client.createDoc(tracker.class.Project, core.space.Space, getProjectData())
|
||||
await createProjectIssueStatuses(id, defaultStatusId)
|
||||
}
|
||||
|
||||
async function createTeamIssueStatuses (
|
||||
teamId: Ref<Team>,
|
||||
async function createProjectIssueStatuses (
|
||||
projectId: Ref<Project>,
|
||||
defaultStatusId: Ref<IssueStatus>,
|
||||
defaultCategoryId = tracker.issueStatusCategory.Backlog
|
||||
): Promise<void> {
|
||||
@@ -117,9 +116,9 @@
|
||||
|
||||
await client.addCollection(
|
||||
tracker.class.IssueStatus,
|
||||
teamId,
|
||||
teamId,
|
||||
tracker.class.Team,
|
||||
projectId,
|
||||
projectId,
|
||||
tracker.class.Project,
|
||||
'issueStatuses',
|
||||
{ name: defaultStatusName, category, rank },
|
||||
category === defaultCategoryId ? defaultStatusId : undefined
|
||||
@@ -128,7 +127,7 @@
|
||||
}
|
||||
|
||||
function chooseIcon (ev: MouseEvent) {
|
||||
showPopup(TeamIconChooser, { icon }, eventToHTMLElement(ev), (result) => {
|
||||
showPopup(ProjectIconChooser, { icon }, eventToHTMLElement(ev), (result) => {
|
||||
if (result !== undefined && result !== null) {
|
||||
icon = result
|
||||
}
|
||||
@@ -137,7 +136,7 @@
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={isNew ? tracker.string.NewTeam : tracker.string.EditTeam}
|
||||
label={isNew ? tracker.string.NewProject : tracker.string.EditProject}
|
||||
okLabel={isNew ? presentation.string.Create : presentation.string.Save}
|
||||
okAction={handleSave}
|
||||
canSave={name.length > 0 && !!selectedWorkDayType && !!selectedWorkDayLength}
|
||||
@@ -148,7 +147,7 @@
|
||||
<div class="flex-row-center flex-between">
|
||||
<EditBox
|
||||
bind:value={name}
|
||||
placeholder={tracker.string.TeamTitlePlaceholder}
|
||||
placeholder={tracker.string.ProjectTitlePlaceholder}
|
||||
kind={'large-style'}
|
||||
focus
|
||||
on:input={() => {
|
||||
@@ -158,7 +157,7 @@
|
||||
<EditBox
|
||||
bind:value={identifier}
|
||||
disabled={!isNew}
|
||||
placeholder={tracker.string.TeamIdentifierPlaceholder}
|
||||
placeholder={tracker.string.ProjectIdentifierPlaceholder}
|
||||
kind={'large-style'}
|
||||
/>
|
||||
</div>
|
||||
@@ -13,43 +13,31 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import { getCurrentLocation, Icon, navigate, tooltip } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { NavLink } from '@hcengineering/ui'
|
||||
import { SpacesNavModel } from '@hcengineering/workbench'
|
||||
import { SpecialElement } from '@hcengineering/workbench-resources'
|
||||
import { TreeNode } from '@hcengineering/view-resources'
|
||||
|
||||
export let value: WithLookup<Project>
|
||||
export let withIcon = false
|
||||
export let onClick: () => void | undefined
|
||||
export let isInteractive = true
|
||||
|
||||
function navigateToProject () {
|
||||
if (!isInteractive) {
|
||||
return
|
||||
}
|
||||
if (onClick) {
|
||||
onClick()
|
||||
}
|
||||
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[4] = 'projects'
|
||||
loc.path[5] = value._id
|
||||
loc.path.length = 6
|
||||
loc.fragment = undefined
|
||||
navigate(loc)
|
||||
}
|
||||
export let space: Project
|
||||
export let model: SpacesNavModel
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
export let currentSpecial: string | undefined
|
||||
export let getActions: Function
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex" on:click={navigateToProject}>
|
||||
{#if withIcon}
|
||||
<div class="mr-2" use:tooltip={{ label: tracker.string.Project }}>
|
||||
<Icon icon={tracker.icon.Projects} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<span title={value.label} class="fs-bold cursor-pointer caption-color overflow-label clear-mins">
|
||||
{value.label}
|
||||
</span>
|
||||
</div>
|
||||
{#if model.specials}
|
||||
<TreeNode icon={space?.icon ?? model.icon} title={space.name} indent={'ml-2'} actions={() => getActions(space)}>
|
||||
{#each model.specials as special}
|
||||
<NavLink space={space._id} special={special.id}>
|
||||
<SpecialElement
|
||||
indent={'ml-4'}
|
||||
label={special.label}
|
||||
icon={special.icon}
|
||||
selected={currentSpace === space._id && special.id === currentSpecial}
|
||||
/>
|
||||
</NavLink>
|
||||
{/each}
|
||||
</TreeNode>
|
||||
{/if}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Project } from '@hcengineering/tracker'
|
||||
import ProjectStatusPresenter from './ProjectStatusPresenter.svelte'
|
||||
|
||||
export let object: Project
|
||||
</script>
|
||||
|
||||
<ProjectStatusPresenter value={object} shouldShowLabel />
|
||||
@@ -16,13 +16,13 @@
|
||||
import { Data, DateRangeMode, generateId, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { Card, getClient, SpaceSelector, UserBoxList } from '@hcengineering/presentation'
|
||||
import { Scrum, Team } from '@hcengineering/tracker'
|
||||
import { Scrum, Project } from '@hcengineering/tracker'
|
||||
import { DateRangePresenter, EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { StyledTextArea } from '@hcengineering/text-editor'
|
||||
|
||||
export let space: Ref<Team>
|
||||
export let space: Ref<Project>
|
||||
|
||||
const objectId: Ref<Scrum> = generateId()
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -66,7 +66,7 @@
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space />
|
||||
<SpaceSelector _class={tracker.class.Project} label={tracker.string.Project} bind:space />
|
||||
</svelte:fragment>
|
||||
<EditBox bind:value={object.title} placeholder={tracker.string.ScrumTitlePlaceholder} kind={'large-style'} focus />
|
||||
<StyledTextArea
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
const TRACKED_OBJECTS = [
|
||||
tracker.class.Issue,
|
||||
tracker.class.IssueTemplate,
|
||||
tracker.class.Project,
|
||||
tracker.class.Component,
|
||||
tracker.class.Sprint
|
||||
] as const
|
||||
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Scrum, ScrumRecord, Team } from '@hcengineering/tracker'
|
||||
import { Scrum, ScrumRecord, Project } from '@hcengineering/tracker'
|
||||
import { closePopup, closeTooltip, location } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import ScrumRecordsView from './ScrumRecordsView.svelte'
|
||||
import ScrumsView from './ScrumsView.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
|
||||
let scrumId: Ref<Scrum> | undefined
|
||||
let scrum: Scrum | undefined
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { ScrumRecord, Sprint, Team } from '@hcengineering/tracker'
|
||||
import { ScrumRecord, Sprint, Project } from '@hcengineering/tracker'
|
||||
import { Button, Icon, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||
import { ActionContext, List } from '@hcengineering/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
@@ -24,7 +24,7 @@
|
||||
import ScrumDatePresenter from './ScrumDatePresenter.svelte'
|
||||
import ScrumPresenter from './ScrumPresenter.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
export let activeScrumRecord: ScrumRecord | undefined
|
||||
|
||||
const showCreateDialog = async () => {
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
import { Data, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { Card, EmployeeBox, getClient, SpaceSelector, UserBoxList } from '@hcengineering/presentation'
|
||||
import { Project, Sprint, SprintStatus, Team } from '@hcengineering/tracker'
|
||||
import { Component, Sprint, SprintStatus, Project } from '@hcengineering/tracker'
|
||||
import ui, { DatePresenter, EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import ProjectSelector from '../ProjectSelector.svelte'
|
||||
import ComponentSelector from '../ComponentSelector.svelte'
|
||||
import SprintStatusSelector from './SprintStatusSelector.svelte'
|
||||
import { StyledTextArea } from '@hcengineering/text-editor'
|
||||
|
||||
export let space: Ref<Team>
|
||||
export let space: Ref<Project>
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
await client.createDoc(tracker.class.Sprint, space, object)
|
||||
}
|
||||
|
||||
const handleProjectStatusChanged = (newSprintStatus: SprintStatus | undefined) => {
|
||||
const handleComponentStatusChanged = (newSprintStatus: SprintStatus | undefined) => {
|
||||
if (newSprintStatus === undefined) {
|
||||
return
|
||||
}
|
||||
@@ -53,12 +53,12 @@
|
||||
object.status = newSprintStatus
|
||||
}
|
||||
|
||||
const handleProjectIdChanged = async (projectId: Ref<Project> | null | undefined) => {
|
||||
if (projectId === undefined) {
|
||||
const handleComponentIdChanged = async (componentId: Ref<Component> | null | undefined) => {
|
||||
if (componentId === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
object.project = projectId ?? undefined
|
||||
object.component = componentId ?? undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -70,17 +70,17 @@
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector _class={tracker.class.Team} label={tracker.string.Team} bind:space />
|
||||
<SpaceSelector _class={tracker.class.Project} label={tracker.string.Project} bind:space />
|
||||
</svelte:fragment>
|
||||
<EditBox bind:value={object.label} placeholder={tracker.string.SprintNamePlaceholder} kind={'large-style'} focus />
|
||||
<StyledTextArea
|
||||
bind:content={object.description}
|
||||
placeholder={tracker.string.ProjectDescriptionPlaceholder}
|
||||
placeholder={tracker.string.ComponentDescriptionPlaceholder}
|
||||
emphasized
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
<SprintStatusSelector selectedSprintStatus={object.status} onSprintStatusChange={handleProjectStatusChanged} />
|
||||
<ProjectSelector value={object.project} onChange={handleProjectIdChanged} />
|
||||
<SprintStatusSelector selectedSprintStatus={object.status} onSprintStatusChange={handleComponentStatusChanged} />
|
||||
<ComponentSelector value={object.component} onChange={handleComponentIdChanged} />
|
||||
<EmployeeBox
|
||||
label={tracker.string.SprintLead}
|
||||
placeholder={tracker.string.AssignTo}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
export let search: string = ''
|
||||
export let mode: SprintViewMode = 'all'
|
||||
|
||||
const space = typeof query.space === 'string' ? query.space : tracker.team.DefaultTeam
|
||||
const space = typeof query.space === 'string' ? query.space : tracker.project.DefaultProject
|
||||
const showCreateDialog = async () => {
|
||||
showPopup(NewSprint, { space, targetElement: null }, 'top')
|
||||
}
|
||||
@@ -105,7 +105,7 @@
|
||||
<div class="fs-title flex-between header">
|
||||
<div class="flex-row-center">
|
||||
<Label {label} />
|
||||
<div class="projectTitle">
|
||||
<div class="title">
|
||||
› <Label label={title} />
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
@@ -184,7 +184,7 @@
|
||||
padding: 0.5rem 0.75rem 0.5rem 2.25rem;
|
||||
}
|
||||
|
||||
.projectTitle {
|
||||
.title {
|
||||
display: flex;
|
||||
margin-left: 0.25rem;
|
||||
color: var(--content-color);
|
||||
|
||||
+13
-12
@@ -14,18 +14,18 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Sprint, Project } from '@hcengineering/tracker'
|
||||
import { Sprint, Component } from '@hcengineering/tracker'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ButtonKind, ButtonShape, ButtonSize, tooltip } from '@hcengineering/ui'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import tracker from '../../plugin'
|
||||
import ProjectSelector from '../ProjectSelector.svelte'
|
||||
import { activeProject } from '../../issues'
|
||||
import ComponentSelector from '../ComponentSelector.svelte'
|
||||
import { activeComponent } from '../../issues'
|
||||
|
||||
export let value: Sprint
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = true
|
||||
export let popupPlaceholder: IntlString = tracker.string.MoveToProject
|
||||
export let popupPlaceholder: IntlString = tracker.string.MoveToComponent
|
||||
export let shouldShowPlaceholder = true
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let size: ButtonSize = 'large'
|
||||
@@ -38,21 +38,21 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const handleProjectIdChanged = async (newProjectId: Ref<Project> | null | undefined) => {
|
||||
if (!isEditable || newProjectId === undefined || value.project === newProjectId) {
|
||||
const handleComponentIdChanged = async (newComponentId: Ref<Component> | null | undefined) => {
|
||||
if (!isEditable || newComponentId === undefined || value.component === newComponentId) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.update(value, { project: newProjectId ?? undefined })
|
||||
await client.update(value, { component: newComponentId ?? undefined })
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if (value.project && value.project !== $activeProject && groupBy !== 'project') || shouldShowPlaceholder}
|
||||
{#if (value.component && value.component !== $activeComponent && groupBy !== 'component') || shouldShowPlaceholder}
|
||||
<div
|
||||
class="clear-mins"
|
||||
use:tooltip={{ label: value.project ? tracker.string.MoveToProject : tracker.string.AddToProject }}
|
||||
use:tooltip={{ label: value.component ? tracker.string.MoveToComponent : tracker.string.AddToComponent }}
|
||||
>
|
||||
<ProjectSelector
|
||||
<ComponentSelector
|
||||
{kind}
|
||||
{size}
|
||||
{shape}
|
||||
@@ -63,8 +63,9 @@
|
||||
{popupPlaceholder}
|
||||
{onlyIcon}
|
||||
{enlargedText}
|
||||
value={value.project}
|
||||
onChange={handleProjectIdChanged}
|
||||
value={value.component}
|
||||
onChange={handleComponentIdChanged}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
AddToComponent
|
||||
@@ -16,7 +16,7 @@
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Issue, IssueTemplate, Sprint, Team } from '@hcengineering/tracker'
|
||||
import { Issue, IssueTemplate, Sprint, Project } from '@hcengineering/tracker'
|
||||
import {
|
||||
ButtonKind,
|
||||
ButtonShape,
|
||||
@@ -48,11 +48,11 @@
|
||||
const client = getClient()
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
let currentTeam: Team | undefined
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: value.space }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
let currentProject: Project | undefined
|
||||
$: spaceQuery.query(tracker.class.Project, { _id: value.space }, (res) => {
|
||||
currentProject = res.shift()
|
||||
})
|
||||
$: workDayLength = currentTeam?.workDayLength
|
||||
$: workDayLength = currentProject?.workDayLength
|
||||
|
||||
const handleSprintIdChanged = async (newSprintId: Ref<Sprint> | null | undefined) => {
|
||||
if (!isEditable || newSprintId === undefined || value.sprint === newSprintId) {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { getObjectPresenter } from '@hcengineering/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
import LeadPopup from '../projects/LeadPopup.svelte'
|
||||
import LeadPopup from '../components/LeadPopup.svelte'
|
||||
|
||||
export let value: Employee | null
|
||||
export let size: IconSize = 'x-small'
|
||||
@@ -70,7 +70,7 @@
|
||||
active: true
|
||||
},
|
||||
allowDeselect: true,
|
||||
placeholder: tracker.string.ProjectLeadSearchPlaceholder
|
||||
placeholder: tracker.string.ComponentLeadSearchPlaceholder
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
handleLeadChanged
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Sprint, Team } from '@hcengineering/tracker'
|
||||
import { Sprint, Project } from '@hcengineering/tracker'
|
||||
import { ButtonKind, DatePresenter, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { getDayOfSprint } from '../../utils'
|
||||
@@ -26,12 +26,12 @@
|
||||
export let kind: ButtonKind = 'link'
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
let currentTeam: Team | undefined
|
||||
let currentProject: Project | undefined
|
||||
$: sprint &&
|
||||
spaceQuery.query(tracker.class.Team, { _id: sprint.space }, (res) => {
|
||||
;[currentTeam] = res
|
||||
spaceQuery.query(tracker.class.Project, { _id: sprint.space }, (res) => {
|
||||
;[currentProject] = res
|
||||
})
|
||||
$: workDayLength = currentTeam?.workDayLength
|
||||
$: workDayLength = currentProject?.workDayLength
|
||||
|
||||
const sprintQuery = createQuery()
|
||||
let sprint: Sprint | undefined
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel, IntlString, translate } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Project, Sprint } from '@hcengineering/tracker'
|
||||
import { Component, Sprint } from '@hcengineering/tracker'
|
||||
import type { ButtonKind, ButtonSize, LabelAndProps } from '@hcengineering/ui'
|
||||
import { Button, ButtonShape, eventToHTMLElement, SelectPopup, showPopup, Label } from '@hcengineering/ui'
|
||||
import tracker from '../../plugin'
|
||||
@@ -35,7 +35,7 @@
|
||||
export let onlyIcon: boolean = false
|
||||
export let enlargedText = false
|
||||
|
||||
export let useProject: Ref<Project> | undefined = undefined
|
||||
export let useComponent: Ref<Component> | undefined = undefined
|
||||
export let showTooltip: LabelAndProps | undefined = undefined
|
||||
|
||||
let selectedSprint: Sprint | undefined
|
||||
@@ -45,7 +45,7 @@
|
||||
let rawSprints: Sprint[] = []
|
||||
query.query(
|
||||
tracker.class.Sprint,
|
||||
useProject ? { project: useProject } : {},
|
||||
useComponent ? { component: useComponent } : {},
|
||||
(res) => {
|
||||
rawSprints = res
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const handleProjectStatusChanged = async (newStatus: SprintStatus | undefined) => {
|
||||
const handleComponentStatusChanged = async (newStatus: SprintStatus | undefined) => {
|
||||
if (!isEditable || newStatus === undefined || value.status === newStatus) {
|
||||
return
|
||||
}
|
||||
@@ -49,6 +49,6 @@
|
||||
{shouldShowLabel}
|
||||
showTooltip={isEditable ? { label: tracker.string.SetStatus } : undefined}
|
||||
selectedSprintStatus={value.status}
|
||||
onSprintStatusChange={handleProjectStatusChanged}
|
||||
onSprintStatusChange={handleComponentStatusChanged}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Sprint, Team } from '@hcengineering/tracker'
|
||||
import { Sprint, Project } from '@hcengineering/tracker'
|
||||
import { closePopup, closeTooltip, getCurrentLocation, location, navigate } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
@@ -24,7 +24,7 @@
|
||||
import EditSprint from './EditSprint.svelte'
|
||||
import SprintBrowser from './SprintBrowser.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
export let label: IntlString = tracker.string.Sprints
|
||||
export let search: string = ''
|
||||
export let mode: SprintViewMode = 'all'
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<!--
|
||||
// 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 { Ref, Space } from '@hcengineering/core'
|
||||
import { Team } from '@hcengineering/tracker'
|
||||
import { NavLink } from '@hcengineering/ui'
|
||||
import { SpacesNavModel } from '@hcengineering/workbench'
|
||||
import { SpecialElement } from '@hcengineering/workbench-resources'
|
||||
import { TreeNode } from '@hcengineering/view-resources'
|
||||
|
||||
export let space: Team
|
||||
export let model: SpacesNavModel
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
export let currentSpecial: string | undefined
|
||||
export let getActions: Function
|
||||
</script>
|
||||
|
||||
{#if model.specials}
|
||||
<TreeNode icon={space?.icon ?? model.icon} title={space.name} indent={'ml-2'} actions={() => getActions(space)}>
|
||||
{#each model.specials as special}
|
||||
<NavLink space={space._id} special={special.id}>
|
||||
<SpecialElement
|
||||
indent={'ml-4'}
|
||||
label={special.label}
|
||||
icon={special.icon}
|
||||
selected={currentSpace === space._id && special.id === currentSpecial}
|
||||
/>
|
||||
</NavLink>
|
||||
{/each}
|
||||
</TreeNode>
|
||||
{/if}
|
||||
@@ -18,22 +18,22 @@
|
||||
import { Card, getClient, KeyedAttribute, SpaceSelector } from '@hcengineering/presentation'
|
||||
import tags, { TagElement } from '@hcengineering/tags'
|
||||
import { StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { IssuePriority, IssueTemplate, Project, Sprint, Team } from '@hcengineering/tracker'
|
||||
import { IssuePriority, IssueTemplate, Component as ComponentType, Sprint, Project } from '@hcengineering/tracker'
|
||||
import { Component, EditBox, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { activeProject, activeSprint } from '../../issues'
|
||||
import { activeComponent, activeSprint } from '../../issues'
|
||||
import tracker from '../../plugin'
|
||||
import AssigneeEditor from '../issues/AssigneeEditor.svelte'
|
||||
import PriorityEditor from '../issues/PriorityEditor.svelte'
|
||||
import ProjectSelector from '../ProjectSelector.svelte'
|
||||
import ComponentSelector from '../ComponentSelector.svelte'
|
||||
import SprintSelector from '../sprints/SprintSelector.svelte'
|
||||
import EstimationEditor from './EstimationEditor.svelte'
|
||||
import SubIssueTemplates from './IssueTemplateChilds.svelte'
|
||||
|
||||
export let space: Ref<Team>
|
||||
export let space: Ref<Project>
|
||||
export let priority: IssuePriority = IssuePriority.NoPriority
|
||||
export let assignee: Ref<Employee> | null = null
|
||||
export let project: Ref<Project> | null = $activeProject ?? null
|
||||
export let component: Ref<ComponentType> | null = $activeComponent ?? null
|
||||
export let sprint: Ref<Sprint> | null = $activeSprint ?? null
|
||||
export let relatedTo: Doc | undefined
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
title: '',
|
||||
description: '',
|
||||
assignee,
|
||||
project,
|
||||
component,
|
||||
sprint,
|
||||
priority,
|
||||
estimation: 0,
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
|
||||
$: _space = space
|
||||
let spaceRef: Team | undefined
|
||||
let spaceRef: Project | undefined
|
||||
|
||||
$: canSave = getTitle(object.title ?? '').length > 0
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
title: getTitle(object.title),
|
||||
description: object.description,
|
||||
assignee: object.assignee,
|
||||
project: object.project,
|
||||
component: object.component,
|
||||
sprint: object.sprint,
|
||||
priority: object.priority,
|
||||
estimation: object.estimation,
|
||||
@@ -104,12 +104,12 @@
|
||||
objectId = generateId()
|
||||
}
|
||||
|
||||
const handleProjectIdChanged = (projectId: Ref<Project> | null | undefined) => {
|
||||
if (projectId === undefined) {
|
||||
const handleComponentIdChanged = (componentId: Ref<ComponentType> | null | undefined) => {
|
||||
if (componentId === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
object = { ...object, project: projectId }
|
||||
object = { ...object, component: componentId }
|
||||
}
|
||||
|
||||
const handleSprintIdChanged = (sprintId: Ref<Sprint> | null | undefined) => {
|
||||
@@ -137,8 +137,8 @@
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<SpaceSelector
|
||||
_class={tracker.class.Team}
|
||||
label={tracker.string.Team}
|
||||
_class={tracker.class.Project}
|
||||
label={tracker.string.Project}
|
||||
bind:space={_space}
|
||||
on:space={(evt) => {
|
||||
spaceRef = evt.detail
|
||||
@@ -160,9 +160,9 @@
|
||||
/>
|
||||
<SubIssueTemplates
|
||||
bind:children={object.children}
|
||||
project={object.project}
|
||||
component={object.component}
|
||||
sprint={object.sprint}
|
||||
team={_space}
|
||||
project={_space}
|
||||
maxHeight="limited"
|
||||
/>
|
||||
<svelte:fragment slot="pool">
|
||||
@@ -198,7 +198,11 @@
|
||||
}}
|
||||
/>
|
||||
<EstimationEditor kind={'no-border'} size={'small'} value={object} />
|
||||
<ProjectSelector value={object.project} onChange={handleProjectIdChanged} />
|
||||
<SprintSelector value={object.sprint} onChange={handleSprintIdChanged} useProject={object.project ?? undefined} />
|
||||
<ComponentSelector value={object.component} onChange={handleComponentIdChanged} />
|
||||
<SprintSelector
|
||||
value={object.sprint}
|
||||
onChange={handleSprintIdChanged}
|
||||
useComponent={object.component ?? undefined}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
IssuePriority,
|
||||
IssueStatus,
|
||||
IssueTemplateChild,
|
||||
Project,
|
||||
Component as ComponentType,
|
||||
Sprint,
|
||||
Team
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import { Button, Component, EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -34,9 +34,9 @@
|
||||
import StatusEditor from '../issues/StatusEditor.svelte'
|
||||
import EstimationEditor from './EstimationEditor.svelte'
|
||||
|
||||
export let team: Team
|
||||
export let project: Project
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let project: Ref<Project> | null = null
|
||||
export let component: Ref<ComponentType> | null = null
|
||||
export let childIssue: DraftIssueChild | undefined = undefined
|
||||
export let showBorder = false
|
||||
export let statuses: WithLookup<IssueStatus>[]
|
||||
@@ -66,8 +66,8 @@
|
||||
title: '',
|
||||
description: '',
|
||||
assignee: null,
|
||||
status: team.defaultIssueStatus,
|
||||
project,
|
||||
status: project.defaultIssueStatus,
|
||||
component,
|
||||
priority: IssuePriority.NoPriority,
|
||||
sprint,
|
||||
estimation: 0
|
||||
@@ -95,7 +95,7 @@
|
||||
const value: IssueTemplateChild = {
|
||||
...newIssue,
|
||||
title: getTitle(newIssue.title),
|
||||
project: project ?? null,
|
||||
component: component ?? null,
|
||||
labels: labels.map((it) => it._id)
|
||||
}
|
||||
if (childIssue === undefined) {
|
||||
@@ -130,7 +130,7 @@
|
||||
{#key newIssue.id}
|
||||
<AttachmentStyledBox
|
||||
objectId={newIssue.id}
|
||||
space={team._id}
|
||||
space={project._id}
|
||||
_class={tracker.class.Issue}
|
||||
bind:content={newIssue.description}
|
||||
placeholder={tracker.string.SubIssueDescriptionPlaceholder}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
DraftIssueChild,
|
||||
IssueStatus,
|
||||
IssueTemplateChild,
|
||||
Project,
|
||||
Component,
|
||||
Sprint,
|
||||
Team
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { ActionContext, FixedColumn } from '@hcengineering/view-resources'
|
||||
@@ -35,9 +35,9 @@
|
||||
import EstimationEditor from './EstimationEditor.svelte'
|
||||
|
||||
export let issues: DraftIssueChild[]
|
||||
export let team: Ref<Team>
|
||||
export let project: Ref<Project>
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let project: Ref<Project> | null = null
|
||||
export let component: Ref<Component> | null = null
|
||||
export let statuses: WithLookup<IssueStatus>[]
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -49,9 +49,9 @@
|
||||
DraftIssueChildEditor,
|
||||
{
|
||||
showBorder: true,
|
||||
team: currentTeam,
|
||||
project: currentProject,
|
||||
sprint,
|
||||
project,
|
||||
component,
|
||||
statuses,
|
||||
childIssue: target
|
||||
},
|
||||
@@ -91,19 +91,19 @@
|
||||
resetDrag()
|
||||
}
|
||||
|
||||
const teamQuery = createQuery()
|
||||
$: teamQuery.query(
|
||||
tracker.class.Team,
|
||||
const projectQuery = createQuery()
|
||||
$: projectQuery.query(
|
||||
tracker.class.Project,
|
||||
{
|
||||
_id: team
|
||||
_id: project
|
||||
},
|
||||
(res) => ([currentTeam] = res)
|
||||
(res) => ([currentProject] = res)
|
||||
)
|
||||
let currentTeam: Team | undefined = undefined
|
||||
let currentProject: Project | undefined = undefined
|
||||
|
||||
function getIssueTemplateId (currentTeam: Team | undefined, issue: IssueTemplateChild): string {
|
||||
return currentTeam
|
||||
? `${currentTeam.identifier}-${issues.findIndex((it) => it.id === issue.id)}`
|
||||
function getIssueTemplateId (currentProject: Project | undefined, issue: IssueTemplateChild): string {
|
||||
return currentProject
|
||||
? `${currentProject.identifier}-${issues.findIndex((it) => it.id === issue.id)}`
|
||||
: `${issues.findIndex((it) => it.id === issue.id)}}`
|
||||
}
|
||||
</script>
|
||||
@@ -156,7 +156,7 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span class="issuePresenter" on:click={(evt) => openIssue(evt, issue)}>
|
||||
<FixedColumn key={'issue_template_issue'} justify={'left'}>
|
||||
{getIssueTemplateId(currentTeam, issue)}
|
||||
{getIssueTemplateId(currentProject, issue)}
|
||||
</FixedColumn>
|
||||
</span>
|
||||
<span class="text name" title={issue.title} on:click={(evt) => openIssue(evt, issue)}>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
import presentation, { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
|
||||
import setting, { settingId } from '@hcengineering/setting'
|
||||
import tags from '@hcengineering/tags'
|
||||
import type { IssueTemplate, IssueTemplateChild, Team } from '@hcengineering/tracker'
|
||||
import type { IssueTemplate, IssueTemplateChild, Project } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
EditBox,
|
||||
@@ -52,7 +52,7 @@
|
||||
const client = getClient()
|
||||
|
||||
let template: WithLookup<IssueTemplate> | undefined
|
||||
let currentTeam: Team | undefined
|
||||
let currentProject: Project | undefined
|
||||
let title = ''
|
||||
let description = ''
|
||||
let innerWidth: number
|
||||
@@ -85,9 +85,9 @@
|
||||
;[template] = result
|
||||
title = template.title
|
||||
description = template.description
|
||||
currentTeam = template.$lookup?.space
|
||||
currentProject = template.$lookup?.space
|
||||
},
|
||||
{ lookup: { space: tracker.class.Team, labels: tags.class.TagElement } }
|
||||
{ lookup: { space: tracker.class.Project, labels: tags.class.TagElement } }
|
||||
)
|
||||
|
||||
$: canSave = title.trim().length > 0
|
||||
@@ -249,11 +249,11 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
{#key template._id && currentTeam !== undefined}
|
||||
{#if currentTeam !== undefined}
|
||||
{#key template._id && currentProject !== undefined}
|
||||
{#if currentProject !== undefined}
|
||||
<SubIssueTemplates
|
||||
maxHeight="limited"
|
||||
team={template.space}
|
||||
project={template.space}
|
||||
bind:children={template.children}
|
||||
on:create-issue={createIssue}
|
||||
on:update-issue={updateIssue}
|
||||
@@ -292,7 +292,7 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="custom-attributes">
|
||||
{#if template && currentTeam}
|
||||
{#if template && currentProject}
|
||||
<TemplateControlPanel issue={template} />
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import presentation, { createQuery, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
import tags, { TagElement, TagReference } from '@hcengineering/tags'
|
||||
import { StyledTextArea } from '@hcengineering/text-editor'
|
||||
import { IssuePriority, IssueTemplateChild, Project, Sprint } from '@hcengineering/tracker'
|
||||
import { IssuePriority, IssueTemplateChild, Component as ComponentType, Sprint } from '@hcengineering/tracker'
|
||||
import { Button, Component, EditBox } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
@@ -26,7 +26,7 @@
|
||||
import EstimationEditor from './EstimationEditor.svelte'
|
||||
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let project: Ref<Project> | null = null
|
||||
export let component: Ref<ComponentType> | null = null
|
||||
export let childIssue: IssueTemplateChild | undefined = undefined
|
||||
export let showBorder = false
|
||||
export let isScrollable: boolean = false
|
||||
@@ -57,7 +57,7 @@
|
||||
title: '',
|
||||
description: '',
|
||||
assignee: null,
|
||||
project: null,
|
||||
component: null,
|
||||
priority: IssuePriority.NoPriority,
|
||||
sprint,
|
||||
estimation: 0
|
||||
@@ -85,7 +85,7 @@
|
||||
const value: IssueTemplateChild = {
|
||||
...newIssue,
|
||||
title: getTitle(newIssue.title),
|
||||
project: project ?? null,
|
||||
component: component ?? null,
|
||||
labels: labels.map((it) => it._id)
|
||||
}
|
||||
if (childIssue === undefined) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import tracker, { IssueTemplateChild, Project, Sprint, Team } from '@hcengineering/tracker'
|
||||
import tracker, { IssueTemplateChild, Component, Sprint, Project } from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { ActionContext, FixedColumn } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -27,9 +27,9 @@
|
||||
import IssueTemplateChildEditor from './IssueTemplateChildEditor.svelte'
|
||||
|
||||
export let issues: IssueTemplateChild[]
|
||||
export let team: Ref<Team>
|
||||
export let project: Ref<Project>
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let project: Ref<Project> | null = null
|
||||
export let component: Ref<Component> | null = null
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
{
|
||||
showBorder: true,
|
||||
sprint,
|
||||
project,
|
||||
component,
|
||||
childIssue: target
|
||||
},
|
||||
eventToHTMLElement(evt),
|
||||
@@ -81,19 +81,19 @@
|
||||
resetDrag()
|
||||
}
|
||||
|
||||
const teamQuery = createQuery()
|
||||
$: teamQuery.query(
|
||||
tracker.class.Team,
|
||||
const projectQuery = createQuery()
|
||||
$: projectQuery.query(
|
||||
tracker.class.Project,
|
||||
{
|
||||
_id: team
|
||||
_id: project
|
||||
},
|
||||
(res) => ([currentTeam] = res)
|
||||
(res) => ([currentProject] = res)
|
||||
)
|
||||
let currentTeam: Team | undefined = undefined
|
||||
let currentProject: Project | undefined = undefined
|
||||
|
||||
function getIssueTemplateId (currentTeam: Team | undefined, issue: IssueTemplateChild): string {
|
||||
return currentTeam
|
||||
? `${currentTeam.identifier}-${issues.findIndex((it) => it.id === issue.id)}`
|
||||
function getIssueTemplateId (currentProject: Project | undefined, issue: IssueTemplateChild): string {
|
||||
return currentProject
|
||||
? `${currentProject.identifier}-${issues.findIndex((it) => it.id === issue.id)}`
|
||||
: `${issues.findIndex((it) => it.id === issue.id)}}`
|
||||
}
|
||||
</script>
|
||||
@@ -138,7 +138,7 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span class="issuePresenter" on:click={(evt) => openIssue(evt, issue)}>
|
||||
<FixedColumn key={'issue_template_issue'} justify={'left'}>
|
||||
{getIssueTemplateId(currentTeam, issue)}
|
||||
{getIssueTemplateId(currentProject, issue)}
|
||||
</FixedColumn>
|
||||
</span>
|
||||
<span class="text name" title={issue.title} on:click={(evt) => openIssue(evt, issue)}>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IssueTemplateChild, Project, Sprint, Team } from '@hcengineering/tracker'
|
||||
import { IssueTemplateChild, Component, Sprint, Project } from '@hcengineering/tracker'
|
||||
import { Button, closeTooltip, ExpandCollapse, IconAdd, Scroller } from '@hcengineering/ui'
|
||||
import { afterUpdate, createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
@@ -24,9 +24,9 @@
|
||||
import IssueTemplateChildList from './IssueTemplateChildList.svelte'
|
||||
|
||||
export let children: IssueTemplateChild[] = []
|
||||
export let team: Ref<Team>
|
||||
export let project: Ref<Project>
|
||||
export let sprint: Ref<Sprint> | null = null
|
||||
export let project: Ref<Project> | null = null
|
||||
export let component: Ref<Component> | null = null
|
||||
export let isScrollable: boolean = false
|
||||
export let maxHeight: 'max' | 'card' | 'limited' | string | undefined = undefined
|
||||
|
||||
@@ -88,10 +88,10 @@
|
||||
<div class="flex-col flex-no-shrink max-h-30 list clear-mins" class:collapsed={isCollapsed}>
|
||||
<Scroller>
|
||||
<IssueTemplateChildList
|
||||
{project}
|
||||
{component}
|
||||
{sprint}
|
||||
bind:issues={children}
|
||||
{team}
|
||||
{project}
|
||||
on:move={handleIssueSwap}
|
||||
on:update-issue
|
||||
/>
|
||||
@@ -102,7 +102,7 @@
|
||||
{#if isCreating}
|
||||
<ExpandCollapse isExpanded={!isCollapsed} on:changeContent>
|
||||
<IssueTemplateChildEditor
|
||||
{project}
|
||||
{component}
|
||||
{sprint}
|
||||
{isScrollable}
|
||||
{maxHeight}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { IssueTemplate, Team } from '@hcengineering/tracker'
|
||||
import { IssueTemplate, Project } from '@hcengineering/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import IssueTemplatesView from './IssueTemplatesView.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let currentSpace: Ref<Project>
|
||||
|
||||
const query: DocumentQuery<IssueTemplate> = { space: currentSpace }
|
||||
</script>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
import tracker from '../../plugin'
|
||||
import AssigneeEditor from '../issues/AssigneeEditor.svelte'
|
||||
import PriorityEditor from '../issues/PriorityEditor.svelte'
|
||||
import ProjectEditor from '../projects/ProjectEditor.svelte'
|
||||
import ComponentEditor from '../components/ComponentEditor.svelte'
|
||||
import SprintEditor from '../sprints/SprintEditor.svelte'
|
||||
|
||||
export let issue: WithLookup<IssueTemplate>
|
||||
@@ -37,7 +37,7 @@
|
||||
keys = filtredKeys.filter((key) => !isCollectionAttr(hierarchy, key))
|
||||
}
|
||||
|
||||
$: updateKeys(['title', 'description', 'priority', 'number', 'assignee', 'project', 'sprint'])
|
||||
$: updateKeys(['title', 'description', 'priority', 'number', 'assignee', 'component', 'sprint'])
|
||||
|
||||
const key: KeyedAttribute = {
|
||||
key: 'labels',
|
||||
@@ -97,9 +97,9 @@
|
||||
<div class="divider" />
|
||||
|
||||
<span class="label">
|
||||
<Label label={tracker.string.Project} />
|
||||
<Label label={tracker.string.Component} />
|
||||
</span>
|
||||
<ProjectEditor value={issue} />
|
||||
<ComponentEditor value={issue} />
|
||||
|
||||
{#if issue.sprint}
|
||||
<span class="label">
|
||||
|
||||
@@ -18,21 +18,21 @@
|
||||
import { AttachedData, Class, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Button, Icon, Label, Panel, Scroller, IconAdd, Loading, closeTooltip, showPopup } from '@hcengineering/ui'
|
||||
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import { calcRank, IssueStatus, IssueStatusCategory, Team } from '@hcengineering/tracker'
|
||||
import { calcRank, IssueStatus, IssueStatusCategory, Project } from '@hcengineering/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import StatusEditor from './StatusEditor.svelte'
|
||||
import StatusPresenter from './StatusPresenter.svelte'
|
||||
import ExpandCollapse from '@hcengineering/ui/src/components/ExpandCollapse.svelte'
|
||||
|
||||
export let teamId: Ref<Team>
|
||||
export let teamClass: Ref<Class<Team>>
|
||||
export let projectId: Ref<Project>
|
||||
export let projectClass: Ref<Class<Project>>
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
const teamQuery = createQuery()
|
||||
const projectQuery = createQuery()
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
let team: Team | undefined
|
||||
let project: Project | undefined
|
||||
let statusCategories: IssueStatusCategory[] | undefined
|
||||
let workflowStatuses: IssueStatus[] | undefined
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
)
|
||||
}
|
||||
|
||||
async function updateTeamDefaultStatus (statusId: Ref<IssueStatus>) {
|
||||
if (team) {
|
||||
await client.update(team, { defaultIssueStatus: statusId })
|
||||
async function updateProjectDefaultStatus (statusId: Ref<IssueStatus>) {
|
||||
if (project) {
|
||||
await client.update(project, { defaultIssueStatus: statusId })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,13 +63,20 @@
|
||||
const nextStatus = workflowStatuses[workflowStatuses.findIndex(({ _id }) => _id === prevStatus._id) + 1]
|
||||
|
||||
isSaving = true
|
||||
await client.addCollection(tracker.class.IssueStatus, teamId, teamId, tracker.class.Team, 'issueStatuses', {
|
||||
name: editingStatus.name,
|
||||
description: editingStatus.description,
|
||||
color: editingStatus.color,
|
||||
category: editingStatus.category,
|
||||
rank: calcRank(prevStatus, nextStatus)
|
||||
})
|
||||
await client.addCollection(
|
||||
tracker.class.IssueStatus,
|
||||
projectId,
|
||||
projectId,
|
||||
tracker.class.Project,
|
||||
'issueStatuses',
|
||||
{
|
||||
name: editingStatus.name,
|
||||
description: editingStatus.description,
|
||||
color: editingStatus.color,
|
||||
category: editingStatus.category,
|
||||
rank: calcRank(prevStatus, nextStatus)
|
||||
}
|
||||
)
|
||||
isSaving = false
|
||||
}
|
||||
|
||||
@@ -147,16 +154,16 @@
|
||||
},
|
||||
undefined,
|
||||
async (result) => {
|
||||
if (result && team && workflowStatuses) {
|
||||
if (result && project && workflowStatuses) {
|
||||
isSaving = true
|
||||
await client.removeDoc(status._class, status.space, status._id)
|
||||
|
||||
if (team.defaultIssueStatus === status._id) {
|
||||
if (project.defaultIssueStatus === status._id) {
|
||||
const newDefaultStatus = workflowStatuses.find(
|
||||
(s) => s._id !== status._id && s.category === status.category
|
||||
)
|
||||
if (newDefaultStatus?._id) {
|
||||
await updateTeamDefaultStatus(newDefaultStatus._id)
|
||||
await updateProjectDefaultStatus(newDefaultStatus._id)
|
||||
}
|
||||
}
|
||||
isSaving = false
|
||||
@@ -213,8 +220,8 @@
|
||||
hoveringStatus = null
|
||||
}
|
||||
|
||||
$: teamQuery.query(teamClass, { _id: teamId }, (result) => ([team] = result), { limit: 1 })
|
||||
$: statusesQuery.query(tracker.class.IssueStatus, { attachedTo: teamId }, (res) => (workflowStatuses = res), {
|
||||
$: 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()
|
||||
@@ -230,14 +237,14 @@
|
||||
<span class="wrapped-title">
|
||||
<Label label={tracker.string.ManageWorkflowStatuses} />
|
||||
</span>
|
||||
{#if team}
|
||||
<span class="wrapped-subtitle">{team.name}</span>
|
||||
{#if project}
|
||||
<span class="wrapped-subtitle">{project.name}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
{#if team === undefined || statusCategories === undefined || workflowStatuses === undefined}
|
||||
{#if project === undefined || statusCategories === undefined || workflowStatuses === undefined}
|
||||
<Loading />
|
||||
{:else}
|
||||
<Scroller>
|
||||
@@ -287,9 +294,9 @@
|
||||
{:else}
|
||||
<StatusPresenter
|
||||
value={status}
|
||||
isDefault={status._id === team.defaultIssueStatus}
|
||||
isDefault={status._id === project.defaultIssueStatus}
|
||||
{isSingle}
|
||||
on:default-update={({ detail }) => updateTeamDefaultStatus(detail)}
|
||||
on:default-update={({ detail }) => updateProjectDefaultStatus(detail)}
|
||||
on:edit={({ detail }) => {
|
||||
closeTooltip()
|
||||
editingStatus = { ...detail, color: detail.color ?? category.color }
|
||||
|
||||
Reference in New Issue
Block a user