TSK-810 Rename Team -> Project, Project -> Component (#2756)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2023-03-17 12:17:53 +06:00
committed by GitHub
parent 26adf14966
commit 0f24fe80d6
110 changed files with 1646 additions and 1324 deletions
@@ -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
@@ -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,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}
@@ -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 />
@@ -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}
@@ -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}
/>
@@ -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;
@@ -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
@@ -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,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;
@@ -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) => {
@@ -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>
@@ -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>
@@ -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} />
@@ -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}
@@ -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
@@ -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 />
@@ -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 }} />
@@ -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} />
@@ -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> = {}
@@ -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}
@@ -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
@@ -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>
@@ -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
@@ -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">
@@ -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} />
@@ -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);
@@ -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 }
+57 -57
View File
@@ -25,7 +25,7 @@ import {
} from '@hcengineering/core'
import { Resources, translate } from '@hcengineering/platform'
import { getClient, MessageBox, ObjectSearchResult } from '@hcengineering/presentation'
import { Issue, Scrum, ScrumRecord, Sprint, Team } from '@hcengineering/tracker'
import { Issue, Scrum, ScrumRecord, Sprint, Project } from '@hcengineering/tracker'
import { showPopup } from '@hcengineering/ui'
import CreateIssue from './components/CreateIssue.svelte'
import Inbox from './components/inbox/Inbox.svelte'
@@ -52,24 +52,24 @@ import TitlePresenter from './components/issues/TitlePresenter.svelte'
import MyIssues from './components/myissues/MyIssues.svelte'
import NewIssueHeader from './components/NewIssueHeader.svelte'
import NopeComponent from './components/NopeComponent.svelte'
import EditProject from './components/projects/EditProject.svelte'
import IconPresenter from './components/projects/IconPresenter.svelte'
import LeadPresenter from './components/projects/LeadPresenter.svelte'
import ProjectEditor from './components/projects/ProjectEditor.svelte'
import ProjectPresenter from './components/projects/ProjectPresenter.svelte'
import Projects from './components/projects/Projects.svelte'
import ProjectStatusEditor from './components/projects/ProjectStatusEditor.svelte'
import ProjectStatusPresenter from './components/projects/ProjectStatusPresenter.svelte'
import ProjectTitlePresenter from './components/projects/ProjectTitlePresenter.svelte'
import Roadmap from './components/projects/Roadmap.svelte'
import TargetDatePresenter from './components/projects/TargetDatePresenter.svelte'
import TeamProjects from './components/projects/TeamProjects.svelte'
import EditComponent from './components/components/EditComponent.svelte'
import IconPresenter from './components/components/IconComponent.svelte'
import LeadPresenter from './components/components/LeadPresenter.svelte'
import ComponentEditor from './components/components/ComponentEditor.svelte'
import ComponentPresenter from './components/components/ComponentPresenter.svelte'
import Components from './components/components/Components.svelte'
import ComponentStatusEditor from './components/components/ComponentStatusEditor.svelte'
import ComponentStatusPresenter from './components/components/ComponentStatusPresenter.svelte'
import ComponentTitlePresenter from './components/components/ComponentTitlePresenter.svelte'
import Roadmap from './components/components/Roadmap.svelte'
import TargetDatePresenter from './components/components/TargetDatePresenter.svelte'
import ProjectComponents from './components/components/ProjectComponents.svelte'
import RelationsPopup from './components/RelationsPopup.svelte'
import SetDueDateActionPopup from './components/SetDueDateActionPopup.svelte'
import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.svelte'
import SprintDatePresenter from './components/sprints/SprintDatePresenter.svelte'
import SprintLeadPresenter from './components/sprints/SprintLeadPresenter.svelte'
import SprintProjectEditor from './components/sprints/SprintProjectEditor.svelte'
import SprintComponentEditor from './components/sprints/SprintComponentEditor.svelte'
import CreateIssueTemplate from './components/templates/CreateIssueTemplate.svelte'
import Views from './components/views/Views.svelte'
import Statuses from './components/workflow/Statuses.svelte'
@@ -103,7 +103,7 @@ import TimeSpendReport from './components/issues/timereport/TimeSpendReport.svel
import RelatedIssues from './components/issues/related/RelatedIssues.svelte'
import RelatedIssueTemplates from './components/issues/related/RelatedIssueTemplates.svelte'
import ProjectSelector from './components/ProjectSelector.svelte'
import ComponentSelector from './components/ComponentSelector.svelte'
import IssueTemplatePresenter from './components/templates/IssueTemplatePresenter.svelte'
import IssueTemplates from './components/templates/IssueTemplates.svelte'
@@ -114,13 +114,13 @@ import EditIssueTemplate from './components/templates/EditIssueTemplate.svelte'
import TemplateEstimationEditor from './components/templates/EstimationEditor.svelte'
import {
getAllPriority,
getAllProjects,
getAllComponents,
getAllSprints,
getAllStatuses,
issuePrioritySort,
issueStatusSort,
moveIssuesToAnotherSprint,
removeTeam,
removeProject,
sprintSort,
subIssueQuery
} from './utils'
@@ -128,11 +128,11 @@ import {
import { EmployeeAccount } from '@hcengineering/contact'
import StatusRefPresenter from './components/issues/StatusRefPresenter.svelte'
import TimeSpendReportPopup from './components/issues/timereport/TimeSpendReportPopup.svelte'
import DeleteProjectPresenter from './components/projects/DeleteProjectPresenter.svelte'
import DeleteComponentPresenter from './components/components/DeleteComponentPresenter.svelte'
import IssueStatistics from './components/sprints/IssueStatistics.svelte'
import SprintRefPresenter from './components/sprints/SprintRefPresenter.svelte'
import CreateTeam from './components/teams/CreateTeam.svelte'
import TeamPresenter from './components/teams/TeamPresenter.svelte'
import CreateProject from './components/projects/CreateProject.svelte'
import ProjectPresenter from './components/projects/ProjectPresenter.svelte'
export { default as SubIssueList } from './components/issues/edit/SubIssueList.svelte'
@@ -142,7 +142,7 @@ export async function queryIssue<D extends Issue> (
search: string,
filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }
): Promise<ObjectSearchResult[]> {
const teams = await client.findAll<Team>(tracker.class.Team, {})
const projects = await client.findAll<Project>(tracker.class.Project, {})
const q: DocumentQuery<Issue> = { title: { $like: `%${search}%` } }
if (filter?.in !== undefined || filter?.nin !== undefined) {
@@ -158,13 +158,13 @@ export async function queryIssue<D extends Issue> (
const named = toIdMap(
await client.findAll<Issue>(_class, q, {
limit: 200,
lookup: { space: tracker.class.Team }
lookup: { space: tracker.class.Project }
})
)
for (const currentTeam of teams) {
for (const currentProject of projects) {
const nids: number[] = []
for (let n = 0; n <= currentTeam.sequence; n++) {
const v = `${currentTeam.identifier}-${n}`
for (let n = 0; n <= currentProject.sequence; n++) {
const v = `${currentProject.identifier}-${n}`
if (v.includes(search)) {
nids.push(n)
}
@@ -174,7 +174,7 @@ export async function queryIssue<D extends Issue> (
if (q._id !== undefined) {
q2._id = q._id
}
const numbered = await client.findAll<Issue>(_class, q2, { limit: 200, lookup: { space: tracker.class.Team } })
const numbered = await client.findAll<Issue>(_class, q2, { limit: 200, lookup: { space: tracker.class.Project } })
for (const d of numbered) {
if (!named.has(d._id)) {
named.set(d._id, d)
@@ -185,45 +185,45 @@ export async function queryIssue<D extends Issue> (
return Array.from(named.values()).map((e) => ({
doc: e,
title: getIssueId(e.$lookup?.space as Team, e),
title: getIssueId(e.$lookup?.space as Project, e),
icon: tracker.icon.TrackerApplication,
component: IssueItem
}))
}
async function editWorkflowStatuses (team: Team | undefined): Promise<void> {
if (team !== undefined) {
showPopup(Statuses, { teamId: team._id, teamClass: team._class }, 'float')
async function editWorkflowStatuses (project: Project | undefined): Promise<void> {
if (project !== undefined) {
showPopup(Statuses, { projectId: project._id, projectClass: project._class }, 'float')
}
}
async function editTeam (team: Team | undefined): Promise<void> {
if (team !== undefined) {
showPopup(CreateTeam, { team })
async function editProject (project: Project | undefined): Promise<void> {
if (project !== undefined) {
showPopup(CreateProject, { project })
}
}
async function deleteTeam (team: Team | undefined): Promise<void> {
if (team !== undefined) {
async function deleteProject (project: Project | undefined): Promise<void> {
if (project !== undefined) {
const client = getClient()
const anyIssue = await client.findOne(tracker.class.Issue, { space: team._id })
const anyIssue = await client.findOne(tracker.class.Issue, { space: project._id })
if (anyIssue !== undefined) {
showPopup(
MessageBox,
{
label: tracker.string.DeleteTeamName,
labelProps: { name: team.name },
message: tracker.string.TeamHasIssues
label: tracker.string.DeleteProjectName,
labelProps: { name: project.name },
message: tracker.string.ProjectHasIssues
},
undefined,
(result?: boolean) => {
if (result === true) {
void removeTeam(team)
void removeProject(project)
}
}
)
} else {
await removeTeam(team)
await removeProject(project)
}
}
}
@@ -353,18 +353,18 @@ export default async (): Promise<Resources> => ({
Backlog,
Inbox,
MyIssues,
Projects,
Components,
Views,
IssuePresenter,
ProjectPresenter,
ProjectTitlePresenter,
ComponentPresenter,
ComponentTitlePresenter,
TitlePresenter,
ModificationDatePresenter,
PriorityPresenter,
PriorityEditor,
PriorityRefPresenter,
SprintRefPresenter,
ProjectEditor,
ComponentEditor,
StatusPresenter,
StatusEditor,
AssigneePresenter,
@@ -374,14 +374,14 @@ export default async (): Promise<Resources> => ({
IconPresenter,
LeadPresenter,
TargetDatePresenter,
ProjectStatusPresenter,
ProjectStatusEditor,
ComponentStatusPresenter,
ComponentStatusEditor,
SetDueDateActionPopup,
SetParentIssueActionPopup,
EditProject,
EditComponent,
IssuesView,
KanbanView,
TeamProjects,
ProjectComponents,
Roadmap,
IssuePreview,
RelationsPopup,
@@ -401,20 +401,20 @@ export default async (): Promise<Resources> => ({
SubIssuesSelector,
RelatedIssues,
RelatedIssueTemplates,
ProjectSelector,
ComponentSelector,
IssueTemplates,
IssueTemplatePresenter,
EditIssueTemplate,
TemplateEstimationEditor,
CreateTeam,
TeamPresenter,
CreateProject,
ProjectPresenter,
IssueStatistics,
StatusRefPresenter,
RelatedIssuesSection,
RelatedIssueSelector,
DeleteProjectPresenter,
DeleteComponentPresenter,
TimeSpendReportPopup,
SprintProjectEditor,
SprintComponentEditor,
SprintDatePresenter,
SprintLeadPresenter
},
@@ -434,14 +434,14 @@ export default async (): Promise<Resources> => ({
SubIssueQuery: subIssueQuery,
GetAllStatuses: getAllStatuses,
GetAllPriority: getAllPriority,
GetAllProjects: getAllProjects,
GetAllComponents: getAllComponents,
GetAllSprints: getAllSprints
},
actionImpl: {
EditWorkflowStatuses: editWorkflowStatuses,
EditTeam: editTeam,
EditProject: editProject,
DeleteSprint: deleteSprint,
DeleteTeam: deleteTeam
DeleteProject: deleteProject
},
resolver: {
Location: resolveLocation
+12 -12
View File
@@ -1,16 +1,16 @@
import { Doc, DocumentUpdate, Ref, RelatedDocument, TxOperations } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Issue, Project, Sprint, Team, trackerId } from '@hcengineering/tracker'
import { Issue, Component, Sprint, Project, trackerId } from '@hcengineering/tracker'
import { getCurrentLocation, getPanelURI, Location, navigate } from '@hcengineering/ui'
import { workbenchId } from '@hcengineering/workbench'
import { writable } from 'svelte/store'
import tracker from './plugin'
export const activeProject = writable<Ref<Project> | undefined>(undefined)
export const activeComponent = writable<Ref<Component> | undefined>(undefined)
export const activeSprint = writable<Ref<Sprint> | undefined>(undefined)
export function getIssueId (team: Team, issue: Issue): string {
return `${team.identifier}-${issue.number}`
export function getIssueId (project: Project, issue: Issue): string {
return `${project.identifier}-${issue.number}`
}
export function isIssueId (shortLink: string): boolean {
@@ -21,16 +21,16 @@ export async function getIssueTitle (client: TxOperations, ref: Ref<Doc>): Promi
const object = await client.findOne(
tracker.class.Issue,
{ _id: ref as Ref<Issue> },
{ lookup: { space: tracker.class.Team } }
{ lookup: { space: tracker.class.Project } }
)
if (object?.$lookup?.space === undefined) throw new Error(`Issue Team not found, _id: ${ref}`)
if (object?.$lookup?.space === undefined) throw new Error(`Issue project not found, _id: ${ref}`)
return getIssueId(object.$lookup.space, object)
}
async function getTitle (doc: Doc): Promise<string> {
const client = getClient()
const issue = doc as Issue
const object = await client.findOne(tracker.class.Team, { _id: issue.space })
const object = await client.findOne(tracker.class.Project, { _id: issue.space })
if (object === undefined) return `?-${issue.number}`
return getIssueId(object, issue)
}
@@ -65,17 +65,17 @@ export async function generateIssueLocation (loc: Location, issueId: string): Pr
if (tokens.length < 2) {
return undefined
}
const teamId = tokens[0]
const projectId = tokens[0]
const issueNumber = Number(tokens[1])
const client = getClient()
const team = await client.findOne(tracker.class.Team, { identifier: teamId })
if (team === undefined) {
const project = await client.findOne(tracker.class.Project, { identifier: projectId })
if (project === undefined) {
console.error(
`Could not find team ${teamId}. Make sure you are in correct workspace and the team was not deleted or renamed.`
`Could not find project ${projectId}. Make sure you are in correct workspace and the project was not deleted or renamed.`
)
return undefined
}
const issue = await client.findOne(tracker.class.Issue, { number: issueNumber, space: team._id })
const issue = await client.findOne(tracker.class.Issue, { number: issueNumber, space: project._id })
if (issue === undefined) {
console.error(`Could not find issue ${issueId}.`)
return undefined
+47 -47
View File
@@ -43,19 +43,19 @@ export default mergeIds(trackerId, tracker, {
BacklogIssues: '' as IntlString,
Backlog: '' as IntlString,
Board: '' as IntlString,
Project: '' as IntlString,
Projects: '' as IntlString,
AllProjects: '' as IntlString,
BacklogProjects: '' as IntlString,
ActiveProjects: '' as IntlString,
ClosedProjects: '' as IntlString,
NewProject: '' as IntlString,
CreateProject: '' as IntlString,
ProjectNamePlaceholder: '' as IntlString,
ProjectDescriptionPlaceholder: '' as IntlString,
ProjectStatusPlaceholder: '' as IntlString,
ProjectLead: '' as IntlString,
ProjectMembers: '' as IntlString,
Component: '' as IntlString,
Components: '' as IntlString,
AllComponents: '' as IntlString,
BacklogComponents: '' as IntlString,
ActiveComponents: '' as IntlString,
ClosedComponents: '' as IntlString,
NewComponent: '' as IntlString,
CreateComponent: '' as IntlString,
ComponentNamePlaceholder: '' as IntlString,
ComponentDescriptionPlaceholder: '' as IntlString,
ComponentStatusPlaceholder: '' as IntlString,
ComponentLead: '' as IntlString,
ComponentMembers: '' as IntlString,
StartDate: '' as IntlString,
TargetDate: '' as IntlString,
Planned: '' as IntlString,
@@ -63,18 +63,18 @@ export default mergeIds(trackerId, tracker, {
Paused: '' as IntlString,
Completed: '' as IntlString,
Canceled: '' as IntlString,
CreateTeam: '' as IntlString,
NewTeam: '' as IntlString,
TeamTitlePlaceholder: '' as IntlString,
TeamIdentifierPlaceholder: '' as IntlString,
CreateProject: '' as IntlString,
NewProject: '' as IntlString,
ProjectTitlePlaceholder: '' as IntlString,
ProjectIdentifierPlaceholder: '' as IntlString,
ChooseIcon: '' as IntlString,
AddIssue: '' as IntlString,
NewIssue: '' as IntlString,
ResumeDraft: '' as IntlString,
NewSubIssue: '' as IntlString,
Team: '' as IntlString,
Project: '' as IntlString,
SelectIssue: '' as IntlString,
SelectTeam: '' as IntlString,
SelectProject: '' as IntlString,
SaveIssue: '' as IntlString,
Todo: '' as IntlString,
Done: '' as IntlString,
@@ -93,10 +93,10 @@ export default mergeIds(trackerId, tracker, {
DefaultIssueStatus: '' as IntlString,
IssueStatuses: '' as IntlString,
EditWorkflowStatuses: '' as IntlString,
EditTeam: '' as IntlString,
DeleteTeam: '' as IntlString,
DeleteTeamName: '' as IntlString,
TeamHasIssues: '' as IntlString,
EditProject: '' as IntlString,
DeleteProject: '' as IntlString,
DeleteProjectName: '' as IntlString,
ProjectHasIssues: '' as IntlString,
ManageWorkflowStatuses: '' as IntlString,
AddWorkflowStatus: '' as IntlString,
EditWorkflowStatus: '' as IntlString,
@@ -157,17 +157,17 @@ export default mergeIds(trackerId, tracker, {
ClearFilters: '' as IntlString,
Back: '' as IntlString,
AssetLabel: '' as IntlString,
AddToProject: '' as IntlString,
MoveToProject: '' as IntlString,
NoProject: '' as IntlString,
ProjectLeadTitle: '' as IntlString,
ProjectMembersTitle: '' as IntlString,
ProjectLeadSearchPlaceholder: '' as IntlString,
ProjectMembersSearchPlaceholder: '' as IntlString,
AddToComponent: '' as IntlString,
MoveToComponent: '' as IntlString,
NoComponent: '' as IntlString,
ComponentLeadTitle: '' as IntlString,
ComponentMembersTitle: '' as IntlString,
ComponentLeadSearchPlaceholder: '' as IntlString,
ComponentMembersSearchPlaceholder: '' as IntlString,
List: '' as IntlString,
NumberLabels: '' as IntlString,
Roadmap: '' as IntlString,
MoveToTeam: '' as IntlString,
MoveToProject: '' as IntlString,
Duplicate: '' as IntlString,
IssueTitlePlaceholder: '' as IntlString,
@@ -179,8 +179,8 @@ export default mergeIds(trackerId, tracker, {
NewIssueDialogClose: '' as IntlString,
NewIssueDialogCloseNote: '' as IntlString,
RemoveProjectDialogClose: '' as IntlString,
RemoveProjectDialogCloseNote: '' as IntlString,
RemoveComponentDialogClose: '' as IntlString,
RemoveComponentDialogCloseNote: '' as IntlString,
CopyIssueUrl: '' as IntlString,
CopyIssueId: '' as IntlString,
@@ -314,16 +314,16 @@ export default mergeIds(trackerId, tracker, {
Issues: '' as AnyComponent,
Active: '' as AnyComponent,
Backlog: '' as AnyComponent,
Projects: '' as AnyComponent,
Components: '' as AnyComponent,
IssuePresenter: '' as AnyComponent,
ProjectTitlePresenter: '' as AnyComponent,
ProjectPresenter: '' as AnyComponent,
ComponentTitlePresenter: '' as AnyComponent,
ComponentPresenter: '' as AnyComponent,
TitlePresenter: '' as AnyComponent,
ModificationDatePresenter: '' as AnyComponent,
PriorityPresenter: '' as AnyComponent,
PriorityEditor: '' as AnyComponent,
PriorityRefPresenter: '' as AnyComponent,
ProjectEditor: '' as AnyComponent,
ComponentEditor: '' as AnyComponent,
SprintEditor: '' as AnyComponent,
StatusPresenter: '' as AnyComponent,
StatusRefPresenter: '' as AnyComponent,
@@ -331,21 +331,21 @@ export default mergeIds(trackerId, tracker, {
AssigneePresenter: '' as AnyComponent,
DueDatePresenter: '' as AnyComponent,
EditIssueTemplate: '' as AnyComponent,
CreateTeam: '' as AnyComponent,
TeamPresenter: '' as AnyComponent,
CreateProject: '' as AnyComponent,
ProjectPresenter: '' as AnyComponent,
NewIssueHeader: '' as AnyComponent,
IconPresenter: '' as AnyComponent,
LeadPresenter: '' as AnyComponent,
TargetDatePresenter: '' as AnyComponent,
ProjectStatusPresenter: '' as AnyComponent,
ProjectStatusEditor: '' as AnyComponent,
ComponentStatusPresenter: '' as AnyComponent,
ComponentStatusEditor: '' as AnyComponent,
SetDueDateActionPopup: '' as AnyComponent,
SetParentIssueActionPopup: '' as AnyComponent,
EditProject: '' as AnyComponent,
EditComponent: '' as AnyComponent,
IssuesView: '' as AnyComponent,
KanbanView: '' as AnyComponent,
Roadmap: '' as AnyComponent,
TeamProjects: '' as AnyComponent,
ProjectComponents: '' as AnyComponent,
IssuePreview: '' as AnyComponent,
RelationsPopup: '' as AnyComponent,
SprintRefPresenter: '' as AnyComponent,
@@ -353,19 +353,19 @@ export default mergeIds(trackerId, tracker, {
SprintPresenter: '' as AnyComponent,
SprintStatusPresenter: '' as AnyComponent,
SprintTitlePresenter: '' as AnyComponent,
SprintProjectEditor: '' as AnyComponent,
SprintComponentEditor: '' as AnyComponent,
SprintDatePresenter: '' as AnyComponent,
SprintLeadPresenter: '' as AnyComponent,
ReportedTimeEditor: '' as AnyComponent,
TimeSpendReport: '' as AnyComponent,
EstimationEditor: '' as AnyComponent,
TemplateEstimationEditor: '' as AnyComponent,
DeleteProjectPresenter: '' as AnyComponent,
DeleteComponentPresenter: '' as AnyComponent,
Scrums: '' as AnyComponent,
ScrumRecordPanel: '' as AnyComponent,
ProjectSelector: '' as AnyComponent,
ComponentSelector: '' as AnyComponent,
IssueTemplates: '' as AnyComponent,
IssueTemplatePresenter: '' as AnyComponent,
@@ -390,7 +390,7 @@ export default mergeIds(trackerId, tracker, {
GetAllPriority: '' as Resource<
(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>
>,
GetAllProjects: '' as Resource<
GetAllComponents: '' as Resource<
(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>
>,
GetAllSprints: '' as Resource<
+17 -17
View File
@@ -21,7 +21,7 @@ import {
IssuesDateModificationPeriod,
IssuesGrouping,
IssuesOrdering,
ProjectStatus,
ComponentStatus,
SprintStatus
} from '@hcengineering/tracker'
import tracker from './plugin'
@@ -38,7 +38,7 @@ export const issuesGroupByOptions: Record<IssuesGrouping, IntlString> = {
[IssuesGrouping.Status]: tracker.string.Status,
[IssuesGrouping.Assignee]: tracker.string.Assignee,
[IssuesGrouping.Priority]: tracker.string.Priority,
[IssuesGrouping.Project]: tracker.string.Project,
[IssuesGrouping.Component]: tracker.string.Component,
[IssuesGrouping.Sprint]: tracker.string.Sprint,
[IssuesGrouping.NoGrouping]: tracker.string.NoGrouping
}
@@ -56,13 +56,13 @@ export const issuesDateModificationPeriodOptions: Record<IssuesDateModificationP
[IssuesDateModificationPeriod.PastWeek]: tracker.string.PastWeek,
[IssuesDateModificationPeriod.PastMonth]: tracker.string.PastMonth
}
export const defaultProjectStatuses = [
ProjectStatus.Backlog,
ProjectStatus.Planned,
ProjectStatus.InProgress,
ProjectStatus.Paused,
ProjectStatus.Completed,
ProjectStatus.Canceled
export const defaultComponentStatuses = [
ComponentStatus.Backlog,
ComponentStatus.Planned,
ComponentStatus.InProgress,
ComponentStatus.Paused,
ComponentStatus.Completed,
ComponentStatus.Canceled
]
export const defaultSprintStatuses = [
@@ -72,13 +72,13 @@ export const defaultSprintStatuses = [
SprintStatus.Canceled
]
export const projectStatusAssets: Record<ProjectStatus, { icon: Asset, label: IntlString }> = {
[ProjectStatus.Backlog]: { icon: tracker.icon.ProjectStatusBacklog, label: tracker.string.Backlog },
[ProjectStatus.Planned]: { icon: tracker.icon.ProjectStatusPlanned, label: tracker.string.Planned },
[ProjectStatus.InProgress]: { icon: tracker.icon.ProjectStatusInProgress, label: tracker.string.InProgress },
[ProjectStatus.Paused]: { icon: tracker.icon.ProjectStatusPaused, label: tracker.string.Paused },
[ProjectStatus.Completed]: { icon: tracker.icon.ProjectStatusCompleted, label: tracker.string.Completed },
[ProjectStatus.Canceled]: { icon: tracker.icon.ProjectStatusCanceled, label: tracker.string.Canceled }
export const componentStatusAssets: Record<ComponentStatus, { icon: Asset, label: IntlString }> = {
[ComponentStatus.Backlog]: { icon: tracker.icon.ComponentStatusBacklog, label: tracker.string.Backlog },
[ComponentStatus.Planned]: { icon: tracker.icon.ComponentStatusPlanned, label: tracker.string.Planned },
[ComponentStatus.InProgress]: { icon: tracker.icon.ComponentStatusInProgress, label: tracker.string.InProgress },
[ComponentStatus.Paused]: { icon: tracker.icon.ComponentStatusPaused, label: tracker.string.Paused },
[ComponentStatus.Completed]: { icon: tracker.icon.ComponentStatusCompleted, label: tracker.string.Completed },
[ComponentStatus.Canceled]: { icon: tracker.icon.ComponentStatusCanceled, label: tracker.string.Canceled }
}
export const sprintStatusAssets: Record<SprintStatus, { icon: Asset, label: IntlString }> = {
@@ -100,7 +100,7 @@ export const issuesGroupBySorting: Record<IssuesGrouping, SortingQuery<Issue>> =
[IssuesGrouping.Status]: { '$lookup.status.rank': SortingOrder.Ascending },
[IssuesGrouping.Assignee]: { '$lookup.assignee.name': SortingOrder.Ascending },
[IssuesGrouping.Priority]: { priority: SortingOrder.Ascending },
[IssuesGrouping.Project]: { '$lookup.project.label': SortingOrder.Ascending },
[IssuesGrouping.Component]: { '$lookup.component.label': SortingOrder.Ascending },
[IssuesGrouping.Sprint]: { '$lookup.sprint.label': SortingOrder.Ascending },
[IssuesGrouping.NoGrouping]: { rank: SortingOrder.Ascending }
}
+37 -37
View File
@@ -39,11 +39,11 @@ import {
IssuesOrdering,
IssueStatus,
IssueTemplateData,
Project,
ProjectStatus,
Component,
ComponentStatus,
Sprint,
SprintStatus,
Team,
Project,
TimeReportDayType
} from '@hcengineering/tracker'
import {
@@ -56,7 +56,7 @@ import {
} from '@hcengineering/ui'
import { CategoryQuery, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
import tracker from './plugin'
import { defaultPriorities, defaultProjectStatuses, defaultSprintStatuses, issuePriorities } from './types'
import { defaultPriorities, defaultComponentStatuses, defaultSprintStatuses, issuePriorities } from './types'
export * from './types'
@@ -72,18 +72,18 @@ export interface NavigationItem {
}
export interface Selection {
currentTeam?: Ref<Team>
currentProject?: Ref<Project>
currentSpecial?: string
}
export type IssuesGroupByKeys = keyof Pick<Issue, 'status' | 'priority' | 'assignee' | 'project' | 'sprint'>
export type IssuesGroupByKeys = keyof Pick<Issue, 'status' | 'priority' | 'assignee' | 'component' | 'sprint'>
export type IssuesOrderByKeys = keyof Pick<Issue, 'status' | 'priority' | 'modifiedOn' | 'dueDate' | 'rank'>
export const issuesGroupKeyMap: Record<IssuesGrouping, IssuesGroupByKeys | undefined> = {
[IssuesGrouping.Status]: 'status',
[IssuesGrouping.Priority]: 'priority',
[IssuesGrouping.Assignee]: 'assignee',
[IssuesGrouping.Project]: 'project',
[IssuesGrouping.Component]: 'component',
[IssuesGrouping.Sprint]: 'sprint',
[IssuesGrouping.NoGrouping]: undefined
}
@@ -104,10 +104,10 @@ export const issuesSortOrderMap: Record<IssuesOrderByKeys, SortingOrder> = {
rank: SortingOrder.Ascending
}
export const issuesGroupEditorMap: Record<'status' | 'priority' | 'project' | 'sprint', AnyComponent | undefined> = {
export const issuesGroupEditorMap: Record<'status' | 'priority' | 'component' | 'sprint', AnyComponent | undefined> = {
status: tracker.component.StatusEditor,
priority: tracker.component.PriorityEditor,
project: tracker.component.ProjectEditor,
component: tracker.component.ComponentEditor,
sprint: tracker.component.SprintEditor
}
@@ -201,10 +201,10 @@ export const getIssueFilterAssetsByType = (type: string): { icon: Asset, label:
label: tracker.string.Priority
}
}
case 'project': {
case 'component': {
return {
icon: tracker.icon.Project,
label: tracker.string.Project
icon: tracker.icon.Component,
label: tracker.string.Component
}
}
case 'sprint': {
@@ -256,25 +256,25 @@ export const getDueDateIconModifier = (
}
}
export type ProjectsViewMode = 'all' | 'backlog' | 'active' | 'closed'
export type ComponentsViewMode = 'all' | 'backlog' | 'active' | 'closed'
export type SprintViewMode = 'all' | 'planned' | 'active' | 'closed'
export type ScrumRecordViewMode = 'timeReports' | 'objects'
export const getIncludedProjectStatuses = (mode: ProjectsViewMode): ProjectStatus[] => {
export const getIncludedComponentStatuses = (mode: ComponentsViewMode): ComponentStatus[] => {
switch (mode) {
case 'all': {
return defaultProjectStatuses
return defaultComponentStatuses
}
case 'active': {
return [ProjectStatus.Planned, ProjectStatus.InProgress, ProjectStatus.Paused]
return [ComponentStatus.Planned, ComponentStatus.InProgress, ComponentStatus.Paused]
}
case 'backlog': {
return [ProjectStatus.Backlog]
return [ComponentStatus.Backlog]
}
case 'closed': {
return [ProjectStatus.Completed, ProjectStatus.Canceled]
return [ComponentStatus.Completed, ComponentStatus.Canceled]
}
default: {
return []
@@ -302,11 +302,11 @@ export const getIncludedSprintStatuses = (mode: SprintViewMode): SprintStatus[]
}
}
export const projectsTitleMap: Record<ProjectsViewMode, IntlString> = Object.freeze({
all: tracker.string.AllProjects,
backlog: tracker.string.BacklogProjects,
active: tracker.string.ActiveProjects,
closed: tracker.string.ClosedProjects
export const componentsTitleMap: Record<ComponentsViewMode, IntlString> = Object.freeze({
all: tracker.string.AllComponents,
backlog: tracker.string.BacklogComponents,
active: tracker.string.ActiveComponents,
closed: tracker.string.ClosedComponents
})
export const sprintTitleMap: Record<SprintViewMode, IntlString> = Object.freeze({
@@ -366,7 +366,7 @@ export async function mapKanbanCategories (
groupBy: string,
categories: any[],
statuses: Array<WithLookup<IssueStatus>>,
projects: Project[],
components: Component[],
sprints: Sprint[],
assignee: Employee[]
): Promise<TypeState[]> {
@@ -421,20 +421,20 @@ export async function mapKanbanCategories (
}
return res
}
if (groupBy === IssuesGrouping.Project) {
const noProject = await translate(tracker.string.NoProject, {})
const res: TypeState[] = projects
if (groupBy === IssuesGrouping.Component) {
const noComponent = await translate(tracker.string.NoComponent, {})
const res: TypeState[] = components
.filter((p) => categories.includes(p._id))
.map((project) => ({
_id: project._id,
title: project.label,
.map((component) => ({
_id: component._id,
title: component.label,
color: UNSET_COLOR,
icon: undefined
}))
if (categories.includes(undefined)) {
res.push({
_id: null,
title: noProject,
title: noComponent,
color: UNSET_COLOR,
icon: undefined
})
@@ -592,16 +592,16 @@ export async function getAllPriority (
return defaultPriorities
}
export async function getAllProjects (
space: Ref<Team> | undefined,
export async function getAllComponents (
space: Ref<Project> | undefined,
onUpdate: () => void,
queryId: Ref<Doc>
): Promise<any[] | undefined> {
return await getAllSomething(tracker.class.Project, space, onUpdate, queryId)
return await getAllSomething(tracker.class.Component, space, onUpdate, queryId)
}
export async function getAllSprints (
space: Ref<Team> | undefined,
space: Ref<Project> | undefined,
onUpdate: () => void,
queryId: Ref<Doc>
): Promise<any[] | undefined> {
@@ -651,7 +651,7 @@ export async function getPreviousAssignees (
})
}
export async function removeTeam (teamToDelete: Team): Promise<void> {
export async function removeProject (project: Project): Promise<void> {
const client = getClient()
await client.removeDoc(tracker.class.Team, core.space.Space, teamToDelete._id)
await client.removeDoc(tracker.class.Project, core.space.Space, project._id)
}