mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
TSK-810 Rename Team -> Project, Project -> Component (#2756)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
26adf14966
commit
0f24fe80d6
@ -38,6 +38,7 @@
|
||||
"@hcengineering/account": "^0.6.0",
|
||||
"@hcengineering/core": "^0.6.21",
|
||||
"@hcengineering/contact": "^0.6.11",
|
||||
"@hcengineering/tracker": "^0.6.2",
|
||||
"@hcengineering/model-all": "^0.6.0",
|
||||
"@hcengineering/model-telegram": "^0.6.0",
|
||||
"@hcengineering/telegram": "^0.6.2",
|
||||
|
@ -11,18 +11,18 @@ import core, {
|
||||
TxOperations,
|
||||
WorkspaceId
|
||||
} from '@hcengineering/core'
|
||||
import tracker, { calcRank, Issue, IssuePriority, IssueStatus } from '../../../plugins/tracker/lib'
|
||||
import tracker, { calcRank, Issue, IssuePriority, IssueStatus } from '@hcengineering/tracker'
|
||||
|
||||
import { connect } from './connect'
|
||||
|
||||
let objectId: Ref<Issue> = generateId()
|
||||
const space = tracker.team.DefaultTeam
|
||||
const space = tracker.project.DefaultProject
|
||||
|
||||
const object: AttachedData<Issue> = {
|
||||
title: '',
|
||||
description: '',
|
||||
assignee: null,
|
||||
project: null,
|
||||
component: null,
|
||||
sprint: null,
|
||||
number: 0,
|
||||
rank: '',
|
||||
@ -67,7 +67,7 @@ export async function generateIssues (
|
||||
async function genIssue (client: TxOperations): Promise<void> {
|
||||
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,
|
||||
{
|
||||
@ -79,7 +79,7 @@ async function genIssue (client: TxOperations): Promise<void> {
|
||||
title: faker.name.title(),
|
||||
description: faker.lorem.paragraphs(),
|
||||
assignee: object.assignee,
|
||||
project: object.project,
|
||||
component: object.component,
|
||||
sprint: object.sprint,
|
||||
number: (incResult as any).object.sequence,
|
||||
status: object.status,
|
||||
|
@ -34,10 +34,10 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTracker.trigger.OnProjectRemove
|
||||
trigger: serverTracker.trigger.OnComponentRemove
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverTracker.trigger.OnTeamDelete
|
||||
trigger: serverTracker.trigger.OnProjectDelete
|
||||
})
|
||||
}
|
||||
|
@ -64,13 +64,13 @@ import {
|
||||
IssueStatusCategory,
|
||||
IssueTemplate,
|
||||
IssueTemplateChild,
|
||||
Project,
|
||||
ProjectStatus,
|
||||
Component,
|
||||
ComponentStatus,
|
||||
Scrum,
|
||||
ScrumRecord,
|
||||
Sprint,
|
||||
SprintStatus,
|
||||
Team,
|
||||
Project,
|
||||
TimeReportDayType,
|
||||
TimeSpendReport,
|
||||
trackerId,
|
||||
@ -132,8 +132,8 @@ export class TTypeIssuePriority extends TType {}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function TypeProjectStatus (): Type<ProjectStatus> {
|
||||
return { _class: tracker.class.TypeProjectStatus, label: 'TypeProjectStatus' as IntlString }
|
||||
export function TypeComponentStatus (): Type<ComponentStatus> {
|
||||
return { _class: tracker.class.TypeComponentStatus, label: 'TypeComponentStatus' as IntlString }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,8 +146,8 @@ export function TypeSprintStatus (): Type<SprintStatus> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@Model(tracker.class.TypeProjectStatus, core.class.Type, DOMAIN_MODEL)
|
||||
export class TTypeProjectStatus extends TType {}
|
||||
@Model(tracker.class.TypeComponentStatus, core.class.Type, DOMAIN_MODEL)
|
||||
export class TTypeComponentStatus extends TType {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -158,9 +158,9 @@ export class TTypeSprintStatus extends TType {}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@Model(tracker.class.Team, core.class.Space, DOMAIN_SPACE)
|
||||
@UX(tracker.string.Team, tracker.icon.Team, 'TEAM')
|
||||
export class TTeam extends TSpace implements Team {
|
||||
@Model(tracker.class.Project, core.class.Space, DOMAIN_SPACE)
|
||||
@UX(tracker.string.Project, tracker.icon.Project, 'Project')
|
||||
export class TProject extends TSpace implements Project {
|
||||
@Prop(TypeString(), tracker.string.Title)
|
||||
@Index(IndexKind.FullText)
|
||||
reamLogo!: IntlString
|
||||
@ -226,9 +226,9 @@ export class TIssue extends TAttachedDoc implements Issue {
|
||||
@Index(IndexKind.Indexed)
|
||||
assignee!: Ref<Employee> | null
|
||||
|
||||
@Prop(TypeRef(tracker.class.Project), tracker.string.Project)
|
||||
@Prop(TypeRef(tracker.class.Component), tracker.string.Component)
|
||||
@Index(IndexKind.Indexed)
|
||||
project!: Ref<Project> | null
|
||||
component!: Ref<Component> | null
|
||||
|
||||
@Prop(Collection(tracker.class.Issue), tracker.string.SubIssues)
|
||||
subIssues!: number
|
||||
@ -250,7 +250,7 @@ export class TIssue extends TAttachedDoc implements Issue {
|
||||
@Prop(Collection(tags.class.TagReference), tracker.string.Labels)
|
||||
labels?: number
|
||||
|
||||
declare space: Ref<Team>
|
||||
declare space: Ref<Project>
|
||||
|
||||
@Prop(TypeDate(DateRangeMode.DATETIME), tracker.string.DueDate)
|
||||
dueDate!: Timestamp | null
|
||||
@ -296,13 +296,13 @@ export class TIssueTemplate extends TDoc implements IssueTemplate {
|
||||
@Prop(TypeRef(contact.class.Employee), tracker.string.Assignee)
|
||||
assignee!: Ref<Employee> | null
|
||||
|
||||
@Prop(TypeRef(tracker.class.Project), tracker.string.Project)
|
||||
project!: Ref<Project> | null
|
||||
@Prop(TypeRef(tracker.class.Component), tracker.string.Component)
|
||||
component!: Ref<Component> | null
|
||||
|
||||
@Prop(ArrOf(TypeRef(tags.class.TagElement)), tracker.string.Labels)
|
||||
labels?: Ref<TagElement>[]
|
||||
|
||||
declare space: Ref<Team>
|
||||
declare space: Ref<Project>
|
||||
|
||||
@Prop(TypeDate(DateRangeMode.DATETIME), tracker.string.DueDate)
|
||||
dueDate!: Timestamp | null
|
||||
@ -351,9 +351,9 @@ export class TTimeSpendReport extends TAttachedDoc implements TimeSpendReport {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@Model(tracker.class.Project, core.class.Doc, DOMAIN_TRACKER)
|
||||
@UX(tracker.string.Project, tracker.icon.Project, 'PROJECT')
|
||||
export class TProject extends TDoc implements Project {
|
||||
@Model(tracker.class.Component, core.class.Doc, DOMAIN_TRACKER)
|
||||
@UX(tracker.string.Component, tracker.icon.Component, 'COMPONENT')
|
||||
export class TComponent extends TDoc implements Component {
|
||||
@Prop(TypeString(), tracker.string.Title)
|
||||
// @Index(IndexKind.FullText)
|
||||
label!: string
|
||||
@ -364,10 +364,10 @@ export class TProject extends TDoc implements Project {
|
||||
@Prop(TypeString(), tracker.string.AssetLabel)
|
||||
icon!: Asset
|
||||
|
||||
@Prop(TypeProjectStatus(), tracker.string.Status)
|
||||
status!: ProjectStatus
|
||||
@Prop(TypeComponentStatus(), tracker.string.Status)
|
||||
status!: ComponentStatus
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), tracker.string.ProjectLead)
|
||||
@Prop(TypeRef(contact.class.Employee), tracker.string.ComponentLead)
|
||||
lead!: Ref<Employee> | null
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.class.Employee)), tracker.string.Members)
|
||||
@ -385,7 +385,7 @@ export class TProject extends TDoc implements Project {
|
||||
@Prop(TypeDate(DateRangeMode.DATETIME), tracker.string.TargetDate)
|
||||
targetDate!: Timestamp | null
|
||||
|
||||
declare space: Ref<Team>
|
||||
declare space: Ref<Project>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -405,7 +405,7 @@ export class TSprint extends TDoc implements Sprint {
|
||||
@Index(IndexKind.Indexed)
|
||||
status!: SprintStatus
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), tracker.string.ProjectLead)
|
||||
@Prop(TypeRef(contact.class.Employee), tracker.string.ComponentLead)
|
||||
lead!: Ref<Employee> | null
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.class.Employee)), tracker.string.Members)
|
||||
@ -423,14 +423,14 @@ export class TSprint extends TDoc implements Sprint {
|
||||
@Prop(TypeDate(), tracker.string.TargetDate)
|
||||
targetDate!: Timestamp
|
||||
|
||||
declare space: Ref<Team>
|
||||
declare space: Ref<Project>
|
||||
|
||||
@Prop(TypeNumber(), tracker.string.Capacity)
|
||||
capacity!: number
|
||||
|
||||
@Prop(TypeRef(tracker.class.Project), tracker.string.Project)
|
||||
@Prop(TypeRef(tracker.class.Component), tracker.string.Component)
|
||||
@Index(IndexKind.Indexed)
|
||||
project!: Ref<Project>
|
||||
component!: Ref<Component>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -460,7 +460,7 @@ export class TScrum extends TDoc implements Scrum {
|
||||
@Prop(TypeDate(DateRangeMode.TIME), tracker.string.ScrumEndTime)
|
||||
endTime!: Timestamp
|
||||
|
||||
declare space: Ref<Team>
|
||||
declare space: Ref<Project>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -485,7 +485,7 @@ export class TScrumRecord extends TAttachedDoc implements ScrumRecord {
|
||||
attachments!: number
|
||||
|
||||
declare attachedTo: Ref<Scrum>
|
||||
declare space: Ref<Team>
|
||||
declare space: Ref<Project>
|
||||
declare scrumRecorder: Ref<EmployeeAccount>
|
||||
}
|
||||
|
||||
@ -495,14 +495,14 @@ export class TTypeReportedTime extends TType {}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TTeam,
|
||||
TProject,
|
||||
TComponent,
|
||||
TIssue,
|
||||
TIssueTemplate,
|
||||
TIssueStatus,
|
||||
TIssueStatusCategory,
|
||||
TTypeIssuePriority,
|
||||
TTypeProjectStatus,
|
||||
TTypeComponentStatus,
|
||||
TSprint,
|
||||
TScrum,
|
||||
TScrumRecord,
|
||||
@ -512,7 +512,7 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
const issuesOptions: ViewOptionsModel = {
|
||||
groupBy: ['status', 'assignee', 'priority', 'project', 'sprint'],
|
||||
groupBy: ['status', 'assignee', 'priority', 'component', 'sprint'],
|
||||
orderBy: [
|
||||
['status', SortingOrder.Ascending],
|
||||
['priority', SortingOrder.Ascending],
|
||||
@ -574,14 +574,14 @@ export function createModel (builder: Builder): void {
|
||||
{ key: '', presenter: tracker.component.DueDatePresenter, props: { kind: 'list' } },
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.ProjectEditor,
|
||||
presenter: tracker.component.ComponentEditor,
|
||||
props: {
|
||||
kind: 'list',
|
||||
size: 'small',
|
||||
shape: 'round',
|
||||
shouldShowPlaceholder: false,
|
||||
listProps: {
|
||||
excludeByKey: 'project',
|
||||
excludeByKey: 'component',
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
@ -702,7 +702,7 @@ export function createModel (builder: Builder): void {
|
||||
attachTo: tracker.class.IssueTemplate,
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: {
|
||||
groupBy: ['assignee', 'priority', 'project', 'sprint'],
|
||||
groupBy: ['assignee', 'priority', 'component', 'sprint'],
|
||||
orderBy: [
|
||||
['priority', SortingOrder.Ascending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
@ -722,7 +722,7 @@ export function createModel (builder: Builder): void {
|
||||
// { key: '', presenter: tracker.component.DueDatePresenter, props: { kind: 'list' } },
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.ProjectEditor,
|
||||
presenter: tracker.component.ComponentEditor,
|
||||
props: { kind: 'list', size: 'small', shape: 'round', shouldShowPlaceholder: false }
|
||||
},
|
||||
{
|
||||
@ -841,7 +841,7 @@ export function createModel (builder: Builder): void {
|
||||
const activeId = 'active'
|
||||
const backlogId = 'backlog'
|
||||
const boardId = 'board'
|
||||
const projectsId = 'projects'
|
||||
const componentsId = 'components'
|
||||
const sprintsId = 'sprints'
|
||||
const templatesId = 'templates'
|
||||
// const scrumsId = 'scrums'
|
||||
@ -902,18 +902,23 @@ export function createModel (builder: Builder): void {
|
||||
presenter: tracker.component.PriorityRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Component, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.ComponentPresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.ProjectPresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Team, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.TeamPresenter
|
||||
})
|
||||
classPresenter(
|
||||
builder,
|
||||
tracker.class.Component,
|
||||
tracker.component.ComponentSelector,
|
||||
tracker.component.ComponentSelector
|
||||
)
|
||||
|
||||
classPresenter(builder, tracker.class.Project, tracker.component.ProjectSelector, tracker.component.ProjectSelector)
|
||||
|
||||
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: tracker.component.ProjectSelector
|
||||
builder.mixin(tracker.class.Component, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: tracker.component.ComponentSelector
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Sprint, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
@ -928,8 +933,8 @@ export function createModel (builder: Builder): void {
|
||||
value: true
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.TypeProjectStatus, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: tracker.component.ProjectStatusEditor
|
||||
builder.mixin(tracker.class.TypeComponentStatus, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: tracker.component.ComponentStatusEditor
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, notification.mixin.LastViewAttached, {})
|
||||
@ -945,8 +950,8 @@ export function createModel (builder: Builder): void {
|
||||
func: tracker.function.GetAllPriority
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.AllValuesFunc, {
|
||||
func: tracker.function.GetAllProjects
|
||||
builder.mixin(tracker.class.Component, core.class.Class, view.mixin.AllValuesFunc, {
|
||||
func: tracker.function.GetAllComponents
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Sprint, core.class.Class, view.mixin.AllValuesFunc, {
|
||||
@ -1001,16 +1006,16 @@ export function createModel (builder: Builder): void {
|
||||
id: 'roadmap',
|
||||
position: 'top',
|
||||
label: tracker.string.Roadmap,
|
||||
icon: tracker.icon.Projects,
|
||||
icon: tracker.icon.Components,
|
||||
component: tracker.component.Roadmap
|
||||
}
|
||||
],
|
||||
spaces: [
|
||||
{
|
||||
label: tracker.string.Teams,
|
||||
spaceClass: tracker.class.Team,
|
||||
addSpaceLabel: tracker.string.CreateTeam,
|
||||
createComponent: tracker.component.CreateTeam,
|
||||
label: tracker.string.Projects,
|
||||
spaceClass: tracker.class.Project,
|
||||
addSpaceLabel: tracker.string.CreateProject,
|
||||
createComponent: tracker.component.CreateProject,
|
||||
icon: tracker.icon.Home,
|
||||
specials: [
|
||||
{
|
||||
@ -1032,10 +1037,10 @@ export function createModel (builder: Builder): void {
|
||||
component: tracker.component.Backlog
|
||||
},
|
||||
{
|
||||
id: projectsId,
|
||||
label: tracker.string.Projects,
|
||||
icon: tracker.icon.Projects,
|
||||
component: tracker.component.TeamProjects
|
||||
id: componentsId,
|
||||
label: tracker.string.Components,
|
||||
icon: tracker.icon.Components,
|
||||
component: tracker.component.ProjectComponents
|
||||
},
|
||||
{
|
||||
id: sprintsId,
|
||||
@ -1069,7 +1074,7 @@ export function createModel (builder: Builder): void {
|
||||
application: trackerId,
|
||||
mode: 'space',
|
||||
spaceSpecial: id,
|
||||
spaceClass: tracker.class.Team
|
||||
spaceClass: tracker.class.Project
|
||||
})
|
||||
}
|
||||
|
||||
@ -1077,7 +1082,7 @@ export function createModel (builder: Builder): void {
|
||||
createGotoSpecialAction(builder, activeId, 'g->a', tracker.string.GotoActive)
|
||||
createGotoSpecialAction(builder, backlogId, 'g->b', tracker.string.GotoBacklog)
|
||||
createGotoSpecialAction(builder, boardId, 'g->d', tracker.string.GotoBoard)
|
||||
createGotoSpecialAction(builder, projectsId, 'g->p', tracker.string.GotoProjects)
|
||||
createGotoSpecialAction(builder, componentsId, 'g->c', tracker.string.GotoComponents)
|
||||
|
||||
createAction(builder, {
|
||||
action: workbench.actionImpl.Navigate,
|
||||
@ -1103,7 +1108,7 @@ export function createModel (builder: Builder): void {
|
||||
icon: view.icon.Statuses,
|
||||
input: 'focus',
|
||||
category: tracker.category.Tracker,
|
||||
target: tracker.class.Team,
|
||||
target: tracker.class.Project,
|
||||
query: {
|
||||
archived: false
|
||||
},
|
||||
@ -1118,12 +1123,12 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: tracker.actionImpl.EditTeam,
|
||||
label: tracker.string.EditTeam,
|
||||
action: tracker.actionImpl.EditProject,
|
||||
label: tracker.string.EditProject,
|
||||
icon: contact.icon.Edit,
|
||||
input: 'focus',
|
||||
category: tracker.category.Tracker,
|
||||
target: tracker.class.Team,
|
||||
target: tracker.class.Project,
|
||||
query: {
|
||||
archived: false
|
||||
},
|
||||
@ -1132,18 +1137,18 @@ export function createModel (builder: Builder): void {
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
tracker.action.EditTeam
|
||||
tracker.action.EditProject
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: tracker.actionImpl.DeleteTeam,
|
||||
label: tracker.string.DeleteTeam,
|
||||
action: tracker.actionImpl.DeleteProject,
|
||||
label: tracker.string.DeleteProject,
|
||||
icon: view.icon.Delete,
|
||||
input: 'focus',
|
||||
category: tracker.category.Tracker,
|
||||
target: tracker.class.Team,
|
||||
target: tracker.class.Project,
|
||||
query: {
|
||||
archived: false
|
||||
},
|
||||
@ -1152,7 +1157,7 @@ export function createModel (builder: Builder): void {
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
tracker.action.DeleteTeam
|
||||
tracker.action.DeleteProject
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
@ -1449,15 +1454,15 @@ export function createModel (builder: Builder): void {
|
||||
action: view.actionImpl.ValueSelector,
|
||||
actionPopup: view.component.ValueSelector,
|
||||
actionProps: {
|
||||
attribute: 'project',
|
||||
_class: tracker.class.Project,
|
||||
attribute: 'component',
|
||||
_class: tracker.class.Component,
|
||||
query: {},
|
||||
searchField: 'label',
|
||||
placeholder: tracker.string.Project
|
||||
placeholder: tracker.string.Component
|
||||
},
|
||||
label: tracker.string.Project,
|
||||
icon: tracker.icon.Project,
|
||||
keyBinding: ['keyP->keyP'],
|
||||
label: tracker.string.Component,
|
||||
icon: tracker.icon.Component,
|
||||
keyBinding: ['keyC'],
|
||||
input: 'any',
|
||||
category: tracker.category.Tracker,
|
||||
target: tracker.class.Issue,
|
||||
@ -1467,7 +1472,7 @@ export function createModel (builder: Builder): void {
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
tracker.action.SetProject
|
||||
tracker.action.SetComponent
|
||||
)
|
||||
|
||||
createAction(
|
||||
@ -1596,7 +1601,7 @@ export function createModel (builder: Builder): void {
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.Move,
|
||||
label: tracker.string.MoveToTeam,
|
||||
label: tracker.string.MoveToProject,
|
||||
icon: view.icon.Move,
|
||||
keyBinding: [],
|
||||
input: 'any',
|
||||
@ -1608,7 +1613,7 @@ export function createModel (builder: Builder): void {
|
||||
group: 'associate'
|
||||
}
|
||||
},
|
||||
tracker.action.MoveToTeam
|
||||
tracker.action.MoveToProject
|
||||
)
|
||||
// TODO: fix icon
|
||||
createAction(
|
||||
@ -1687,7 +1692,7 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
const sprintOptions: ViewOptionsModel = {
|
||||
groupBy: ['project', 'lead'],
|
||||
groupBy: ['component', 'lead'],
|
||||
orderBy: [
|
||||
['startDate', SortingOrder.Descending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
@ -1712,7 +1717,7 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
{ key: '', presenter: tracker.component.SprintPresenter, props: { shouldUseMargin: true } },
|
||||
{ key: '', presenter: view.component.GrowPresenter, props: { type: 'grow' } },
|
||||
{ key: '', presenter: tracker.component.SprintProjectEditor, props: { kind: 'list' } },
|
||||
{ key: '', presenter: tracker.component.SprintComponentEditor, props: { kind: 'list' } },
|
||||
{
|
||||
key: '',
|
||||
presenter: contact.component.MembersPresenter,
|
||||
|
@ -14,16 +14,20 @@
|
||||
//
|
||||
|
||||
import core, {
|
||||
Class,
|
||||
Doc,
|
||||
DocumentUpdate,
|
||||
DOMAIN_TX,
|
||||
generateId,
|
||||
Ref,
|
||||
SortingOrder,
|
||||
TxCreateDoc,
|
||||
TxOperations,
|
||||
TxResult
|
||||
TxResult,
|
||||
TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
|
||||
import { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import tags from '@hcengineering/tags'
|
||||
import {
|
||||
calcRank,
|
||||
@ -31,7 +35,9 @@ import {
|
||||
Issue,
|
||||
IssueStatus,
|
||||
IssueStatusCategory,
|
||||
Team,
|
||||
IssueTemplate,
|
||||
IssueTemplateChild,
|
||||
Project,
|
||||
TimeReportDayType,
|
||||
WorkDayLength
|
||||
} from '@hcengineering/tracker'
|
||||
@ -46,9 +52,9 @@ enum DeprecatedIssueStatus {
|
||||
Canceled
|
||||
}
|
||||
|
||||
interface CreateTeamIssueStatusesArgs {
|
||||
interface CreateProjectIssueStatusesArgs {
|
||||
tx: TxOperations
|
||||
teamId: Ref<Team>
|
||||
projectId: Ref<Project>
|
||||
categories: IssueStatusCategory[]
|
||||
defaultStatusId?: Ref<IssueStatus>
|
||||
defaultCategoryId?: Ref<IssueStatusCategory>
|
||||
@ -62,13 +68,13 @@ const categoryByDeprecatedIssueStatus = {
|
||||
[DeprecatedIssueStatus.Canceled]: tracker.issueStatusCategory.Canceled
|
||||
} as const
|
||||
|
||||
async function createTeamIssueStatuses ({
|
||||
async function createProjectIssueStatuses ({
|
||||
tx,
|
||||
teamId: attachedTo,
|
||||
projectId: attachedTo,
|
||||
categories,
|
||||
defaultStatusId,
|
||||
defaultCategoryId = tracker.issueStatusCategory.Backlog
|
||||
}: CreateTeamIssueStatusesArgs): Promise<void> {
|
||||
}: CreateProjectIssueStatusesArgs): Promise<void> {
|
||||
const issueStatusRanks = [...genRanks(categories.length)]
|
||||
|
||||
for (const [i, statusCategory] of categories.entries()) {
|
||||
@ -79,7 +85,7 @@ async function createTeamIssueStatuses ({
|
||||
tracker.class.IssueStatus,
|
||||
attachedTo,
|
||||
attachedTo,
|
||||
tracker.class.Team,
|
||||
tracker.class.Project,
|
||||
'issueStatuses',
|
||||
{ name: defaultStatusName, category, rank },
|
||||
category === defaultCategoryId ? defaultStatusId : undefined
|
||||
@ -87,13 +93,13 @@ async function createTeamIssueStatuses ({
|
||||
}
|
||||
}
|
||||
|
||||
async function createDefaultTeam (tx: TxOperations): Promise<void> {
|
||||
const current = await tx.findOne(tracker.class.Team, {
|
||||
_id: tracker.team.DefaultTeam
|
||||
async function createDefaultProject (tx: TxOperations): Promise<void> {
|
||||
const current = await tx.findOne(tracker.class.Project, {
|
||||
_id: tracker.project.DefaultProject
|
||||
})
|
||||
|
||||
const currentDeleted = await tx.findOne(core.class.TxRemoveDoc, {
|
||||
objectId: tracker.team.DefaultTeam
|
||||
objectId: tracker.project.DefaultProject
|
||||
})
|
||||
|
||||
// Create new if not deleted by customers.
|
||||
@ -105,12 +111,12 @@ async function createDefaultTeam (tx: TxOperations): Promise<void> {
|
||||
{ sort: { order: SortingOrder.Ascending } }
|
||||
)
|
||||
|
||||
await tx.createDoc<Team>(
|
||||
tracker.class.Team,
|
||||
await tx.createDoc<Project>(
|
||||
tracker.class.Project,
|
||||
core.space.Space,
|
||||
{
|
||||
name: 'Default',
|
||||
description: 'Default team',
|
||||
description: 'Default project',
|
||||
private: false,
|
||||
members: [],
|
||||
archived: false,
|
||||
@ -122,16 +128,16 @@ async function createDefaultTeam (tx: TxOperations): Promise<void> {
|
||||
defaultAssignee: undefined,
|
||||
workDayLength: WorkDayLength.EIGHT_HOURS
|
||||
},
|
||||
tracker.team.DefaultTeam
|
||||
tracker.project.DefaultProject
|
||||
)
|
||||
await createTeamIssueStatuses({ tx, teamId: tracker.team.DefaultTeam, categories, defaultStatusId })
|
||||
await createProjectIssueStatuses({ tx, projectId: tracker.project.DefaultProject, categories, defaultStatusId })
|
||||
}
|
||||
}
|
||||
|
||||
async function fixTeamIssueStatusesOrder (tx: TxOperations, team: Team): Promise<TxResult> {
|
||||
async function fixProjectIssueStatusesOrder (tx: TxOperations, project: Project): Promise<TxResult> {
|
||||
const statuses = await tx.findAll(
|
||||
tracker.class.IssueStatus,
|
||||
{ attachedTo: team._id },
|
||||
{ attachedTo: project._id },
|
||||
{ lookup: { category: tracker.class.IssueStatusCategory } }
|
||||
)
|
||||
statuses.sort((a, b) => (a.$lookup?.category?.order ?? 0) - (b.$lookup?.category?.order ?? 0))
|
||||
@ -143,19 +149,19 @@ async function fixTeamIssueStatusesOrder (tx: TxOperations, team: Team): Promise
|
||||
})
|
||||
}
|
||||
|
||||
async function fixTeamsIssueStatusesOrder (tx: TxOperations): Promise<void> {
|
||||
const teams = await tx.findAll(tracker.class.Team, {})
|
||||
await Promise.all(teams.map((team) => fixTeamIssueStatusesOrder(tx, team)))
|
||||
async function fixProjectsIssueStatusesOrder (tx: TxOperations): Promise<void> {
|
||||
const projects = await tx.findAll(tracker.class.Project, {})
|
||||
await Promise.all(projects.map((project) => fixProjectIssueStatusesOrder(tx, project)))
|
||||
}
|
||||
|
||||
async function upgradeTeamSettings (tx: TxOperations): Promise<void> {
|
||||
const teams = await tx.findAll(tracker.class.Team, {
|
||||
async function upgradeProjectSettings (tx: TxOperations): Promise<void> {
|
||||
const projects = await tx.findAll(tracker.class.Project, {
|
||||
defaultTimeReportDay: { $exists: false },
|
||||
workDayLength: { $exists: false }
|
||||
})
|
||||
await Promise.all(
|
||||
teams.map((team) =>
|
||||
tx.update(team, {
|
||||
projects.map((project) =>
|
||||
tx.update(project, {
|
||||
defaultTimeReportDay: TimeReportDayType.PreviousWorkDay,
|
||||
workDayLength: WorkDayLength.EIGHT_HOURS
|
||||
})
|
||||
@ -163,21 +169,21 @@ async function upgradeTeamSettings (tx: TxOperations): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
async function upgradeTeamIssueStatuses (tx: TxOperations): Promise<void> {
|
||||
const teams = await tx.findAll(tracker.class.Team, { issueStatuses: undefined })
|
||||
async function upgradeProjectIssueStatuses (tx: TxOperations): Promise<void> {
|
||||
const projects = await tx.findAll(tracker.class.Project, { issueStatuses: undefined })
|
||||
|
||||
if (teams.length > 0) {
|
||||
if (projects.length > 0) {
|
||||
const categories = await tx.findAll(
|
||||
tracker.class.IssueStatusCategory,
|
||||
{},
|
||||
{ sort: { order: SortingOrder.Ascending } }
|
||||
)
|
||||
|
||||
for (const team of teams) {
|
||||
for (const project of projects) {
|
||||
const defaultStatusId: Ref<IssueStatus> = generateId()
|
||||
|
||||
await tx.update(team, { issueStatuses: 0, defaultIssueStatus: defaultStatusId })
|
||||
await createTeamIssueStatuses({ tx, teamId: team._id, categories, defaultStatusId })
|
||||
await tx.update(project, { issueStatuses: 0, defaultIssueStatus: defaultStatusId })
|
||||
await createProjectIssueStatuses({ tx, projectId: project._id, categories, defaultStatusId })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,38 +297,38 @@ async function migrateIssueParentInfo (client: MigrationClient): Promise<void> {
|
||||
await updateIssueParentInfo(client, null)
|
||||
}
|
||||
|
||||
async function migrateIssueProjects (client: MigrationClient): Promise<void> {
|
||||
const issues = await client.find(DOMAIN_TRACKER, { _class: tracker.class.Issue, project: { $exists: false } })
|
||||
async function migrateIssueComponents (client: MigrationClient): Promise<void> {
|
||||
const issues = await client.find(DOMAIN_TRACKER, { _class: tracker.class.Issue, component: { $exists: false } })
|
||||
|
||||
if (issues.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const issue of issues) {
|
||||
await client.update(DOMAIN_TRACKER, { _id: issue._id }, { project: null })
|
||||
await client.update(DOMAIN_TRACKER, { _id: issue._id }, { component: null })
|
||||
}
|
||||
}
|
||||
|
||||
async function upgradeProjectIcons (tx: TxOperations): Promise<void> {
|
||||
const projects = await tx.findAll(tracker.class.Project, {})
|
||||
async function upgradeComponentIcons (tx: TxOperations): Promise<void> {
|
||||
const components = await tx.findAll(tracker.class.Component, {})
|
||||
|
||||
if (projects.length === 0) {
|
||||
if (components.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const project of projects) {
|
||||
const icon = project.icon as unknown
|
||||
for (const component of components) {
|
||||
const icon = component.icon as unknown
|
||||
|
||||
if (icon !== undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
await tx.update(project, { icon: tracker.icon.Projects })
|
||||
await tx.update(component, { icon: tracker.icon.Components })
|
||||
}
|
||||
}
|
||||
|
||||
async function createDefaults (tx: TxOperations): Promise<void> {
|
||||
await createDefaultTeam(tx)
|
||||
await createDefaultProject(tx)
|
||||
await createOrUpdate(
|
||||
tx,
|
||||
tags.class.TagCategory,
|
||||
@ -377,10 +383,10 @@ async function fillRank (client: MigrationClient): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function upgradeTeams (tx: TxOperations): Promise<void> {
|
||||
await upgradeTeamIssueStatuses(tx)
|
||||
await fixTeamsIssueStatusesOrder(tx)
|
||||
await upgradeTeamSettings(tx)
|
||||
async function upgradeProjects (tx: TxOperations): Promise<void> {
|
||||
await upgradeProjectIssueStatuses(tx)
|
||||
await fixProjectsIssueStatusesOrder(tx)
|
||||
await upgradeProjectSettings(tx)
|
||||
}
|
||||
|
||||
async function upgradeIssues (tx: TxOperations): Promise<void> {
|
||||
@ -408,8 +414,277 @@ async function upgradeIssues (tx: TxOperations): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function upgradeProjects (tx: TxOperations): Promise<void> {
|
||||
await upgradeProjectIcons(tx)
|
||||
async function upgradeComponents (tx: TxOperations): Promise<void> {
|
||||
await upgradeComponentIcons(tx)
|
||||
}
|
||||
|
||||
async function renameProject (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_TRACKER,
|
||||
{
|
||||
_class: { $in: [tracker.class.Issue, tracker.class.Sprint] },
|
||||
project: { $exists: true }
|
||||
},
|
||||
{
|
||||
$rename: { project: 'component' }
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx._class': core.class.TxCreateDoc,
|
||||
'tx.objectClass': tracker.class.Issue,
|
||||
'tx.attributes.project': { $exists: true }
|
||||
},
|
||||
{
|
||||
$rename: { 'tx.attributes.project': 'tx.attributes.component' }
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx._class': core.class.TxUpdateDoc,
|
||||
'tx.objectClass': tracker.class.Issue,
|
||||
'tx.operations.project': { $exists: true }
|
||||
},
|
||||
{
|
||||
$rename: { 'tx.operations.project': 'tx.operations.component' }
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectClass: tracker.class.Sprint,
|
||||
_class: core.class.TxCreateDoc,
|
||||
'attributes.project': { $exists: true }
|
||||
},
|
||||
{
|
||||
$rename: { 'attributes.project': 'attributes.component' }
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectClass: { $in: [tracker.class.Issue, tracker.class.Sprint] },
|
||||
_class: core.class.TxUpdateDoc,
|
||||
'operations.project': { $exists: true }
|
||||
},
|
||||
{
|
||||
$rename: { 'operations.project': 'operations.component' }
|
||||
}
|
||||
)
|
||||
|
||||
const templates = await client.find<IssueTemplate>(DOMAIN_TRACKER, {
|
||||
_class: tracker.class.IssueTemplate,
|
||||
project: { $exists: true }
|
||||
})
|
||||
for (const template of templates) {
|
||||
const children: IssueTemplateChild[] = template.children.map((p) => {
|
||||
const res = {
|
||||
...p,
|
||||
component: p.component
|
||||
}
|
||||
delete (res as any).project
|
||||
return res
|
||||
})
|
||||
await client.update<IssueTemplate>(
|
||||
DOMAIN_TRACKER,
|
||||
{
|
||||
_id: template._id
|
||||
},
|
||||
{
|
||||
children
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TRACKER,
|
||||
{
|
||||
_id: template._id
|
||||
},
|
||||
{
|
||||
$rename: { project: 'component' }
|
||||
}
|
||||
)
|
||||
const createTxes = await client.find<TxCreateDoc<IssueTemplate>>(DOMAIN_TX, {
|
||||
objectId: template._id,
|
||||
_class: core.class.TxCreateDoc
|
||||
})
|
||||
for (const createTx of createTxes) {
|
||||
const children: IssueTemplateChild[] = createTx.attributes.children.map((p) => {
|
||||
const res = {
|
||||
...p,
|
||||
component: p.component
|
||||
}
|
||||
delete (res as any).project
|
||||
return res
|
||||
})
|
||||
await client.update<TxCreateDoc<IssueTemplate>>(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_id: createTx._id
|
||||
},
|
||||
{
|
||||
children
|
||||
}
|
||||
)
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_id: createTx._id
|
||||
},
|
||||
{
|
||||
$rename: { 'attributes.project': 'attributes.component' }
|
||||
}
|
||||
)
|
||||
}
|
||||
const updateTxes = await client.find<TxUpdateDoc<IssueTemplate>>(DOMAIN_TX, {
|
||||
objectId: template._id,
|
||||
_class: core.class.TxUpdateDoc
|
||||
})
|
||||
for (const updateTx of updateTxes) {
|
||||
if ((updateTx.operations as any).project !== undefined) {
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_id: updateTx._id
|
||||
},
|
||||
{
|
||||
$rename: { 'operations.project': 'operations.component' }
|
||||
}
|
||||
)
|
||||
}
|
||||
if (updateTx.operations.children !== undefined) {
|
||||
const children: IssueTemplateChild[] = updateTx.operations.children.map((p) => {
|
||||
const res = {
|
||||
...p,
|
||||
component: p.component
|
||||
}
|
||||
delete (res as any).project
|
||||
return res
|
||||
})
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_id: updateTx._id
|
||||
},
|
||||
{
|
||||
children
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultSpace = (
|
||||
await client.find<Project>(DOMAIN_SPACE, {
|
||||
_id: 'tracker:team:DefaultTeam' as Ref<Project>
|
||||
})
|
||||
)[0]
|
||||
if (defaultSpace !== undefined) {
|
||||
await client.delete(DOMAIN_SPACE, tracker.project.DefaultProject)
|
||||
await client.create(DOMAIN_SPACE, {
|
||||
...defaultSpace,
|
||||
_id: tracker.project.DefaultProject,
|
||||
_class: tracker.class.Project,
|
||||
description: defaultSpace.description === 'Default team' ? 'Default project' : defaultSpace.description
|
||||
})
|
||||
await client.delete(DOMAIN_SPACE, defaultSpace._id)
|
||||
}
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_id: 'tracker:team:DefaultTeam' as Ref<Project>,
|
||||
_class: 'tracker:class:Team' as Ref<Class<Doc>>
|
||||
},
|
||||
{
|
||||
_id: tracker.project.DefaultProject,
|
||||
_class: tracker.class.Project,
|
||||
description: 'Default project'
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TRACKER,
|
||||
{
|
||||
attachedTo: 'tracker:team:DefaultTeam' as Ref<Doc>
|
||||
},
|
||||
{
|
||||
attachedTo: tracker.project.DefaultProject
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TRACKER,
|
||||
{
|
||||
space: 'tracker:team:DefaultTeam' as Ref<Project>
|
||||
},
|
||||
{
|
||||
space: tracker.project.DefaultProject
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TRACKER,
|
||||
{
|
||||
attachedToClass: 'tracker:class:Team' as Ref<Class<Doc>>
|
||||
},
|
||||
{
|
||||
attachedToClass: tracker.class.Project
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectId: 'tracker:team:DefaultTeam' as Ref<Project>
|
||||
},
|
||||
{
|
||||
objectId: tracker.project.DefaultProject
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectClass: 'tracker:class:Team' as Ref<Class<Doc>>
|
||||
},
|
||||
{
|
||||
objectClass: tracker.class.Project
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
'tx.objectClass': 'tracker:class:Team' as Ref<Class<Doc>>
|
||||
},
|
||||
{
|
||||
'tx.objectClass': tracker.class.Project
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectSpace: 'tracker:team:DefaultTeam' as Ref<Project>
|
||||
},
|
||||
{
|
||||
objectSpace: tracker.project.DefaultProject
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
'tx.objectSpace': 'tracker:team:DefaultTeam' as Ref<Project>
|
||||
},
|
||||
{
|
||||
'tx.objectSpace': tracker.project.DefaultProject
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const trackerOperation: MigrateOperation = {
|
||||
@ -423,15 +698,16 @@ export const trackerOperation: MigrateOperation = {
|
||||
reportedTime: 0
|
||||
}
|
||||
)
|
||||
await Promise.all([migrateIssueProjects(client), migrateParentIssues(client)])
|
||||
await Promise.all([migrateIssueComponents(client), migrateParentIssues(client)])
|
||||
await migrateIssueParentInfo(client)
|
||||
await fillRank(client)
|
||||
await renameProject(client)
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const tx = new TxOperations(client, core.account.System)
|
||||
await createDefaults(tx)
|
||||
await upgradeTeams(tx)
|
||||
await upgradeIssues(tx)
|
||||
await upgradeProjects(tx)
|
||||
await upgradeIssues(tx)
|
||||
await upgradeComponents(tx)
|
||||
}
|
||||
}
|
||||
|
@ -26,12 +26,12 @@ import { Application } from '@hcengineering/workbench'
|
||||
export default mergeIds(trackerId, tracker, {
|
||||
string: {
|
||||
TrackerApplication: '' as IntlString,
|
||||
Teams: '' as IntlString,
|
||||
Projects: '' as IntlString,
|
||||
GotoIssues: '' as IntlString,
|
||||
GotoActive: '' as IntlString,
|
||||
GotoBacklog: '' as IntlString,
|
||||
GotoBoard: '' as IntlString,
|
||||
GotoProjects: '' as IntlString,
|
||||
GotoComponents: '' as IntlString,
|
||||
GotoTrackerApplication: '' as IntlString,
|
||||
SearchIssue: '' as IntlString,
|
||||
Parent: '' as IntlString
|
||||
@ -61,14 +61,14 @@ export default mergeIds(trackerId, tracker, {
|
||||
actionImpl: {
|
||||
CopyToClipboard: '' as ViewAction,
|
||||
EditWorkflowStatuses: '' as ViewAction,
|
||||
EditTeam: '' as ViewAction,
|
||||
DeleteTeam: '' as ViewAction,
|
||||
EditProject: '' as ViewAction,
|
||||
DeleteProject: '' as ViewAction,
|
||||
DeleteSprint: '' as ViewAction
|
||||
},
|
||||
action: {
|
||||
NewRelatedIssue: '' as Ref<Action<Doc, Record<string, any>>>,
|
||||
DeleteSprint: '' as Ref<Action<Doc, Record<string, any>>>,
|
||||
DeleteTeam: '' as Ref<Action<Doc, Record<string, any>>>,
|
||||
DeleteProject: '' as Ref<Action<Doc, Record<string, any>>>,
|
||||
SetSprintLead: '' as Ref<Action<Doc, Record<string, any>>>
|
||||
}
|
||||
})
|
||||
|
@ -362,13 +362,13 @@
|
||||
{#if item.startDate}
|
||||
{@const target = item.targetDate ?? item.startDate + NOT_ENDED}
|
||||
<div
|
||||
class="project-item"
|
||||
class="component-item"
|
||||
class:noTarget={item.targetDate === null}
|
||||
style:left={`${getOffsetByDate(item.startDate)}px`}
|
||||
style:right={`${getOffsetByDate(target) + dayWidth - 1}px`}
|
||||
style:width={`${getOffsetByDate(target) - getOffsetByDate(item.startDate) + dayWidth - 1}px`}
|
||||
>
|
||||
<div class="project-presenter gap-2">
|
||||
<div class="component-presenter gap-2">
|
||||
{#if item.icon}<Icon
|
||||
icon={item.icon}
|
||||
size={item.iconSize ?? 'small'}
|
||||
@ -636,14 +636,14 @@
|
||||
}
|
||||
|
||||
.timeline-action__button,
|
||||
.project-item {
|
||||
.component-item {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
box-shadow: var(--button-shadow);
|
||||
}
|
||||
.project-item {
|
||||
.component-item {
|
||||
top: 0.25rem;
|
||||
bottom: 0.25rem;
|
||||
background-color: var(--button-bg-color);
|
||||
@ -659,7 +659,7 @@
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.project-presenter {
|
||||
.component-presenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@ -766,7 +766,7 @@
|
||||
padding-left: 0.45rem;
|
||||
}
|
||||
|
||||
.projectPresenter {
|
||||
.componentPresenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
@ -82,6 +82,6 @@
|
||||
{width}
|
||||
{justify}
|
||||
title={buttonTitle}
|
||||
icon={tracker.icon.ProjectMembers}
|
||||
icon={tracker.icon.ComponentMembers}
|
||||
on:click={handleMembersEditorOpened}
|
||||
/>
|
||||
|
@ -20,7 +20,14 @@
|
||||
import { Vacancy as VacancyClass } from '@hcengineering/recruit'
|
||||
import tags from '@hcengineering/tags'
|
||||
import task, { createKanban, KanbanTemplate } from '@hcengineering/task'
|
||||
import tracker, { calcRank, Issue, IssueStatus, IssueTemplate, IssueTemplateData, Team } from '@hcengineering/tracker'
|
||||
import tracker, {
|
||||
calcRank,
|
||||
Issue,
|
||||
IssueStatus,
|
||||
IssueTemplate,
|
||||
IssueTemplateData,
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import { Button, Component, createFocusManager, EditBox, FocusHandler, IconAttachment } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
@ -63,7 +70,7 @@
|
||||
|
||||
async function saveIssue (
|
||||
id: Ref<VacancyClass>,
|
||||
space: Ref<Team>,
|
||||
space: Ref<Project>,
|
||||
template: IssueTemplateData,
|
||||
parent: Ref<Issue> = tracker.ids.NoParent
|
||||
): Promise<Ref<Issue>> {
|
||||
@ -73,7 +80,7 @@
|
||||
{ sort: { rank: SortingOrder.Descending } }
|
||||
)
|
||||
const incResult = await client.updateDoc(
|
||||
tracker.class.Team,
|
||||
tracker.class.Project,
|
||||
core.space.Space,
|
||||
space,
|
||||
{
|
||||
@ -81,16 +88,16 @@
|
||||
},
|
||||
true
|
||||
)
|
||||
const team = await client.findOne(tracker.class.Team, { _id: space })
|
||||
const project = await client.findOne(tracker.class.Project, { _id: space })
|
||||
const rank = calcRank(lastOne, undefined)
|
||||
const resId = await client.addCollection(tracker.class.Issue, space, parent, tracker.class.Issue, 'subIssues', {
|
||||
title: template.title + ` (${name})`,
|
||||
description: template.description,
|
||||
assignee: template.assignee,
|
||||
project: template.project,
|
||||
component: template.component,
|
||||
sprint: template.sprint,
|
||||
number: (incResult as any).object.sequence,
|
||||
status: team?.defaultIssueStatus as Ref<IssueStatus>,
|
||||
status: project?.defaultIssueStatus as Ref<IssueStatus>,
|
||||
priority: template.priority,
|
||||
rank,
|
||||
comments: 0,
|
||||
|
@ -22,7 +22,7 @@
|
||||
<symbol id="stop" viewBox="-4 -4 16 16">
|
||||
<rect width="8" height="8"/>
|
||||
</symbol>
|
||||
<symbol id="project" viewBox="0 0 16 16">
|
||||
<symbol id="component" viewBox="0 0 16 16">
|
||||
<path d="M11.7,3.6h-1.1C10.4,2.7,9.7,2,8.8,2H7.2C6.3,2,5.6,2.7,5.4,3.6H4.3C3,3.6,2,4.6,2,5.9V8c0,0.2,0.1,0.3,0.2,0.4 C3.8,9.3,5.9,9.9,8,9.9c2.1,0,4.2-0.5,5.8-1.5C13.9,8.3,14,8.2,14,8V5.9C14,4.6,13,3.6,11.7,3.6z M7.2,3h1.5c0.4,0,0.6,0.2,0.8,0.6 H6.5C6.6,3.2,6.9,3,7.2,3z M13,7.7c-1.4,0.8-3.2,1.2-5,1.2c-1.8,0-3.6-0.4-5-1.2V5.9c0-0.7,0.6-1.3,1.3-1.3h7.4 c0.7,0,1.3,0.6,1.3,1.3V7.7z"/>
|
||||
<path d="M13.5,9.7c-0.3,0-0.5,0.2-0.5,0.5l-0.1,1.5c-0.1,0.8-0.7,1.3-1.4,1.3H4.5c-0.7,0-1.4-0.6-1.4-1.3L3,10.1 c0-0.3-0.3-0.5-0.5-0.5C2.2,9.7,2,9.9,2,10.2l0.1,1.5C2.2,13,3.3,14,4.5,14h6.9c1.3,0,2.3-1,2.4-2.3l0.1-1.5 C14,9.9,13.8,9.7,13.5,9.7z"/>
|
||||
</symbol>
|
||||
@ -30,7 +30,7 @@
|
||||
<path d="M13.3,8.3c-0.1,2.8-2.5,5.1-5.4,5.1C5,13.4,2.6,11,2.6,8c0-2.9,2.3-5.2,5.1-5.4c0.1-0.4,0.2-0.7,0.4-1c0,0-0.1,0-0.1,0 C4.4,1.7,1.6,4.5,1.6,8c0,3.5,2.9,6.4,6.4,6.4s6.4-2.9,6.4-6.4c0,0,0-0.1,0-0.1C14,8.1,13.7,8.2,13.3,8.3z"/>
|
||||
<ellipse cx="12.1" cy="3.9" rx="2.5" ry="2.5"/>
|
||||
</symbol>
|
||||
<symbol id="team" viewBox="0 0 24 24">
|
||||
<symbol id="project" viewBox="0 0 24 24">
|
||||
<path d="M13,1h-2.2H9.4H6.6H5.2H3C1.9,1,1,1.9,1,3v10c0,1.1,0.9,2,2,2h2.2h1.4h2.8h1.4H13c1.1,0,2-0.9,2-2V3C15,1.9,14.1,1,13,1z M3,13.8c-0.4,0-0.8-0.4-0.8-0.8V3c0-0.4,0.4-0.8,0.8-0.8h2.2v11.6H3z M6.6,13.8V2.2h2.8v11.6H6.6z M13.8,13c0,0.4-0.4,0.8-0.8,0.8 h-2.2V2.2H13c0.4,0,0.8,0.4,0.8,0.8V13z"/>
|
||||
</symbol>
|
||||
<symbol id="document" viewBox="0 0 16 16">
|
||||
@ -50,7 +50,7 @@
|
||||
<symbol id="issues" viewBox="0 0 16 16">
|
||||
<path d="M12.5 11.2204C13.3829 10.8346 13.9999 9.95362 13.9999 8.92853V4.5C13.9999 3.11929 12.8806 2 11.4999 2H7.07132C6.04623 2 5.16524 2.61697 4.77942 3.49983L10 3.49983C11.3807 3.49983 12.5 4.61912 12.5 5.99983V11.2204ZM4.5 13.9998C3.11929 13.9998 2 12.8805 2 11.4998V7.07126C2 5.69055 3.11929 4.57126 4.5 4.57126L8.92853 4.57126C10.3092 4.57126 11.4285 5.69055 11.4285 7.07126V11.4998C11.4285 12.8805 10.3092 13.9998 8.92853 13.9998H4.5ZM8 9.49979C8 10.3282 7.32843 10.9998 6.5 10.9998C5.67157 10.9998 5 10.3282 5 9.49979C5 8.67136 5.67157 7.99979 6.5 7.99979C7.32843 7.99979 8 8.67136 8 9.49979ZM9.5 9.49979C9.5 11.1566 8.15685 12.4998 6.5 12.4998C4.84315 12.4998 3.5 11.1566 3.5 9.49979C3.5 7.84293 4.84315 6.49979 6.5 6.49979C8.15685 6.49979 9.5 7.84293 9.5 9.49979Z"/>
|
||||
</symbol>
|
||||
<symbol id="projects" viewBox="-3 -3 16 16">
|
||||
<symbol id="components" viewBox="-3 -3 16 16">
|
||||
<path d="M3.45455 0H0.545455C0.244208 0 0 0.244208 0 0.545455V3.45455C0 3.75579 0.244208 4 0.545455 4H3.45455C3.75579 4 4 3.75579 4 3.45455V0.545455C4 0.244208 3.75579 0 3.45455 0Z"/>
|
||||
<path d="M9.95479 0H7.0457C6.74445 0 6.50024 0.244208 6.50024 0.545455V3.45455C6.50024 3.75579 6.74445 4 7.0457 4H9.95479C10.256 4 10.5002 3.75579 10.5002 3.45455V0.545455C10.5002 0.244208 10.256 0 9.95479 0Z"/>
|
||||
<path d="M3.45455 6.50024H0.545455C0.244208 6.50024 0 6.74445 0 7.0457V9.95479C0 10.256 0.244208 10.5002 0.545455 10.5002H3.45455C3.75579 10.5002 4 10.256 4 9.95479V7.0457C4 6.74445 3.75579 6.50024 3.45455 6.50024Z"/>
|
||||
@ -125,12 +125,12 @@
|
||||
<rect x="1" y="5.75" width="9" height="2.5" rx="0.5" />
|
||||
<rect x="4" y="10.5" width="9" height="2.5" rx="0.5" />
|
||||
</symbol>
|
||||
<symbol id="projectMembers" viewBox="0 0 16 16">
|
||||
<symbol id="componentMembers" viewBox="0 0 16 16">
|
||||
<path d="M1 3C1 1.89543 1.89543 1 3 1H9C10.1046 1 11 1.89543 11 3V3.5H6C4.61929 3.5 3.5 4.61929 3.5 6V11H3C1.89543 11 1 10.1046 1 9V3Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C5.89543 5 5 5.89543 5 7V13C5 14.1046 5.89543 15 7 15H13C14.1046 15 15 14.1046 15 13V7C15 5.89543 14.1046 5 13 5H7ZM10 10C10.9665 10 11.5 9.2165 11.5 8.25C11.5 7.2835 10.9665 6.5 10 6.5C9.0335 6.5 8.5 7.2835 8.5 8.25C8.5 9.2165 9.0335 10 10 10ZM7 12.5616C7 11.5144 7.9841 10.746 9 11C9.47572 11.7136 10.5243 11.7136 11 11C12.0159 10.746 13 11.5144 13 12.5616V13.0101C13 13.2806 12.7806 13.5 12.5101 13.5H7.48995C7.21936 13.5 7 13.2806 7 13.0101V12.5616Z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="project-status-backlog" viewBox="1 1 14 14" fill="#F2994A">
|
||||
<symbol id="component-status-backlog" viewBox="1 1 14 14" fill="#F2994A">
|
||||
<path d="M2 4.74695C2 4.68722 2.01039 4.62899 2.02989 4.57451L2.11601 4.42269C2.15266 4.37819 2.19711 4.33975 2.24806 4.30966L3.16473 3.76824L3.92054 5.08013L3.5 5.32852V5.8313H2V4.74695Z" />
|
||||
<path d="M4.8372 4.53871L4.0814 3.22682L5.91473 2.14398L6.67054 3.45588L4.8372 4.53871Z" />
|
||||
<path d="M7.5872 2.91446L6.8314 1.60257L7.74806 1.06115C7.7997 1.03065 7.85539 1.01027 7.91244 1H8.08756C8.14461 1.01027 8.2003 1.03065 8.25194 1.06115L9.1686 1.60257L8.4128 2.91446L8 2.67065L7.5872 2.91446Z" />
|
||||
@ -144,23 +144,23 @@
|
||||
<path d="M3.92054 10.9199L3.16473 12.2318L2.24806 11.6903C2.19711 11.6602 2.15266 11.6218 2.11601 11.5773L2.02989 11.4255C2.01039 11.371 2 11.3128 2 11.253V10.1687H3.5V10.6715L3.92054 10.9199Z" />
|
||||
<path d="M3.5 9.08435H2V6.91565H3.5V9.08435Z" />
|
||||
</symbol>
|
||||
<symbol id="project-status-planned" viewBox="1 1 14 14" fill="#D7D8DB">
|
||||
<symbol id="component-status-planned" viewBox="1 1 14 14" fill="#D7D8DB">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 5.36133L8 2.73633L3.5 5.36133L3.5 10.6382L8 13.2632L12.5 10.6382L12.5 5.36133ZM8.75581 1.44066C8.28876 1.16822 7.71124 1.16822 7.24419 1.44066L2.74419 4.06566C2.28337 4.33448 2 4.82783 2 5.36133V10.6382C2 11.1717 2.28337 11.6651 2.74419 11.9339L7.24419 14.5589C7.71124 14.8313 8.28876 14.8313 8.75581 14.5589L13.2558 11.9339C13.7166 11.6651 14 11.1717 14 10.6382V5.36133C14 4.82783 13.7166 4.33448 13.2558 4.06566L8.75581 1.44066Z" />
|
||||
</symbol>
|
||||
<symbol id="project-status-in-progress" viewBox="1 1 14 14" fill="#F2C94C">
|
||||
<symbol id="component-status-in-progress" viewBox="1 1 14 14" fill="#F2C94C">
|
||||
<path d="M8.3779 4.74233C8.14438 4.60607 7.85562 4.60607 7.6221 4.74233L5.37209 6.05513C5.14168 6.18957 5 6.4363 5 6.70311V9.34216C5 9.60897 5.14168 9.85573 5.37209 9.99016L7.6221 11.303C7.85562 11.4392 8.14438 11.4392 8.3779 11.303L10.6279 9.99016C10.8583 9.85573 11 9.60897 11 9.34216V6.70311C11 6.4363 10.8583 6.18957 10.6279 6.05513L8.3779 4.74233Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.24419 1.44066C7.71124 1.16822 8.28876 1.16822 8.75581 1.44066L13.2558 4.06566C13.7166 4.33448 14 4.82783 14 5.36133V10.6382C14 11.1717 13.7166 11.6651 13.2558 11.9339L8.75581 14.5589C8.28876 14.8313 7.71124 14.8313 7.24419 14.5589L2.74419 11.9339C2.28337 11.6651 2 11.1717 2 10.6382V5.36133C2 4.82783 2.28337 4.33448 2.74419 4.06566L7.24419 1.44066ZM8 2.73633L12.5 5.36133V10.6382L8 13.2632L3.5 10.6382V5.36133L8 2.73633Z" />
|
||||
</symbol>
|
||||
<symbol id="project-status-paused" viewBox="1 1 14 14" fill="#8A8F98">
|
||||
<symbol id="component-status-paused" viewBox="1 1 14 14" fill="#8A8F98">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.75581 1.21148C8.28876 0.929507 7.71124 0.929507 7.24419 1.21148L2.74419 3.92829C2.28337 4.20651 2 4.71711 2 5.26927V10.7307C2 11.2829 2.28337 11.7935 2.74419 12.0717L7.24419 14.7885C7.71124 15.0705 8.28876 15.0705 8.75581 14.7885L13.2558 12.0717C13.7166 11.7935 14 11.2829 14 10.7307V5.26927C14 4.71711 13.7166 4.20651 13.2558 3.92829L8.75581 1.21148ZM12.5 5.26928L8 2.55246L3.5 5.26927L3.5 10.7307L8 13.4475L12.5 10.7307L12.5 5.26928Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.5 5.75C6.91421 5.75 7.25 6.08579 7.25 6.5V9.5C7.25 9.91421 6.91421 10.25 6.5 10.25C6.08579 10.25 5.75 9.91421 5.75 9.5V6.5C5.75 6.08579 6.08579 5.75 6.5 5.75Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 5.75C9.91421 5.75 10.25 6.08579 10.25 6.5V9.5C10.25 9.91421 9.91421 10.25 9.5 10.25C9.08579 10.25 8.75 9.91421 8.75 9.5V6.5C8.75 6.08579 9.08579 5.75 9.5 5.75Z" />
|
||||
</symbol>
|
||||
<symbol id="project-status-completed" viewBox="1 1 14 14" fill="#5E6AD2">
|
||||
<symbol id="component-status-completed" viewBox="1 1 14 14" fill="#5E6AD2">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 5.125L8 2.5L3.5 5.125L3.5 10.4019L8 13.0269L12.5 10.4019L12.5 5.125ZM8.75581 1.20433C8.28876 0.93189 7.71124 0.931889 7.24419 1.20433L2.74419 3.82933C2.28337 4.09815 2 4.5915 2 5.125V10.4019C2 10.9354 2.28337 11.4287 2.74419 11.6976L7.24419 14.3226C7.71124 14.595 8.28876 14.595 8.75581 14.3226L13.2558 11.6976C13.7166 11.4287 14 10.9354 14 10.4019V5.125C14 4.5915 13.7166 4.09815 13.2558 3.82933L8.75581 1.20433Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.7381 5.69424C11.0526 5.96381 11.089 6.43728 10.8194 6.75178L7.81944 10.2518C7.68349 10.4104 7.48754 10.5051 7.27878 10.5131C7.07003 10.5212 6.86739 10.4417 6.71967 10.294L5.21967 8.79402C4.92678 8.50112 4.92678 8.02625 5.21967 7.73336C5.51256 7.44046 5.98744 7.44046 6.28033 7.73336L7.20764 8.66066L9.68056 5.77559C9.95012 5.4611 10.4236 5.42468 10.7381 5.69424Z" />
|
||||
</symbol>
|
||||
<symbol id="project-status-canceled" viewBox="1 1 14 14" fill="#8A8F98">
|
||||
<symbol id="component-status-canceled" viewBox="1 1 14 14" fill="#8A8F98">
|
||||
<path d="M5.96967 5.96967C6.26256 5.67678 6.73744 5.67678 7.03033 5.96967L8 6.93934L8.96967 5.96967C9.26256 5.67678 9.73744 5.67678 10.0303 5.96967C10.3232 6.26256 10.3232 6.73744 10.0303 7.03033L9.06066 8L10.0303 8.96967C10.3232 9.26256 10.3232 9.73744 10.0303 10.0303C9.73744 10.3232 9.26256 10.3232 8.96967 10.0303L8 9.06066L7.03033 10.0303C6.73744 10.3232 6.26256 10.3232 5.96967 10.0303C5.67678 9.73744 5.67678 9.26256 5.96967 8.96967L6.93934 8L5.96967 7.03033C5.67678 6.73744 5.67678 6.26256 5.96967 5.96967Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.75581 1.21148C8.28876 0.929507 7.71124 0.929507 7.24419 1.21148L2.74419 3.92829C2.28337 4.20651 2 4.71711 2 5.26927V10.7307C2 11.2829 2.28337 11.7935 2.74419 12.0717L7.24419 14.7885C7.71124 15.0705 8.28876 15.0705 8.75581 14.7885L13.2558 12.0717C13.7166 11.7935 14 11.2829 14 10.7307V5.26927C14 4.71711 13.7166 4.20651 13.2558 3.92829L8.75581 1.21148ZM12.5 5.26928L8 2.55246L3.5 5.26927L3.5 10.7307L8 13.4475L12.5 10.7307L12.5 5.26928Z" />
|
||||
</symbol>
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"string": {
|
||||
"TrackerApplication": "Tracker",
|
||||
"Teams": "Your teams",
|
||||
"Projects": "Your projects",
|
||||
"More": "More",
|
||||
"Default": "Default",
|
||||
"MakeDefault": "Make default",
|
||||
@ -20,18 +20,18 @@
|
||||
"BacklogIssues": "Backlog",
|
||||
"Backlog": "Backlog",
|
||||
"Board": "Board",
|
||||
"Projects": "Projects",
|
||||
"AllProjects": "All",
|
||||
"BacklogProjects": "Backlog",
|
||||
"ActiveProjects": "Active",
|
||||
"ClosedProjects": "Closed",
|
||||
"NewProject": "New project",
|
||||
"CreateProject": "Create project",
|
||||
"ProjectNamePlaceholder": "Project name",
|
||||
"ProjectDescriptionPlaceholder": "Description (optional)",
|
||||
"ProjectStatusPlaceholder": "Change project status...",
|
||||
"ProjectLead": "Lead",
|
||||
"ProjectMembers": "Members",
|
||||
"Components": "Components",
|
||||
"AllComponents": "All",
|
||||
"BacklogComponents": "Backlog",
|
||||
"ActiveComponents": "Active",
|
||||
"ClosedComponents": "Closed",
|
||||
"NewComponent": "New component",
|
||||
"CreateComponent": "Create component",
|
||||
"ComponentNamePlaceholder": "Component name",
|
||||
"ComponentDescriptionPlaceholder": "Description (optional)",
|
||||
"ComponentStatusPlaceholder": "Change component status...",
|
||||
"ComponentLead": "Lead",
|
||||
"ComponentMembers": "Members",
|
||||
"StartDate": "Start date",
|
||||
"TargetDate": "Target date",
|
||||
"Planned": "Planned",
|
||||
@ -39,10 +39,10 @@
|
||||
"Paused": "Paused",
|
||||
"Completed": "Completed",
|
||||
"Canceled": "Canceled",
|
||||
"CreateTeam": "Create team",
|
||||
"NewTeam": "New team",
|
||||
"TeamTitlePlaceholder": "Team title",
|
||||
"TeamIdentifierPlaceholder": "Team ID",
|
||||
"CreateProject": "Create project",
|
||||
"NewProject": "New project",
|
||||
"ProjectTitlePlaceholder": "Project title",
|
||||
"ProjectIdentifierPlaceholder": "Project ID",
|
||||
"ChooseIcon": "Choose icon",
|
||||
"AddIssue": "Add Issue",
|
||||
"NewIssue": "New issue",
|
||||
@ -90,12 +90,12 @@
|
||||
"Comments": "Comments",
|
||||
"Attachments": "Attachments",
|
||||
"Labels": "Labels",
|
||||
"Project": "Project",
|
||||
"Component": "Component",
|
||||
"Space": "",
|
||||
"SetDueDate": "Set due date\u2026",
|
||||
"ChangeDueDate": "Change due date\u2026",
|
||||
"ModificationDate": "Updated {value}",
|
||||
"Team": "Team",
|
||||
"Project": "Project",
|
||||
"Issue": "Issue",
|
||||
"Document": "",
|
||||
"DocumentIcon": "",
|
||||
@ -108,8 +108,8 @@
|
||||
"AddIssueTooltip": "Add issue...",
|
||||
"NewIssueDialogClose": "Do you want to close this dialog?",
|
||||
"NewIssueDialogCloseNote": "All changes will be lost",
|
||||
"RemoveProjectDialogClose": "Delete the project?",
|
||||
"RemoveProjectDialogCloseNote": "Are you sure you want to delete this project? This operation cannot be undone",
|
||||
"RemoveComponentDialogClose": "Delete the component?",
|
||||
"RemoveComponentDialogCloseNote": "Are you sure you want to delete this component? This operation cannot be undone",
|
||||
"DueDatePopupTitle": "Due on {value}",
|
||||
"DueDatePopupOverdueTitle": "Was due on {value}",
|
||||
"DueDatePopupDescription": "{value, plural, =0 {Today} =1 {Tomorrow} other {# days remaining}}",
|
||||
@ -130,22 +130,22 @@
|
||||
"CopyIssueBranch": "Copy Git branch name to clipboard",
|
||||
"CopyIssueTitle": "Copy Issue title to clipboard",
|
||||
"AssetLabel": "Asset",
|
||||
"AddToProject": "Add to project\u2026",
|
||||
"MoveToProject": "Move to project\u2026",
|
||||
"NoProject": "No project",
|
||||
"ProjectLeadTitle": "Project lead",
|
||||
"ProjectMembersTitle": "Project members",
|
||||
"ProjectLeadSearchPlaceholder": "Set project lead\u2026",
|
||||
"ProjectMembersSearchPlaceholder": "Change project members\u2026",
|
||||
"AddToComponent": "Add to component\u2026",
|
||||
"MoveToComponent": "Move to component\u2026",
|
||||
"NoComponent": "No component",
|
||||
"ComponentLeadTitle": "Component lead",
|
||||
"ComponentMembersTitle": "Component members",
|
||||
"ComponentLeadSearchPlaceholder": "Set component lead\u2026",
|
||||
"ComponentMembersSearchPlaceholder": "Change component members\u2026",
|
||||
"Roadmap": "Roadmap",
|
||||
"MoveToTeam": "Move to team",
|
||||
"MoveToProject": "Move to project",
|
||||
"Duplicate": "Duplicate",
|
||||
|
||||
"GotoIssues": "Go to issues",
|
||||
"GotoActive": "Go to active issues",
|
||||
"GotoBacklog": "Go to backlog",
|
||||
"GotoBoard": "Go to issue board",
|
||||
"GotoProjects": "Go to projects",
|
||||
"GotoComponents": "Go to components",
|
||||
"GotoTrackerApplication": "Switch to Tracker Application",
|
||||
|
||||
"Filter": "Filter",
|
||||
@ -174,11 +174,11 @@
|
||||
|
||||
"EditIssue": "Edit {title}",
|
||||
"EditWorkflowStatuses": "Edit issue statuses",
|
||||
"EditTeam": "Edit team",
|
||||
"DeleteTeam": "Delete team",
|
||||
"DeleteTeamName": "Delete team {name}?",
|
||||
"TeamHasIssues": "There are existing issues in this team, are you sure that you want to delete? Both the team and the issues will be deleted.",
|
||||
"ManageWorkflowStatuses": "Manage issue statuses within team",
|
||||
"EditProject": "Edit project",
|
||||
"DeleteProject": "Delete project",
|
||||
"DeleteProjectName": "Delete project {name}?",
|
||||
"ProjectHasIssues": "There are existing issues in this project, are you sure that you want to delete? Both the project and the issues will be deleted.",
|
||||
"ManageWorkflowStatuses": "Manage issue statuses within project",
|
||||
"AddWorkflowStatus": "Add issue status",
|
||||
"EditWorkflowStatus": "Edit issue status",
|
||||
"DeleteWorkflowStatus": "Delete issue status",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"string": {
|
||||
"TrackerApplication": "Трекер",
|
||||
"Teams": "Команды",
|
||||
"Projects": "Проекты",
|
||||
"More": "Больше",
|
||||
"Default": "По умолчанию",
|
||||
"MakeDefault": "Установить по умолчанию",
|
||||
@ -20,18 +20,18 @@
|
||||
"BacklogIssues": "Пул задач",
|
||||
"Backlog": "Пул задач",
|
||||
"Board": "Канбан",
|
||||
"Projects": "Проекты",
|
||||
"AllProjects": "Все",
|
||||
"BacklogProjects": "Пул задач",
|
||||
"ActiveProjects": "Активные",
|
||||
"ClosedProjects": "Закрытые",
|
||||
"NewProject": "Новый проект",
|
||||
"CreateProject": "Создать проект",
|
||||
"ProjectNamePlaceholder": "Название проекта",
|
||||
"ProjectDescriptionPlaceholder": "Описание (необязательно)",
|
||||
"ProjectStatusPlaceholder": "Сменить статус проекта...",
|
||||
"ProjectLead": "Руководитель",
|
||||
"ProjectMembers": "Участники",
|
||||
"Components": "Компоненты",
|
||||
"AllComponents": "Все",
|
||||
"BacklogComponents": "Пул задач",
|
||||
"ActiveComponents": "Активные",
|
||||
"ClosedComponents": "Закрытые",
|
||||
"NewComponent": "Новый компонент",
|
||||
"CreateComponent": "Создать компонент",
|
||||
"ComponentNamePlaceholder": "Название компонента",
|
||||
"ComponentDescriptionPlaceholder": "Описание (необязательно)",
|
||||
"ComponentStatusPlaceholder": "Сменить статус компонента...",
|
||||
"ComponentLead": "Руководитель",
|
||||
"ComponentMembers": "Участники",
|
||||
"StartDate": "Дата начала",
|
||||
"TargetDate": "Дата завершения",
|
||||
"Planned": "Запланирован",
|
||||
@ -39,10 +39,10 @@
|
||||
"Paused": "Приостановлен",
|
||||
"Completed": "Завершен",
|
||||
"Canceled": "Отменено",
|
||||
"CreateTeam": "Создать команду",
|
||||
"NewTeam": "Новая команда",
|
||||
"TeamTitlePlaceholder": "Название команды",
|
||||
"TeamIdentifierPlaceholder": "Идентификатор Команды",
|
||||
"CreateProject": "Создать проект",
|
||||
"NewProject": "Новый проект",
|
||||
"ProjectTitlePlaceholder": "Название проекта",
|
||||
"ProjectIdentifierPlaceholder": "Идентификатор проекта",
|
||||
"ChooseIcon": "Выбрать иконку",
|
||||
"AddIssue": "Добавить задачу",
|
||||
"NewIssue": "Новая задача",
|
||||
@ -90,12 +90,12 @@
|
||||
"Comments": "Комментарии",
|
||||
"Attachments": "Вложения",
|
||||
"Labels": "Метки",
|
||||
"Project": "Проект",
|
||||
"Component": "компонент",
|
||||
"Space": "",
|
||||
"SetDueDate": "Указать срок выполнения\u2026",
|
||||
"ChangeDueDate": "Изменить срок выполнения\u2026",
|
||||
"ModificationDate": "Изменено {value}",
|
||||
"Team": "",
|
||||
"Project": "Проект",
|
||||
"Issue": "Задача",
|
||||
"Document": "",
|
||||
"DocumentIcon": "",
|
||||
@ -108,8 +108,8 @@
|
||||
"AddIssueTooltip": "Добавить задачу\u2026",
|
||||
"NewIssueDialogClose": "Вы действительно хотите закрыть окно?",
|
||||
"NewIssueDialogCloseNote": "Все внесенные изменения будут потеряны",
|
||||
"RemoveProjectDialogClose": "Удалить проект?",
|
||||
"RemoveProjectDialogCloseNote": "Уверены, что хотите удалить этот проект? Эта операция не может быть отменена",
|
||||
"RemoveComponentDialogClose": "Удалить компонент?",
|
||||
"RemoveComponentDialogCloseNote": "Уверены, что хотите удалить этот компонент? Эта операция не может быть отменена",
|
||||
"DueDatePopupTitle": "Срок {value}",
|
||||
"DueDatePopupOverdueTitle": "Должна была завершится {value}",
|
||||
"DueDatePopupDescription": "{value, plural, =0 {Сегодня} =1 {Завтра} other {# дней осталось}}",
|
||||
@ -130,22 +130,22 @@
|
||||
"CopyIssueBranch": "Копировать имя ветки Git в буфер обмена",
|
||||
"CopyIssueTitle": "Копировать имя задачи в буфер обмена",
|
||||
"AssetLabel": "Asset",
|
||||
"AddToProject": "Добавить в проект\u2026",
|
||||
"MoveToProject": "Переместить в проект\u2026",
|
||||
"NoProject": "Без проекта",
|
||||
"ProjectLeadTitle": "Руководитель проекта",
|
||||
"ProjectMembersTitle": "Участники проекта",
|
||||
"ProjectLeadSearchPlaceholder": "Назначьте руководителя проекта\u2026",
|
||||
"ProjectMembersSearchPlaceholder": "Измененить участников проекта\u2026",
|
||||
"AddToComponent": "Добавить в компонент\u2026",
|
||||
"MoveToComponent": "Переместить в компонент\u2026",
|
||||
"NoComponent": "Без компонента",
|
||||
"ComponentLeadTitle": "Руководитель компонента",
|
||||
"ComponentMembersTitle": "Участники компонента",
|
||||
"ComponentLeadSearchPlaceholder": "Назначьте руководителя компонента\u2026",
|
||||
"ComponentMembersSearchPlaceholder": "Измененить участников компонента\u2026",
|
||||
"Roadmap": "Планирование",
|
||||
"MoveToTeam": "Изменить команду",
|
||||
"MoveToProject": "Изменить проект",
|
||||
"Duplicate": "Дублировать",
|
||||
|
||||
"GotoIssues": "Перейти к задачам",
|
||||
"GotoActive": "Перейти к активным задачам",
|
||||
"GotoBacklog": "Перейти к пулу задач",
|
||||
"GotoBoard": "Перейти к канбану",
|
||||
"GotoProjects": "Перейти к проекту",
|
||||
"GotoComponents": "Перейти к компоненту",
|
||||
"GotoTrackerApplication": "Перейти к приложению Трекер",
|
||||
|
||||
"Filter": "Фильтр",
|
||||
@ -174,10 +174,10 @@
|
||||
|
||||
"EditIssue": "Редактирование {title}",
|
||||
"EditWorkflowStatuses": "Редактировать статусы задач",
|
||||
"EditTeam": "Редактировать команду",
|
||||
"DeleteTeam": "Удалить команду",
|
||||
"DeleteTeamName": "Удалить команду {name}?",
|
||||
"TeamHasIssues": "Для данной команды существуют задачи, уверены, что хотите удалить? Задачи и команда будут удалены.",
|
||||
"EditProject": "Редактировать проект",
|
||||
"DeleteProject": "Удалить проект",
|
||||
"DeleteProjectName": "Удалить проект {name}?",
|
||||
"ProjectHasIssues": "Для данного проекта существуют задачи, уверены, что хотите удалить? Задачи и проект будут удалены.",
|
||||
"ManageWorkflowStatuses": "Управлять статусами задач для команды",
|
||||
"AddWorkflowStatus": "Добавить статус задачи",
|
||||
"EditWorkflowStatus": "Редактировать статус задачи",
|
||||
|
@ -19,15 +19,15 @@ import tracker, { trackerId } from '@hcengineering/tracker'
|
||||
const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||
loadMetadata(tracker.icon, {
|
||||
TrackerApplication: `${icons}#tracker`,
|
||||
Project: `${icons}#project`,
|
||||
Component: `${icons}#component`,
|
||||
Issue: `${icons}#issue`,
|
||||
Team: `${icons}#team`,
|
||||
Project: `${icons}#project`,
|
||||
Document: `${icons}#document`,
|
||||
Inbox: `${icons}#inbox`,
|
||||
MyIssues: `${icons}#myissues`,
|
||||
Views: `${icons}#views`,
|
||||
Issues: `${icons}#myissues`,
|
||||
Projects: `${icons}#projects`,
|
||||
Components: `${icons}#components`,
|
||||
NewIssue: `${icons}#newissue`,
|
||||
Magnifier: `${icons}#magnifier`,
|
||||
Home: `${icons}#home`,
|
||||
@ -51,22 +51,22 @@ loadMetadata(tracker.icon, {
|
||||
PriorityMedium: `${icons}#priority-medium`,
|
||||
PriorityLow: `${icons}#priority-low`,
|
||||
|
||||
ProjectsList: `${icons}#list`,
|
||||
ProjectsTimeline: `${icons}#timeline`,
|
||||
ProjectMembers: `${icons}#projectMembers`,
|
||||
ComponentsList: `${icons}#list`,
|
||||
ComponentsTimeline: `${icons}#timeline`,
|
||||
ComponentMembers: `${icons}#componentMembers`,
|
||||
|
||||
ProjectStatusBacklog: `${icons}#project-status-backlog`,
|
||||
ProjectStatusPlanned: `${icons}#project-status-planned`,
|
||||
ProjectStatusInProgress: `${icons}#project-status-in-progress`,
|
||||
ProjectStatusPaused: `${icons}#project-status-paused`,
|
||||
ProjectStatusCompleted: `${icons}#project-status-completed`,
|
||||
ProjectStatusCanceled: `${icons}#project-status-canceled`,
|
||||
ComponentStatusBacklog: `${icons}#component-status-backlog`,
|
||||
ComponentStatusPlanned: `${icons}#component-status-planned`,
|
||||
ComponentStatusInProgress: `${icons}#component-status-in-progress`,
|
||||
ComponentStatusPaused: `${icons}#component-status-paused`,
|
||||
ComponentStatusCompleted: `${icons}#component-status-completed`,
|
||||
ComponentStatusCanceled: `${icons}#component-status-canceled`,
|
||||
|
||||
SprintStatusPlanned: `${icons}#project-status-planned`,
|
||||
SprintStatusInProgress: `${icons}#project-status-in-progress`,
|
||||
SprintStatusPaused: `${icons}#project-status-paused`,
|
||||
SprintStatusCompleted: `${icons}#project-status-completed`,
|
||||
SprintStatusCanceled: `${icons}#project-status-canceled`,
|
||||
SprintStatusPlanned: `${icons}#component-status-planned`,
|
||||
SprintStatusInProgress: `${icons}#component-status-in-progress`,
|
||||
SprintStatusPaused: `${icons}#component-status-paused`,
|
||||
SprintStatusCompleted: `${icons}#component-status-completed`,
|
||||
SprintStatusCanceled: `${icons}#component-status-canceled`,
|
||||
|
||||
CopyID: `${icons}#copyID`,
|
||||
CopyURL: `${icons}#copyURL`,
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user