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

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-03-17 12:17:53 +06:00 committed by GitHub
parent 26adf14966
commit 0f24fe80d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 1646 additions and 1324 deletions

View File

@ -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",

View File

@ -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,

View File

@ -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
})
}

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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>>>
}
})

View File

@ -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;

View File

@ -82,6 +82,6 @@
{width}
{justify}
title={buttonTitle}
icon={tracker.icon.ProjectMembers}
icon={tracker.icon.ComponentMembers}
on:click={handleMembersEditorOpened}
/>

View File

@ -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,

View File

@ -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

View File

@ -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",

View File

@ -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": "Редактировать статус задачи",

View File

@ -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`,

View File

@ -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
>

View File

@ -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 />

View File

@ -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')

View File

@ -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 }

View File

@ -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

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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}

View File

@ -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 />

View File

@ -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}

View File

@ -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}
/>

View File

@ -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;

View File

@ -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

View File

@ -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}

View File

@ -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;

View File

@ -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) => {

View File

@ -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>

View File

@ -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>

View File

@ -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} />

View File

@ -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}

View File

@ -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

View File

@ -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 />

View File

@ -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 }} />

View File

@ -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} />

View File

@ -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()

View File

@ -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>

View File

@ -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) {

View File

@ -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> = {}

View File

@ -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

View File

@ -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">

View File

@ -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}

View File

@ -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

View File

@ -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>

View File

@ -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'),

View File

@ -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={{

View File

@ -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">

View File

@ -30,7 +30,7 @@
(result) => {
documents = result
},
{ lookup: isIssue ? { space: tracker.class.Team } : {} }
{ lookup: isIssue ? { space: tracker.class.Project } : {} }
)
async function handleClick (issue: RelatedDocument) {

View File

@ -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()

View File

@ -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">

View File

@ -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} />

View File

@ -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}

View File

@ -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) => {

View File

@ -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 }]
}
}

View File

@ -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>

View File

@ -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) }
}),

View File

@ -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) }
}),

View File

@ -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">

View File

@ -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}

View File

@ -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}

View File

@ -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

View File

@ -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>

View File

@ -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)
)

View File

@ -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

View File

@ -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}

View File

@ -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(

View File

@ -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">

View File

@ -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} />

View File

@ -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>

View File

@ -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}

View File

@ -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 />

View File

@ -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

View File

@ -27,7 +27,7 @@
const TRACKED_OBJECTS = [
tracker.class.Issue,
tracker.class.IssueTemplate,
tracker.class.Project,
tracker.class.Component,
tracker.class.Sprint
] as const

View File

@ -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

View File

@ -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 () => {

View File

@ -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}

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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
},

View File

@ -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}

View File

@ -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'

View File

@ -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}

View File

@ -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>

View File

@ -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}

View File

@ -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)}>

View File

@ -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}

View File

@ -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) {

View File

@ -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)}>

View File

@ -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}

View File

@ -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>

View File

@ -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">

View File

@ -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 }

View File

@ -25,7 +25,7 @@ import {
} from '@hcengineering/core'
import { Resources, translate } from '@hcengineering/platform'
import { getClient, MessageBox, ObjectSearchResult } from '@hcengineering/presentation'
import { Issue, Scrum, ScrumRecord, Sprint, Team } from '@hcengineering/tracker'
import { Issue, Scrum, ScrumRecord, Sprint, Project } from '@hcengineering/tracker'
import { showPopup } from '@hcengineering/ui'
import CreateIssue from './components/CreateIssue.svelte'
import Inbox from './components/inbox/Inbox.svelte'
@ -52,24 +52,24 @@ import TitlePresenter from './components/issues/TitlePresenter.svelte'
import MyIssues from './components/myissues/MyIssues.svelte'
import NewIssueHeader from './components/NewIssueHeader.svelte'
import NopeComponent from './components/NopeComponent.svelte'
import EditProject from './components/projects/EditProject.svelte'
import IconPresenter from './components/projects/IconPresenter.svelte'
import LeadPresenter from './components/projects/LeadPresenter.svelte'
import ProjectEditor from './components/projects/ProjectEditor.svelte'
import ProjectPresenter from './components/projects/ProjectPresenter.svelte'
import Projects from './components/projects/Projects.svelte'
import ProjectStatusEditor from './components/projects/ProjectStatusEditor.svelte'
import ProjectStatusPresenter from './components/projects/ProjectStatusPresenter.svelte'
import ProjectTitlePresenter from './components/projects/ProjectTitlePresenter.svelte'
import Roadmap from './components/projects/Roadmap.svelte'
import TargetDatePresenter from './components/projects/TargetDatePresenter.svelte'
import TeamProjects from './components/projects/TeamProjects.svelte'
import EditComponent from './components/components/EditComponent.svelte'
import IconPresenter from './components/components/IconComponent.svelte'
import LeadPresenter from './components/components/LeadPresenter.svelte'
import ComponentEditor from './components/components/ComponentEditor.svelte'
import ComponentPresenter from './components/components/ComponentPresenter.svelte'
import Components from './components/components/Components.svelte'
import ComponentStatusEditor from './components/components/ComponentStatusEditor.svelte'
import ComponentStatusPresenter from './components/components/ComponentStatusPresenter.svelte'
import ComponentTitlePresenter from './components/components/ComponentTitlePresenter.svelte'
import Roadmap from './components/components/Roadmap.svelte'
import TargetDatePresenter from './components/components/TargetDatePresenter.svelte'
import ProjectComponents from './components/components/ProjectComponents.svelte'
import RelationsPopup from './components/RelationsPopup.svelte'
import SetDueDateActionPopup from './components/SetDueDateActionPopup.svelte'
import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.svelte'
import SprintDatePresenter from './components/sprints/SprintDatePresenter.svelte'
import SprintLeadPresenter from './components/sprints/SprintLeadPresenter.svelte'
import SprintProjectEditor from './components/sprints/SprintProjectEditor.svelte'
import SprintComponentEditor from './components/sprints/SprintComponentEditor.svelte'
import CreateIssueTemplate from './components/templates/CreateIssueTemplate.svelte'
import Views from './components/views/Views.svelte'
import Statuses from './components/workflow/Statuses.svelte'
@ -103,7 +103,7 @@ import TimeSpendReport from './components/issues/timereport/TimeSpendReport.svel
import RelatedIssues from './components/issues/related/RelatedIssues.svelte'
import RelatedIssueTemplates from './components/issues/related/RelatedIssueTemplates.svelte'
import ProjectSelector from './components/ProjectSelector.svelte'
import ComponentSelector from './components/ComponentSelector.svelte'
import IssueTemplatePresenter from './components/templates/IssueTemplatePresenter.svelte'
import IssueTemplates from './components/templates/IssueTemplates.svelte'
@ -114,13 +114,13 @@ import EditIssueTemplate from './components/templates/EditIssueTemplate.svelte'
import TemplateEstimationEditor from './components/templates/EstimationEditor.svelte'
import {
getAllPriority,
getAllProjects,
getAllComponents,
getAllSprints,
getAllStatuses,
issuePrioritySort,
issueStatusSort,
moveIssuesToAnotherSprint,
removeTeam,
removeProject,
sprintSort,
subIssueQuery
} from './utils'
@ -128,11 +128,11 @@ import {
import { EmployeeAccount } from '@hcengineering/contact'
import StatusRefPresenter from './components/issues/StatusRefPresenter.svelte'
import TimeSpendReportPopup from './components/issues/timereport/TimeSpendReportPopup.svelte'
import DeleteProjectPresenter from './components/projects/DeleteProjectPresenter.svelte'
import DeleteComponentPresenter from './components/components/DeleteComponentPresenter.svelte'
import IssueStatistics from './components/sprints/IssueStatistics.svelte'
import SprintRefPresenter from './components/sprints/SprintRefPresenter.svelte'
import CreateTeam from './components/teams/CreateTeam.svelte'
import TeamPresenter from './components/teams/TeamPresenter.svelte'
import CreateProject from './components/projects/CreateProject.svelte'
import ProjectPresenter from './components/projects/ProjectPresenter.svelte'
export { default as SubIssueList } from './components/issues/edit/SubIssueList.svelte'
@ -142,7 +142,7 @@ export async function queryIssue<D extends Issue> (
search: string,
filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }
): Promise<ObjectSearchResult[]> {
const teams = await client.findAll<Team>(tracker.class.Team, {})
const projects = await client.findAll<Project>(tracker.class.Project, {})
const q: DocumentQuery<Issue> = { title: { $like: `%${search}%` } }
if (filter?.in !== undefined || filter?.nin !== undefined) {
@ -158,13 +158,13 @@ export async function queryIssue<D extends Issue> (
const named = toIdMap(
await client.findAll<Issue>(_class, q, {
limit: 200,
lookup: { space: tracker.class.Team }
lookup: { space: tracker.class.Project }
})
)
for (const currentTeam of teams) {
for (const currentProject of projects) {
const nids: number[] = []
for (let n = 0; n <= currentTeam.sequence; n++) {
const v = `${currentTeam.identifier}-${n}`
for (let n = 0; n <= currentProject.sequence; n++) {
const v = `${currentProject.identifier}-${n}`
if (v.includes(search)) {
nids.push(n)
}
@ -174,7 +174,7 @@ export async function queryIssue<D extends Issue> (
if (q._id !== undefined) {
q2._id = q._id
}
const numbered = await client.findAll<Issue>(_class, q2, { limit: 200, lookup: { space: tracker.class.Team } })
const numbered = await client.findAll<Issue>(_class, q2, { limit: 200, lookup: { space: tracker.class.Project } })
for (const d of numbered) {
if (!named.has(d._id)) {
named.set(d._id, d)
@ -185,45 +185,45 @@ export async function queryIssue<D extends Issue> (
return Array.from(named.values()).map((e) => ({
doc: e,
title: getIssueId(e.$lookup?.space as Team, e),
title: getIssueId(e.$lookup?.space as Project, e),
icon: tracker.icon.TrackerApplication,
component: IssueItem
}))
}
async function editWorkflowStatuses (team: Team | undefined): Promise<void> {
if (team !== undefined) {
showPopup(Statuses, { teamId: team._id, teamClass: team._class }, 'float')
async function editWorkflowStatuses (project: Project | undefined): Promise<void> {
if (project !== undefined) {
showPopup(Statuses, { projectId: project._id, projectClass: project._class }, 'float')
}
}
async function editTeam (team: Team | undefined): Promise<void> {
if (team !== undefined) {
showPopup(CreateTeam, { team })
async function editProject (project: Project | undefined): Promise<void> {
if (project !== undefined) {
showPopup(CreateProject, { project })
}
}
async function deleteTeam (team: Team | undefined): Promise<void> {
if (team !== undefined) {
async function deleteProject (project: Project | undefined): Promise<void> {
if (project !== undefined) {
const client = getClient()
const anyIssue = await client.findOne(tracker.class.Issue, { space: team._id })
const anyIssue = await client.findOne(tracker.class.Issue, { space: project._id })
if (anyIssue !== undefined) {
showPopup(
MessageBox,
{
label: tracker.string.DeleteTeamName,
labelProps: { name: team.name },
message: tracker.string.TeamHasIssues
label: tracker.string.DeleteProjectName,
labelProps: { name: project.name },
message: tracker.string.ProjectHasIssues
},
undefined,
(result?: boolean) => {
if (result === true) {
void removeTeam(team)
void removeProject(project)
}
}
)
} else {
await removeTeam(team)
await removeProject(project)
}
}
}
@ -353,18 +353,18 @@ export default async (): Promise<Resources> => ({
Backlog,
Inbox,
MyIssues,
Projects,
Components,
Views,
IssuePresenter,
ProjectPresenter,
ProjectTitlePresenter,
ComponentPresenter,
ComponentTitlePresenter,
TitlePresenter,
ModificationDatePresenter,
PriorityPresenter,
PriorityEditor,
PriorityRefPresenter,
SprintRefPresenter,
ProjectEditor,
ComponentEditor,
StatusPresenter,
StatusEditor,
AssigneePresenter,
@ -374,14 +374,14 @@ export default async (): Promise<Resources> => ({
IconPresenter,
LeadPresenter,
TargetDatePresenter,
ProjectStatusPresenter,
ProjectStatusEditor,
ComponentStatusPresenter,
ComponentStatusEditor,
SetDueDateActionPopup,
SetParentIssueActionPopup,
EditProject,
EditComponent,
IssuesView,
KanbanView,
TeamProjects,
ProjectComponents,
Roadmap,
IssuePreview,
RelationsPopup,
@ -401,20 +401,20 @@ export default async (): Promise<Resources> => ({
SubIssuesSelector,
RelatedIssues,
RelatedIssueTemplates,
ProjectSelector,
ComponentSelector,
IssueTemplates,
IssueTemplatePresenter,
EditIssueTemplate,
TemplateEstimationEditor,
CreateTeam,
TeamPresenter,
CreateProject,
ProjectPresenter,
IssueStatistics,
StatusRefPresenter,
RelatedIssuesSection,
RelatedIssueSelector,
DeleteProjectPresenter,
DeleteComponentPresenter,
TimeSpendReportPopup,
SprintProjectEditor,
SprintComponentEditor,
SprintDatePresenter,
SprintLeadPresenter
},
@ -434,14 +434,14 @@ export default async (): Promise<Resources> => ({
SubIssueQuery: subIssueQuery,
GetAllStatuses: getAllStatuses,
GetAllPriority: getAllPriority,
GetAllProjects: getAllProjects,
GetAllComponents: getAllComponents,
GetAllSprints: getAllSprints
},
actionImpl: {
EditWorkflowStatuses: editWorkflowStatuses,
EditTeam: editTeam,
EditProject: editProject,
DeleteSprint: deleteSprint,
DeleteTeam: deleteTeam
DeleteProject: deleteProject
},
resolver: {
Location: resolveLocation

Some files were not shown because too many files have changed in this diff Show More