mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-03 08:57:14 +03:00
UBERF-5339 (#4566)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
7b61f3d652
commit
b1268d6c17
@ -11,7 +11,7 @@ import core, {
|
|||||||
TxOperations,
|
TxOperations,
|
||||||
WorkspaceId
|
WorkspaceId
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import tracker, { Issue, IssuePriority, IssueStatus } from '@hcengineering/tracker'
|
import tracker, { Issue, IssuePriority, IssueStatus, Project } from '@hcengineering/tracker'
|
||||||
|
|
||||||
import { connect } from './connect'
|
import { connect } from './connect'
|
||||||
import { calcRank } from '@hcengineering/task'
|
import { calcRank } from '@hcengineering/task'
|
||||||
@ -38,6 +38,7 @@ const object: AttachedData<Issue> = {
|
|||||||
estimation: 0,
|
estimation: 0,
|
||||||
reports: 0,
|
reports: 0,
|
||||||
childInfo: [],
|
childInfo: [],
|
||||||
|
identifier: '',
|
||||||
kind: tracker.taskTypes.Issue
|
kind: tracker.taskTypes.Issue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,13 +83,15 @@ async function genIssue (client: TxOperations, statuses: Ref<IssueStatus>[]): Pr
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
const project = (incResult as any).object as Project
|
||||||
|
const number = project.sequence
|
||||||
const value: AttachedData<Issue> = {
|
const value: AttachedData<Issue> = {
|
||||||
title: faker.commerce.productName(),
|
title: faker.commerce.productName(),
|
||||||
description: faker.lorem.paragraphs(),
|
description: faker.lorem.paragraphs(),
|
||||||
assignee: object.assignee,
|
assignee: object.assignee,
|
||||||
component: object.component,
|
component: object.component,
|
||||||
milestone: object.milestone,
|
milestone: object.milestone,
|
||||||
number: (incResult as any).object.sequence,
|
number,
|
||||||
status: faker.random.arrayElement(statuses),
|
status: faker.random.arrayElement(statuses),
|
||||||
priority: faker.random.arrayElement(Object.values(IssuePriority)) as IssuePriority,
|
priority: faker.random.arrayElement(Object.values(IssuePriority)) as IssuePriority,
|
||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
@ -102,6 +105,7 @@ async function genIssue (client: TxOperations, statuses: Ref<IssueStatus>[]): Pr
|
|||||||
reports: 0,
|
reports: 0,
|
||||||
relations: [],
|
relations: [],
|
||||||
childInfo: [],
|
childInfo: [],
|
||||||
|
identifier: `${project.identifier}-${number}`,
|
||||||
kind: tracker.taskTypes.Issue
|
kind: tracker.taskTypes.Issue
|
||||||
}
|
}
|
||||||
await client.addCollection(
|
await client.addCollection(
|
||||||
|
@ -178,14 +178,16 @@ async function genApplicant (
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const applicantId = `vacancy-${vacancyId}-${candidateId}` as Ref<Applicant>
|
const applicantId = `vacancy-${vacancyId}-${candidateId}` as Ref<Applicant>
|
||||||
|
|
||||||
|
const number = faker.datatype.number()
|
||||||
const applicant: AttachedData<Applicant> = {
|
const applicant: AttachedData<Applicant> = {
|
||||||
number: faker.datatype.number(),
|
number,
|
||||||
assignee: faker.random.arrayElement(emoloyeeIds),
|
assignee: faker.random.arrayElement(emoloyeeIds),
|
||||||
status: faker.random.arrayElement(states),
|
status: faker.random.arrayElement(states),
|
||||||
rank,
|
rank,
|
||||||
startDate: null,
|
startDate: null,
|
||||||
dueDate: null,
|
dueDate: null,
|
||||||
kind: recruit.taskTypes.Applicant
|
kind: recruit.taskTypes.Applicant,
|
||||||
|
identifier: `APP-${number}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update or create candidate
|
// Update or create candidate
|
||||||
|
@ -85,7 +85,7 @@ export class TCommonBoardPreference extends TPreference implements CommonBoardPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Model(board.class.Card, task.class.Task)
|
@Model(board.class.Card, task.class.Task)
|
||||||
@UX(board.string.Card, board.icon.Card, undefined, 'title')
|
@UX(board.string.Card, board.icon.Card, 'CARD', 'title')
|
||||||
export class TCard extends TTask implements Card {
|
export class TCard extends TTask implements Card {
|
||||||
@Prop(TypeString(), board.string.Title)
|
@Prop(TypeString(), board.string.Title)
|
||||||
@Index(IndexKind.FullText)
|
@Index(IndexKind.FullText)
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { boardId } from '@hcengineering/board'
|
import { type Card, boardId } from '@hcengineering/board'
|
||||||
import { type Ref, TxOperations } from '@hcengineering/core'
|
import { type Ref, TxOperations } from '@hcengineering/core'
|
||||||
import {
|
import {
|
||||||
type MigrateOperation,
|
type MigrateOperation,
|
||||||
@ -23,7 +23,7 @@ import {
|
|||||||
tryMigrate
|
tryMigrate
|
||||||
} from '@hcengineering/model'
|
} from '@hcengineering/model'
|
||||||
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||||
import { createProjectType, createSequence, fixTaskTypes } from '@hcengineering/model-task'
|
import { DOMAIN_TASK, createProjectType, createSequence, fixTaskTypes } from '@hcengineering/model-task'
|
||||||
import tags from '@hcengineering/tags'
|
import tags from '@hcengineering/tags'
|
||||||
import task, { type ProjectType } from '@hcengineering/task'
|
import task, { type ProjectType } from '@hcengineering/task'
|
||||||
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||||
@ -126,6 +126,19 @@ async function createDefaults (tx: TxOperations): Promise<void> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function migrateIdentifiers (client: MigrationClient): Promise<void> {
|
||||||
|
const docs = await client.find<Card>(DOMAIN_TASK, { _class: board.class.Card, identifier: { $exists: false } })
|
||||||
|
for (const doc of docs) {
|
||||||
|
await client.update(
|
||||||
|
DOMAIN_TASK,
|
||||||
|
{ _id: doc._id },
|
||||||
|
{
|
||||||
|
identifier: `CARD-${doc.number}`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const boardOperation: MigrateOperation = {
|
export const boardOperation: MigrateOperation = {
|
||||||
async migrate (client: MigrationClient): Promise<void> {
|
async migrate (client: MigrationClient): Promise<void> {
|
||||||
await tryMigrate(client, boardId, [
|
await tryMigrate(client, boardId, [
|
||||||
@ -162,6 +175,10 @@ export const boardOperation: MigrateOperation = {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
state: 'identifier',
|
||||||
|
func: migrateIdentifiers
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
@ -67,7 +67,7 @@ export class TFunnel extends TProject implements Funnel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Model(lead.class.Lead, task.class.Task)
|
@Model(lead.class.Lead, task.class.Task)
|
||||||
@UX(lead.string.Lead, lead.icon.Lead, undefined, 'title')
|
@UX(lead.string.Lead, lead.icon.Lead, 'LEAD', 'title')
|
||||||
export class TLead extends TTask implements Lead {
|
export class TLead extends TTask implements Lead {
|
||||||
@Prop(TypeRef(contact.class.Contact), lead.string.Customer)
|
@Prop(TypeRef(contact.class.Contact), lead.string.Customer)
|
||||||
@ReadOnly()
|
@ReadOnly()
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import { TxOperations } from '@hcengineering/core'
|
import { TxOperations } from '@hcengineering/core'
|
||||||
import { leadId } from '@hcengineering/lead'
|
import { type Lead, leadId } from '@hcengineering/lead'
|
||||||
import {
|
import {
|
||||||
tryMigrate,
|
tryMigrate,
|
||||||
tryUpgrade,
|
tryUpgrade,
|
||||||
@ -23,7 +23,7 @@ import {
|
|||||||
type MigrationUpgradeClient
|
type MigrationUpgradeClient
|
||||||
} from '@hcengineering/model'
|
} from '@hcengineering/model'
|
||||||
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||||
import task, { createProjectType, createSequence, fixTaskTypes } from '@hcengineering/model-task'
|
import task, { DOMAIN_TASK, createProjectType, createSequence, fixTaskTypes } from '@hcengineering/model-task'
|
||||||
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||||
import lead from './plugin'
|
import lead from './plugin'
|
||||||
|
|
||||||
@ -121,6 +121,19 @@ async function createDefaults (tx: TxOperations): Promise<void> {
|
|||||||
await createSequence(tx, lead.class.Lead)
|
await createSequence(tx, lead.class.Lead)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function migrateIdentifiers (client: MigrationClient): Promise<void> {
|
||||||
|
const docs = await client.find<Lead>(DOMAIN_TASK, { _class: lead.class.Lead, identifier: { $exists: false } })
|
||||||
|
for (const doc of docs) {
|
||||||
|
await client.update(
|
||||||
|
DOMAIN_TASK,
|
||||||
|
{ _id: doc._id },
|
||||||
|
{
|
||||||
|
identifier: `LEAD-${doc.number}`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const leadOperation: MigrateOperation = {
|
export const leadOperation: MigrateOperation = {
|
||||||
async migrate (client: MigrationClient): Promise<void> {
|
async migrate (client: MigrationClient): Promise<void> {
|
||||||
await tryMigrate(client, leadId, [
|
await tryMigrate(client, leadId, [
|
||||||
@ -157,6 +170,10 @@ export const leadOperation: MigrateOperation = {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
state: 'identifier',
|
||||||
|
func: migrateIdentifiers
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
@ -24,8 +24,8 @@ import {
|
|||||||
type MigrationUpgradeClient
|
type MigrationUpgradeClient
|
||||||
} from '@hcengineering/model'
|
} from '@hcengineering/model'
|
||||||
import tags, { type TagCategory } from '@hcengineering/model-tags'
|
import tags, { type TagCategory } from '@hcengineering/model-tags'
|
||||||
import { createProjectType, createSequence, fixTaskTypes } from '@hcengineering/model-task'
|
import { DOMAIN_TASK, createProjectType, createSequence, fixTaskTypes } from '@hcengineering/model-task'
|
||||||
import { recruitId } from '@hcengineering/recruit'
|
import { type Applicant, recruitId } from '@hcengineering/recruit'
|
||||||
import task, { type ProjectType } from '@hcengineering/task'
|
import task, { type ProjectType } from '@hcengineering/task'
|
||||||
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||||
import recruit from './plugin'
|
import recruit from './plugin'
|
||||||
@ -67,6 +67,10 @@ export const recruitOperation: MigrateOperation = {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
state: 'identifier',
|
||||||
|
func: migrateIdentifiers
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
@ -89,6 +93,22 @@ export const recruitOperation: MigrateOperation = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function migrateIdentifiers (client: MigrationClient): Promise<void> {
|
||||||
|
const docs = await client.find<Applicant>(DOMAIN_TASK, {
|
||||||
|
_class: recruit.class.Applicant,
|
||||||
|
identifier: { $exists: false }
|
||||||
|
})
|
||||||
|
for (const doc of docs) {
|
||||||
|
await client.update(
|
||||||
|
DOMAIN_TASK,
|
||||||
|
{ _id: doc._id },
|
||||||
|
{
|
||||||
|
identifier: `APP-${doc.number}`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function createDefaults (tx: TxOperations): Promise<void> {
|
async function createDefaults (tx: TxOperations): Promise<void> {
|
||||||
await createSpaces(tx)
|
await createSpaces(tx)
|
||||||
|
|
||||||
|
@ -60,10 +60,7 @@ export function createModel (builder: Builder): void {
|
|||||||
component: contact.component.Avatar,
|
component: contact.component.Avatar,
|
||||||
props: [{ avatar: ['attachedTo', 'avatar'] }, { name: ['attachedTo', 'name'] }]
|
props: [{ avatar: ['attachedTo', 'avatar'] }, { name: ['attachedTo', 'name'] }]
|
||||||
},
|
},
|
||||||
shortTitle: {
|
shortTitle: 'identifier',
|
||||||
tmpl: 'APP-{number}',
|
|
||||||
props: ['number']
|
|
||||||
},
|
|
||||||
title: {
|
title: {
|
||||||
tmpl: '{name} - {vacName}',
|
tmpl: '{name} - {vacName}',
|
||||||
props: [{ _class: ['attachedTo', '_class'] }, { name: ['attachedTo', 'name'] }, { vacName: ['space', 'name'] }]
|
props: [{ _class: ['attachedTo', '_class'] }, { name: ['attachedTo', 'name'] }, { vacName: ['space', 'name'] }]
|
||||||
|
@ -42,10 +42,7 @@ export function createModel (builder: Builder): void {
|
|||||||
component: tracker.component.IssueSearchIcon,
|
component: tracker.component.IssueSearchIcon,
|
||||||
props: ['status', 'space']
|
props: ['status', 'space']
|
||||||
},
|
},
|
||||||
shortTitle: {
|
shortTitle: 'identifier',
|
||||||
tmpl: '{identifier}-{number}',
|
|
||||||
props: [{ identifier: ['space', 'identifier'] }, 'number']
|
|
||||||
},
|
|
||||||
title: 'title'
|
title: 'title'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -133,7 +133,14 @@ export class TTask extends TAttachedDoc implements Task {
|
|||||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||||
attachments?: number
|
attachments?: number
|
||||||
|
|
||||||
isDone?: boolean
|
@Prop(TypeBoolean(), getEmbeddedLabel('isDone'))
|
||||||
|
@Hidden()
|
||||||
|
isDone?: boolean
|
||||||
|
|
||||||
|
@Prop(TypeString(), task.string.Identifier)
|
||||||
|
@ReadOnly()
|
||||||
|
@Index(IndexKind.Indexed)
|
||||||
|
identifier!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mixin(task.mixin.KanbanCard, core.class.Class)
|
@Mixin(task.mixin.KanbanCard, core.class.Class)
|
||||||
|
@ -42,7 +42,9 @@ import {
|
|||||||
baseIssueTaskStatuses,
|
baseIssueTaskStatuses,
|
||||||
classicIssueTaskStatuses,
|
classicIssueTaskStatuses,
|
||||||
createStatesData,
|
createStatesData,
|
||||||
type IssueStatus
|
type IssueStatus,
|
||||||
|
type Issue,
|
||||||
|
type Project
|
||||||
} from '@hcengineering/tracker'
|
} from '@hcengineering/tracker'
|
||||||
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||||
import tracker from './plugin'
|
import tracker from './plugin'
|
||||||
@ -333,6 +335,20 @@ async function restoreToDoCategory (client: MigrationClient): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function migrateIdentifiers (client: MigrationClient): Promise<void> {
|
||||||
|
const classes = client.hierarchy.getDescendants(tracker.class.Issue)
|
||||||
|
const issues = await client.find<Issue>(DOMAIN_TASK, { _class: { $in: classes }, identifier: { $exists: false } })
|
||||||
|
if (issues.length === 0) return
|
||||||
|
const projects = await client.find<Project>(DOMAIN_SPACE, { _class: tracker.class.Project })
|
||||||
|
const projectsMap = toIdMap(projects)
|
||||||
|
for (const issue of issues) {
|
||||||
|
const project = projectsMap.get(issue.space)
|
||||||
|
if (project === undefined) continue
|
||||||
|
const identifier = project.identifier + '-' + issue.number
|
||||||
|
await client.update(DOMAIN_TASK, { _id: issue._id }, { $set: { identifier } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const trackerOperation: MigrateOperation = {
|
export const trackerOperation: MigrateOperation = {
|
||||||
async migrate (client: MigrationClient): Promise<void> {
|
async migrate (client: MigrationClient): Promise<void> {
|
||||||
await tryMigrate(client, 'tracker', [
|
await tryMigrate(client, 'tracker', [
|
||||||
@ -464,6 +480,10 @@ export const trackerOperation: MigrateOperation = {
|
|||||||
{
|
{
|
||||||
state: 'restoreToDoCategory',
|
state: 'restoreToDoCategory',
|
||||||
func: restoreToDoCategory
|
func: restoreToDoCategory
|
||||||
|
},
|
||||||
|
{
|
||||||
|
state: 'identifier',
|
||||||
|
func: migrateIdentifiers
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
@ -17,11 +17,12 @@
|
|||||||
import type { Board, Card as BoardCard } from '@hcengineering/board'
|
import type { Board, Card as BoardCard } from '@hcengineering/board'
|
||||||
import core, { AttachedData, Ref, SortingOrder, Space, generateId } from '@hcengineering/core'
|
import core, { AttachedData, Ref, SortingOrder, Space, generateId } from '@hcengineering/core'
|
||||||
import { OK, Status } from '@hcengineering/platform'
|
import { OK, Status } from '@hcengineering/platform'
|
||||||
import { Card, SpaceSelector, getClient } from '@hcengineering/presentation'
|
import { Card, SpaceSelector, createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import task, { calcRank } from '@hcengineering/task'
|
import task, { TaskType, calcRank } from '@hcengineering/task'
|
||||||
import { EditBox, Grid, Status as StatusControl } from '@hcengineering/ui'
|
import { EditBox, Grid, Status as StatusControl } from '@hcengineering/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import board from '../plugin'
|
import board from '../plugin'
|
||||||
|
import { TaskKindSelector } from '@hcengineering/task-resources'
|
||||||
|
|
||||||
export let space: Ref<Space>
|
export let space: Ref<Space>
|
||||||
|
|
||||||
@ -32,12 +33,14 @@
|
|||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const cardId = generateId()
|
const cardId = generateId<BoardCard>()
|
||||||
|
|
||||||
export function canClose (): boolean {
|
export function canClose (): boolean {
|
||||||
return title !== ''
|
return title !== ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let kind: Ref<TaskType> | undefined = undefined
|
||||||
|
|
||||||
async function createCard () {
|
async function createCard () {
|
||||||
const sp = await client.findOne(board.class.Board, { _id: _space as Ref<Board> })
|
const sp = await client.findOne(board.class.Board, { _id: _space as Ref<Board> })
|
||||||
if (sp === undefined) {
|
if (sp === undefined) {
|
||||||
@ -51,6 +54,9 @@
|
|||||||
if (status === undefined) {
|
if (status === undefined) {
|
||||||
throw new Error('Status not found')
|
throw new Error('Status not found')
|
||||||
}
|
}
|
||||||
|
if (kind === undefined) {
|
||||||
|
throw new Error('kind is not specified')
|
||||||
|
}
|
||||||
const sequence = await client.findOne(task.class.Sequence, { attachedTo: board.class.Card })
|
const sequence = await client.findOne(task.class.Sequence, { attachedTo: board.class.Card })
|
||||||
if (sequence === undefined) {
|
if (sequence === undefined) {
|
||||||
throw new Error('sequence object not found')
|
throw new Error('sequence object not found')
|
||||||
@ -59,10 +65,14 @@
|
|||||||
const lastOne = await client.findOne(board.class.Card, {}, { sort: { rank: SortingOrder.Descending } })
|
const lastOne = await client.findOne(board.class.Card, {}, { sort: { rank: SortingOrder.Descending } })
|
||||||
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
||||||
|
|
||||||
|
const number = (incResult as any).object.sequence
|
||||||
|
|
||||||
const value: AttachedData<BoardCard> = {
|
const value: AttachedData<BoardCard> = {
|
||||||
status: status._id,
|
status: status._id,
|
||||||
number: (incResult as any).object.sequence,
|
number,
|
||||||
title,
|
title,
|
||||||
|
kind,
|
||||||
|
identifier: `CARD-${number}`,
|
||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
assignee: null,
|
assignee: null,
|
||||||
description: '',
|
description: '',
|
||||||
@ -75,6 +85,14 @@
|
|||||||
await client.addCollection(board.class.Card, _space, _space, board.class.Board, 'cards', value, cardId)
|
await client.addCollection(board.class.Card, _space, _space, board.class.Board, 'cards', value, cardId)
|
||||||
dispatch('close')
|
dispatch('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let boards: Board[] = []
|
||||||
|
const boardQuery = createQuery()
|
||||||
|
boardQuery.query(board.class.Board, {}, (res) => {
|
||||||
|
boards = res
|
||||||
|
})
|
||||||
|
|
||||||
|
$: currentBoard = boards.find((it) => it._id === _space)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
@ -88,6 +106,7 @@
|
|||||||
>
|
>
|
||||||
<svelte:fragment slot="header">
|
<svelte:fragment slot="header">
|
||||||
<SpaceSelector _class={board.class.Board} label={board.string.BoardName} bind:space={_space} />
|
<SpaceSelector _class={board.class.Board} label={board.string.BoardName} bind:space={_space} />
|
||||||
|
<TaskKindSelector projectType={currentBoard?.type} bind:value={kind} baseClass={board.class.Card} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<StatusControl slot="error" {status} />
|
<StatusControl slot="error" {status} />
|
||||||
<Grid column={1} rowGap={1.5}>
|
<Grid column={1} rowGap={1.5}>
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
|
|
||||||
const mixins: Mixin<Doc>[] = []
|
const mixins: Mixin<Doc>[] = []
|
||||||
const allowedCollections = ['labels']
|
const allowedCollections = ['labels']
|
||||||
const ignoreKeys = ['isArchived', 'location', 'title', 'description', 'status', 'number', 'assignee']
|
const ignoreKeys = ['isArchived', 'location', 'title', 'description', 'status', 'number', 'assignee', 'identifier']
|
||||||
|
|
||||||
function change (field: string, value: any) {
|
function change (field: string, value: any) {
|
||||||
if (object) {
|
if (object) {
|
||||||
|
@ -27,15 +27,17 @@ export async function createCard (
|
|||||||
|
|
||||||
const lastOne = await client.findOne(board.class.Card, {}, { sort: { rank: SortingOrder.Descending } })
|
const lastOne = await client.findOne(board.class.Card, {}, { sort: { rank: SortingOrder.Descending } })
|
||||||
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
||||||
|
const number = (incResult as any).object.sequence
|
||||||
|
|
||||||
const value: AttachedData<Card> = {
|
const value: AttachedData<Card> = {
|
||||||
title: '',
|
title: '',
|
||||||
status,
|
status,
|
||||||
startDate: null,
|
startDate: null,
|
||||||
dueDate: null,
|
dueDate: null,
|
||||||
number: (incResult as any).object.sequence,
|
number,
|
||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
assignee: null,
|
assignee: null,
|
||||||
|
identifier: `CARD-${number}`,
|
||||||
description: '',
|
description: '',
|
||||||
...attribues
|
...attribues
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@
|
|||||||
import type { Customer, Funnel, Lead } from '@hcengineering/lead'
|
import type { Customer, Funnel, Lead } from '@hcengineering/lead'
|
||||||
import { OK, Status } from '@hcengineering/platform'
|
import { OK, Status } from '@hcengineering/platform'
|
||||||
import { Card, createQuery, getClient, InlineAttributeBar, SpaceSelector } from '@hcengineering/presentation'
|
import { Card, createQuery, getClient, InlineAttributeBar, SpaceSelector } from '@hcengineering/presentation'
|
||||||
import task, { calcRank, getStates } from '@hcengineering/task'
|
import task, { calcRank, getStates, TaskType } from '@hcengineering/task'
|
||||||
|
import { TaskKindSelector, typeStore } from '@hcengineering/task-resources'
|
||||||
import { Button, createFocusManager, EditBox, FocusHandler, Label, Status as StatusControl } from '@hcengineering/ui'
|
import { Button, createFocusManager, EditBox, FocusHandler, Label, Status as StatusControl } from '@hcengineering/ui'
|
||||||
import { statusStore } from '@hcengineering/view-resources'
|
import { statusStore } from '@hcengineering/view-resources'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import lead from '../plugin'
|
import lead from '../plugin'
|
||||||
import { typeStore } from '@hcengineering/task-resources'
|
|
||||||
|
|
||||||
export let space: Ref<Funnel>
|
export let space: Ref<Funnel>
|
||||||
export let customer: Ref<Contact> | null = null
|
export let customer: Ref<Contact> | null = null
|
||||||
@ -71,19 +71,27 @@
|
|||||||
|
|
||||||
$: funnel = funnels.find((it) => it._id === _space)
|
$: funnel = funnels.find((it) => it._id === _space)
|
||||||
|
|
||||||
|
let kind: Ref<TaskType> | undefined = undefined
|
||||||
|
|
||||||
async function createLead () {
|
async function createLead () {
|
||||||
const sequence = await client.findOne(task.class.Sequence, { attachedTo: lead.class.Lead })
|
const sequence = await client.findOne(task.class.Sequence, { attachedTo: lead.class.Lead })
|
||||||
if (sequence === undefined || customer == null) {
|
if (sequence === undefined || customer == null) {
|
||||||
throw new Error('Lead creation failed')
|
throw new Error('Lead creation failed')
|
||||||
}
|
}
|
||||||
|
if (kind === undefined) {
|
||||||
|
throw new Error('kind is not specified')
|
||||||
|
}
|
||||||
|
|
||||||
const lastOne = await client.findOne(lead.class.Lead, {}, { sort: { rank: SortingOrder.Descending } })
|
const lastOne = await client.findOne(lead.class.Lead, {}, { sort: { rank: SortingOrder.Descending } })
|
||||||
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
||||||
|
const number = (incResult as any).object.sequence
|
||||||
|
|
||||||
const value: AttachedData<Lead> = {
|
const value: AttachedData<Lead> = {
|
||||||
status: state,
|
status: state,
|
||||||
number: (incResult as any).object.sequence,
|
number,
|
||||||
|
identifier: `LEAD-${number}`,
|
||||||
title,
|
title,
|
||||||
|
kind,
|
||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
assignee: null,
|
assignee: null,
|
||||||
startDate: null,
|
startDate: null,
|
||||||
@ -137,6 +145,7 @@
|
|||||||
label: lead.string.CreateFunnel
|
label: lead.string.CreateFunnel
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<TaskKindSelector projectType={funnel?.type} bind:value={kind} baseClass={lead.class.Lead} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="title">
|
<svelte:fragment slot="title">
|
||||||
<div class="flex-row-center gap-2">
|
<div class="flex-row-center gap-2">
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
dispatch('open', { ignoreKeys: ['comments', 'number', 'title', 'customer'] })
|
dispatch('open', { ignoreKeys: ['comments', 'number', 'title', 'customer', 'identifier'] })
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
import { ActionIcon, Component, DueDatePresenter, IconMoreH, showPopup } from '@hcengineering/ui'
|
import { ActionIcon, Component, DueDatePresenter, IconMoreH, showPopup } from '@hcengineering/ui'
|
||||||
import { BuildModelKey } from '@hcengineering/view'
|
import { BuildModelKey } from '@hcengineering/view'
|
||||||
import { ContextMenu, enabledConfig, openDoc, statusStore } from '@hcengineering/view-resources'
|
import { ContextMenu, enabledConfig, openDoc, statusStore } from '@hcengineering/view-resources'
|
||||||
import { ChatMessagesPresenter } from '@hcengineering/notification-resources'
|
import { ChatMessagesPresenter } from '@hcengineering/chunter-resources'
|
||||||
import task from '@hcengineering/task'
|
import task from '@hcengineering/task'
|
||||||
|
|
||||||
import lead from '../plugin'
|
import lead from '../plugin'
|
||||||
|
@ -31,14 +31,14 @@
|
|||||||
{#if value}
|
{#if value}
|
||||||
<DocNavLink object={value} {inline} {disabled} {noUnderline} {accent}>
|
<DocNavLink object={value} {inline} {disabled} {noUnderline} {accent}>
|
||||||
{#if inline}
|
{#if inline}
|
||||||
<span class="antiMention" use:tooltip={{ label: lead.string.Lead }}>@LEAD-{value.number}</span>
|
<span class="antiMention" use:tooltip={{ label: lead.string.Lead }}>@{value.identifier}</span>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-presenter">
|
<div class="flex-presenter">
|
||||||
{#if shouldShowAvatar}
|
{#if shouldShowAvatar}
|
||||||
<div class="icon"><Icon icon={lead.icon.Lead} size={'small'} /></div>
|
<div class="icon"><Icon icon={lead.icon.Lead} size={'small'} /></div>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="label nowrap" class:no-underline={noUnderline || disabled} class:fs-bold={accent}
|
<span class="label nowrap" class:no-underline={noUnderline || disabled} class:fs-bold={accent}
|
||||||
>LEAD-{value.number}</span
|
>{value.identifier}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -89,6 +89,7 @@
|
|||||||
attachedToClass: recruit.mixin.Candidate,
|
attachedToClass: recruit.mixin.Candidate,
|
||||||
_class: recruit.class.Applicant,
|
_class: recruit.class.Applicant,
|
||||||
space,
|
space,
|
||||||
|
identifier: '',
|
||||||
_id: generateId(),
|
_id: generateId(),
|
||||||
collection: 'applications',
|
collection: 'applications',
|
||||||
modifiedOn: Date.now(),
|
modifiedOn: Date.now(),
|
||||||
@ -137,6 +138,8 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const number = (incResult as any).object.sequence
|
||||||
|
|
||||||
await client.addCollection(
|
await client.addCollection(
|
||||||
recruit.class.Applicant,
|
recruit.class.Applicant,
|
||||||
_space,
|
_space,
|
||||||
@ -146,7 +149,8 @@
|
|||||||
{
|
{
|
||||||
...doc,
|
...doc,
|
||||||
status: selectedState._id,
|
status: selectedState._id,
|
||||||
number: (incResult as any).object.sequence,
|
number,
|
||||||
|
identifier: `APP-${number}`,
|
||||||
assignee: doc.assignee,
|
assignee: doc.assignee,
|
||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
startDate: null,
|
startDate: null,
|
||||||
|
@ -31,11 +31,11 @@
|
|||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const sendOpen = () => {
|
const sendOpen = () => {
|
||||||
if (object?.number !== undefined) {
|
if (object !== undefined) {
|
||||||
dispatch('open', {
|
dispatch('open', {
|
||||||
ignoreKeys: ['comments', 'number'],
|
ignoreKeys: ['comments', 'number', 'identifier'],
|
||||||
allowedCollections: ['labels'],
|
allowedCollections: ['labels'],
|
||||||
title: `APP-${object.number}`
|
title: object.identifier
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import contact, { getName } from '@hcengineering/contact'
|
import contact, { getName } from '@hcengineering/contact'
|
||||||
import { type Class, type Client, type Doc, Hierarchy, type Ref } from '@hcengineering/core'
|
import { Hierarchy, type Class, type Client, type Doc, type Ref } from '@hcengineering/core'
|
||||||
import presentation, { getClient } from '@hcengineering/presentation'
|
|
||||||
import { getMetadata } from '@hcengineering/platform'
|
import { getMetadata } from '@hcengineering/platform'
|
||||||
|
import presentation, { getClient } from '@hcengineering/presentation'
|
||||||
import {
|
import {
|
||||||
|
recruitId,
|
||||||
type Applicant,
|
type Applicant,
|
||||||
type Candidate,
|
type Candidate,
|
||||||
type Review,
|
type Review,
|
||||||
type Vacancy,
|
type Vacancy,
|
||||||
type VacancyList,
|
type VacancyList
|
||||||
recruitId
|
|
||||||
} from '@hcengineering/recruit'
|
} from '@hcengineering/recruit'
|
||||||
import { type Location, type ResolvedLocation, getCurrentResolvedLocation, getPanelURI } from '@hcengineering/ui'
|
import { getCurrentResolvedLocation, getPanelURI, type Location, type ResolvedLocation } from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
import { workbenchId } from '@hcengineering/workbench'
|
||||||
import recruit from './plugin'
|
import recruit from './plugin'
|
||||||
@ -191,7 +191,7 @@ export async function getAppIdentifier (client: Client, ref: Ref<Applicant>, doc
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return `APP-${applicant.number}`
|
return applicant.identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRevTitle (client: Client, ref: Ref<Review>): Promise<string> {
|
export async function getRevTitle (client: Client, ref: Ref<Review>): Promise<string> {
|
||||||
@ -201,6 +201,9 @@ export async function getRevTitle (client: Client, ref: Ref<Review>): Promise<st
|
|||||||
export async function getSequenceId (doc: RecruitDocument): Promise<string> {
|
export async function getSequenceId (doc: RecruitDocument): Promise<string> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
|
if (hierarchy.isDerived(doc._class, recruit.class.Applicant)) {
|
||||||
|
return (doc as Applicant).identifier
|
||||||
|
}
|
||||||
let clazz = hierarchy.getClass(doc._class)
|
let clazz = hierarchy.getClass(doc._class)
|
||||||
let label = clazz.shortLabel
|
let label = clazz.shortLabel
|
||||||
while (label === undefined && clazz.extends !== undefined) {
|
while (label === undefined && clazz.extends !== undefined) {
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
"ProcessStates": "Process states",
|
"ProcessStates": "Process states",
|
||||||
"Type": "Type",
|
"Type": "Type",
|
||||||
"Group": "Group",
|
"Group": "Group",
|
||||||
"Color": "Color"
|
"Color": "Color",
|
||||||
|
"Identifier": "Identifier"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -94,6 +94,7 @@
|
|||||||
"ProcessStates": "Состояния процесса",
|
"ProcessStates": "Состояния процесса",
|
||||||
"Type": "Тип",
|
"Type": "Тип",
|
||||||
"Group": "Группа",
|
"Group": "Группа",
|
||||||
"Color": "Цвет"
|
"Color": "Цвет",
|
||||||
|
"Identifier": "Идентификатор"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -56,6 +56,7 @@ export interface Task extends AttachedDoc, DocWithRank {
|
|||||||
comments?: number
|
comments?: number
|
||||||
attachments?: number
|
attachments?: number
|
||||||
labels?: number
|
labels?: number
|
||||||
|
identifier: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,7 +245,8 @@ const task = plugin(taskId, {
|
|||||||
Dashboard: '' as IntlString,
|
Dashboard: '' as IntlString,
|
||||||
ProjectTypes: '' as IntlString,
|
ProjectTypes: '' as IntlString,
|
||||||
TaskType: '' as IntlString,
|
TaskType: '' as IntlString,
|
||||||
ProjectType: '' as IntlString
|
ProjectType: '' as IntlString,
|
||||||
|
Identifier: '' as IntlString
|
||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
Sequence: '' as Ref<Class<Sequence>>,
|
Sequence: '' as Ref<Class<Sequence>>,
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { ObjectBox } from '@hcengineering/view-resources'
|
import { ObjectBox } from '@hcengineering/view-resources'
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||||
import { activeComponent, activeMilestone, generateIssueShortLink, getIssueId, updateIssueRelation } from '../issues'
|
import { activeComponent, activeMilestone, generateIssueShortLink, updateIssueRelation } from '../issues'
|
||||||
import tracker from '../plugin'
|
import tracker from '../plugin'
|
||||||
import SetParentIssueActionPopup from './SetParentIssueActionPopup.svelte'
|
import SetParentIssueActionPopup from './SetParentIssueActionPopup.svelte'
|
||||||
import ComponentSelector from './components/ComponentSelector.svelte'
|
import ComponentSelector from './components/ComponentSelector.svelte'
|
||||||
@ -300,7 +300,7 @@
|
|||||||
|
|
||||||
$: spaceQuery.query(tracker.class.Project, { _id: _space }, (res) => {
|
$: spaceQuery.query(tracker.class.Project, { _id: _space }, (res) => {
|
||||||
resetDefaultAssigneeId()
|
resetDefaultAssigneeId()
|
||||||
currentProject = res.shift()
|
currentProject = res[0]
|
||||||
})
|
})
|
||||||
|
|
||||||
const docCreateManager = DocCreateExtensionManager.create(tracker.class.Issue)
|
const docCreateManager = DocCreateExtensionManager.create(tracker.class.Issue)
|
||||||
@ -370,13 +370,15 @@
|
|||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const number = (incResult as any).object.sequence
|
||||||
|
|
||||||
const value: DocData<Issue> = {
|
const value: DocData<Issue> = {
|
||||||
title: getTitle(object.title),
|
title: getTitle(object.title),
|
||||||
description: object.description,
|
description: object.description,
|
||||||
assignee: object.assignee,
|
assignee: object.assignee,
|
||||||
component: object.component,
|
component: object.component,
|
||||||
milestone: object.milestone,
|
milestone: object.milestone,
|
||||||
number: (incResult as any).object.sequence,
|
number,
|
||||||
status: object.status,
|
status: object.status,
|
||||||
priority: object.priority,
|
priority: object.priority,
|
||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
@ -396,7 +398,8 @@
|
|||||||
reports: 0,
|
reports: 0,
|
||||||
relations: relatedTo !== undefined ? [{ _id: relatedTo._id, _class: relatedTo._class }] : [],
|
relations: relatedTo !== undefined ? [{ _id: relatedTo._id, _class: relatedTo._class }] : [],
|
||||||
childInfo: [],
|
childInfo: [],
|
||||||
kind
|
kind,
|
||||||
|
identifier: `${currentProject?.identifier}-${number}`
|
||||||
}
|
}
|
||||||
|
|
||||||
await docCreateManager.commit(operations, _id, _space, value)
|
await docCreateManager.commit(operations, _id, _space, value)
|
||||||
@ -439,7 +442,7 @@
|
|||||||
{
|
{
|
||||||
issueId: _id,
|
issueId: _id,
|
||||||
subTitlePostfix: (await translate(tracker.string.CreatedOne, {}, $themeStore.language)).toLowerCase(),
|
subTitlePostfix: (await translate(tracker.string.CreatedOne, {}, $themeStore.language)).toLowerCase(),
|
||||||
issueUrl: currentProject != null && generateIssueShortLink(getIssueId(currentProject, value as Issue))
|
issueUrl: currentProject != null && generateIssueShortLink(value.identifier)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,12 +15,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import core, { AttachedData, FindOptions, Ref, SortingOrder } from '@hcengineering/core'
|
import core, { AttachedData, FindOptions, Ref, SortingOrder } from '@hcengineering/core'
|
||||||
import { ObjectPopup, getClient } from '@hcengineering/presentation'
|
import { ObjectPopup, getClient } from '@hcengineering/presentation'
|
||||||
|
import { calcRank } from '@hcengineering/task'
|
||||||
import { Issue, IssueDraft } from '@hcengineering/tracker'
|
import { Issue, IssueDraft } from '@hcengineering/tracker'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { getIssueId } from '../issues'
|
|
||||||
import tracker from '../plugin'
|
import tracker from '../plugin'
|
||||||
import IssueStatusIcon from './issues/IssueStatusIcon.svelte'
|
import IssueStatusIcon from './issues/IssueStatusIcon.svelte'
|
||||||
import { calcRank } from '@hcengineering/task'
|
|
||||||
|
|
||||||
export let value: Issue | AttachedData<Issue> | Issue[] | IssueDraft
|
export let value: Issue | AttachedData<Issue> | Issue[] | IssueDraft
|
||||||
export let width: 'medium' | 'large' | 'full' = 'large'
|
export let width: 'medium' | 'large' | 'full' = 'large'
|
||||||
@ -29,7 +28,6 @@
|
|||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const options: FindOptions<Issue> = {
|
const options: FindOptions<Issue> = {
|
||||||
lookup: {
|
lookup: {
|
||||||
space: tracker.class.Project,
|
|
||||||
status: [tracker.class.IssueStatus, { category: core.class.StatusCategory }]
|
status: [tracker.class.IssueStatus, { category: core.class.StatusCategory }]
|
||||||
},
|
},
|
||||||
sort: { modifiedOn: SortingOrder.Descending }
|
sort: { modifiedOn: SortingOrder.Descending }
|
||||||
@ -98,17 +96,14 @@
|
|||||||
on:close={onClose}
|
on:close={onClose}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="item" let:item={issue}>
|
<svelte:fragment slot="item" let:item={issue}>
|
||||||
{@const issueId = getIssueId(issue.$lookup.space, issue)}
|
<div class="flex-center clear-mins w-full h-9">
|
||||||
{#if issueId}
|
{#if issue?.$lookup?.status}
|
||||||
<div class="flex-center clear-mins w-full h-9">
|
<div class="icon mr-4 h-8">
|
||||||
{#if issue?.$lookup?.status}
|
<IssueStatusIcon value={issue.$lookup.status} space={issue.space} size="small" />
|
||||||
<div class="icon mr-4 h-8">
|
</div>
|
||||||
<IssueStatusIcon value={issue.$lookup.status} space={issue.space} size="small" />
|
{/if}
|
||||||
</div>
|
<span class="overflow-label flex-no-shrink mr-3">{issue.identifier}</span>
|
||||||
{/if}
|
<span class="overflow-label w-full content-color">{issue.title}</span>
|
||||||
<span class="overflow-label flex-no-shrink mr-3">{issueId}</span>
|
</div>
|
||||||
<span class="overflow-label w-full content-color">{issue.title}</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</ObjectPopup>
|
</ObjectPopup>
|
||||||
|
@ -15,13 +15,11 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { WithLookup } from '@hcengineering/core'
|
import { WithLookup } from '@hcengineering/core'
|
||||||
import type { Issue, Project } from '@hcengineering/tracker'
|
import type { Issue } from '@hcengineering/tracker'
|
||||||
import { FixedColumn, statusStore } from '@hcengineering/view-resources'
|
import { FixedColumn, statusStore } from '@hcengineering/view-resources'
|
||||||
import { getIssueId } from '../../issues'
|
|
||||||
import IssueStatusIcon from './IssueStatusIcon.svelte'
|
import IssueStatusIcon from './IssueStatusIcon.svelte'
|
||||||
|
|
||||||
export let value: WithLookup<Issue>
|
export let value: WithLookup<Issue>
|
||||||
$: title = getIssueId(value.$lookup?.space as Project, value)
|
|
||||||
$: st = $statusStore.byId.get(value.status)
|
$: st = $statusStore.byId.get(value.status)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -33,6 +31,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</FixedColumn>
|
</FixedColumn>
|
||||||
<span class="ml-2 max-w-120 overflow-label">
|
<span class="ml-2 max-w-120 overflow-label">
|
||||||
{title} - {value.title}
|
{value.identifier} - {value.title}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,14 +16,13 @@
|
|||||||
import { WithLookup } from '@hcengineering/core'
|
import { WithLookup } from '@hcengineering/core'
|
||||||
import { Asset } from '@hcengineering/platform'
|
import { Asset } from '@hcengineering/platform'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import type { Issue, Project } from '@hcengineering/tracker'
|
import { taskTypeStore } from '@hcengineering/task-resources'
|
||||||
|
import TaskTypeIcon from '@hcengineering/task-resources/src/components/taskTypes/TaskTypeIcon.svelte'
|
||||||
|
import type { Issue } from '@hcengineering/tracker'
|
||||||
import { AnySvelteComponent, Component, Icon, tooltip } from '@hcengineering/ui'
|
import { AnySvelteComponent, Component, Icon, tooltip } from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { DocNavLink } from '@hcengineering/view-resources'
|
import { DocNavLink } from '@hcengineering/view-resources'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import { activeProjects } from '../../utils'
|
|
||||||
import { taskTypeStore } from '@hcengineering/task-resources'
|
|
||||||
import TaskTypeIcon from '@hcengineering/task-resources/src/components/taskTypes/TaskTypeIcon.svelte'
|
|
||||||
|
|
||||||
export let value: WithLookup<Issue> | undefined
|
export let value: WithLookup<Issue> | undefined
|
||||||
export let disabled: boolean = false
|
export let disabled: boolean = false
|
||||||
@ -35,14 +34,6 @@
|
|||||||
export let kind: 'list' | undefined = undefined
|
export let kind: 'list' | undefined = undefined
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
|
|
||||||
let currentProject: Project | undefined = value?.$lookup?.space
|
|
||||||
|
|
||||||
$: if (value !== undefined) {
|
|
||||||
currentProject = $activeProjects.get(value?.space) as Project
|
|
||||||
}
|
|
||||||
|
|
||||||
$: title = currentProject ? `${currentProject.identifier}-${value?.number}` : `${value?.number}`
|
|
||||||
|
|
||||||
$: presenters =
|
$: presenters =
|
||||||
value !== undefined ? getClient().getHierarchy().findMixinMixins(value, view.mixin.ObjectPresenter) : []
|
value !== undefined ? getClient().getHierarchy().findMixinMixins(value, view.mixin.ObjectPresenter) : []
|
||||||
|
|
||||||
@ -61,7 +52,7 @@
|
|||||||
shrink={0}
|
shrink={0}
|
||||||
>
|
>
|
||||||
{#if inline}
|
{#if inline}
|
||||||
<span class="antiMention" use:tooltip={{ label: tracker.string.Issue }}>@{title}</span>
|
<span class="antiMention" use:tooltip={{ label: tracker.string.Issue }}>@{value.identifier}</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="issuePresenterRoot" class:list={kind === 'list'} class:cursor-pointer={!disabled}>
|
<span class="issuePresenterRoot" class:list={kind === 'list'} class:cursor-pointer={!disabled}>
|
||||||
{#if shouldShowAvatar}
|
{#if shouldShowAvatar}
|
||||||
@ -74,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="overflow-label" class:select-text={!noSelect} title={value?.title}>
|
<span class="overflow-label" class:select-text={!noSelect} title={value?.title}>
|
||||||
{title}
|
{value.identifier}
|
||||||
<slot name="details" />
|
<slot name="details" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -45,11 +45,6 @@
|
|||||||
{ limit: 1 }
|
{ limit: 1 }
|
||||||
)
|
)
|
||||||
|
|
||||||
let currentProject: Project | undefined
|
|
||||||
|
|
||||||
$: currentProject = $activeProjects.get(space) as Project
|
|
||||||
$: issueName = currentProject && issue && `${currentProject.identifier}-${issue.number}`
|
|
||||||
|
|
||||||
const limit: number = 350
|
const limit: number = 350
|
||||||
let cHeight: number = 0
|
let cHeight: number = 0
|
||||||
|
|
||||||
@ -74,7 +69,7 @@
|
|||||||
<span class="overflow-label content-color">{parent.title}</span>
|
<span class="overflow-label content-color">{parent.title}</span>
|
||||||
<IconForward size={'x-small'} />
|
<IconForward size={'x-small'} />
|
||||||
{/if}
|
{/if}
|
||||||
<span class="content-dark-color">{issueName}</span>
|
<span class="content-dark-color">{issue.identifier}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="overflow-label text-xl caption-color">{issue.title}</span>
|
<span class="overflow-label text-xl caption-color">{issue.title}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,14 +21,12 @@
|
|||||||
let currentProject: Project | undefined = undefined
|
let currentProject: Project | undefined = undefined
|
||||||
|
|
||||||
$: currentProject = $activeProjects.get(value.space) as Project
|
$: currentProject = $activeProjects.get(value.space) as Project
|
||||||
|
|
||||||
$: title = currentProject ? `${currentProject.identifier}-${value?.number}` : `${value?.number}`
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="flex-row-center crop-presenter">
|
<div class="flex-row-center crop-presenter">
|
||||||
<span class="font-medium mr-2 whitespace-nowrap clear-mins">{title}</span>
|
<span class="font-medium mr-2 whitespace-nowrap clear-mins">{value.identifier}</span>
|
||||||
<span class="overflow-label">
|
<span class="overflow-label">
|
||||||
{currentProject?.name}
|
{currentProject?.name}
|
||||||
</span>
|
</span>
|
||||||
|
@ -13,33 +13,22 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Issue, Project } from '@hcengineering/tracker'
|
import { Issue } from '@hcengineering/tracker'
|
||||||
import { Button, IconClose, Spinner } from '@hcengineering/ui'
|
import { Button, IconClose } from '@hcengineering/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { getIssueId } from '../../issues'
|
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import { activeProjects } from '../../utils'
|
|
||||||
import PriorityRefPresenter from './PriorityRefPresenter.svelte'
|
import PriorityRefPresenter from './PriorityRefPresenter.svelte'
|
||||||
|
|
||||||
export let issue: Issue
|
export let issue: Issue
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
let project: Project | undefined
|
|
||||||
|
|
||||||
$: project = $activeProjects.get(issue.space)
|
|
||||||
$: issueId = project && getIssueId(project, issue)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="parentIssue-container">
|
<div class="parentIssue-container">
|
||||||
<div class="flex-no-shrink mr-1-5">
|
<div class="flex-no-shrink mr-1-5">
|
||||||
<PriorityRefPresenter value={issue.priority} shouldShowLabel={false} />
|
<PriorityRefPresenter value={issue.priority} shouldShowLabel={false} />
|
||||||
</div>
|
</div>
|
||||||
{#if issueId}
|
<span class="overflow-label flex-no-shrink content-dark-color">{issue.identifier}</span>
|
||||||
<span class="overflow-label flex-no-shrink content-dark-color">{issueId}</span>
|
|
||||||
{:else}
|
|
||||||
<Spinner size="small" />
|
|
||||||
{/if}
|
|
||||||
<span class="overflow-label issue-title">{issue.title}</span>
|
<span class="overflow-label issue-title">{issue.title}</span>
|
||||||
<Button
|
<Button
|
||||||
icon={IconClose}
|
icon={IconClose}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import { Issue } from '@hcengineering/tracker'
|
import { Issue } from '@hcengineering/tracker'
|
||||||
import { Component, Icon, IconClose, navigate } from '@hcengineering/ui'
|
import { Component, Icon, IconClose, navigate } from '@hcengineering/ui'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { getIssueId, issueLinkFragmentProvider, updateIssueRelation } from '../../issues'
|
import { issueLinkFragmentProvider, updateIssueRelation } from '../../issues'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
|
|
||||||
export let value: Issue
|
export let value: Issue
|
||||||
@ -24,14 +24,9 @@
|
|||||||
|
|
||||||
$: isIssue = client.getHierarchy().isDerived(_class, tracker.class.Issue)
|
$: isIssue = client.getHierarchy().isDerived(_class, tracker.class.Issue)
|
||||||
let documents: WithLookup<Doc>[] = []
|
let documents: WithLookup<Doc>[] = []
|
||||||
$: issuesQuery.query(
|
$: issuesQuery.query(_class, query, (result) => {
|
||||||
_class,
|
documents = result
|
||||||
query,
|
})
|
||||||
(result) => {
|
|
||||||
documents = result
|
|
||||||
},
|
|
||||||
{ lookup: isIssue ? { space: tracker.class.Project } : {} }
|
|
||||||
)
|
|
||||||
|
|
||||||
async function handleClick (issue: RelatedDocument) {
|
async function handleClick (issue: RelatedDocument) {
|
||||||
const prop = type === 'isBlocking' ? 'blockedBy' : type
|
const prop = type === 'isBlocking' ? 'blockedBy' : type
|
||||||
@ -82,19 +77,17 @@
|
|||||||
{#each documents as doc}
|
{#each documents as doc}
|
||||||
{#if isIssue}
|
{#if isIssue}
|
||||||
{@const issue = asIssue(doc)}
|
{@const issue = asIssue(doc)}
|
||||||
{#if issue.$lookup?.space}
|
<div class="tag-container">
|
||||||
<div class="tag-container">
|
<Icon {icon} size={'small'} />
|
||||||
<Icon {icon} size={'small'} />
|
<div class="flex-grow">
|
||||||
<div class="flex-grow">
|
<button on:click|stopPropagation={() => handleRedirect(issue)}>
|
||||||
<button on:click|stopPropagation={() => handleRedirect(issue)}>
|
<span class="overflow-label ml-1-5 content-color">{issue.identifier}</span>
|
||||||
<span class="overflow-label ml-1-5 content-color">{getIssueId(issue.$lookup.space, issue)}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button class="btn-close" on:click|stopPropagation={() => handleClick(issue)}>
|
|
||||||
<Icon icon={IconClose} size={'x-small'} />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
<button class="btn-close" on:click|stopPropagation={() => handleClick(issue)}>
|
||||||
|
<Icon icon={IconClose} size={'x-small'} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="tag-container between">
|
<div class="tag-container between">
|
||||||
<Component
|
<Component
|
||||||
|
@ -90,7 +90,8 @@
|
|||||||
'dueDate',
|
'dueDate',
|
||||||
'milestone',
|
'milestone',
|
||||||
'relations',
|
'relations',
|
||||||
'blockedBy'
|
'blockedBy',
|
||||||
|
'identifier'
|
||||||
])
|
])
|
||||||
|
|
||||||
let account: PersonAccount | undefined
|
let account: PersonAccount | undefined
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { ContextMenu, DocNavLink, ParentsNavigator } from '@hcengineering/view-resources'
|
import { ContextMenu, DocNavLink, ParentsNavigator } from '@hcengineering/view-resources'
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||||
import { generateIssueShortLink, getIssueId } from '../../../issues'
|
import { generateIssueShortLink } from '../../../issues'
|
||||||
import tracker from '../../../plugin'
|
import tracker from '../../../plugin'
|
||||||
import IssueStatusActivity from '../IssueStatusActivity.svelte'
|
import IssueStatusActivity from '../IssueStatusActivity.svelte'
|
||||||
import ControlPanel from './ControlPanel.svelte'
|
import ControlPanel from './ControlPanel.svelte'
|
||||||
@ -104,7 +104,6 @@
|
|||||||
{ lookup: { attachedTo: tracker.class.Issue, space: tracker.class.Project } }
|
{ lookup: { attachedTo: tracker.class.Issue, space: tracker.class.Project } }
|
||||||
)
|
)
|
||||||
|
|
||||||
$: issueId = currentProject !== undefined && issue !== undefined && getIssueId(currentProject, issue)
|
|
||||||
$: canSave = title.trim().length > 0
|
$: canSave = title.trim().length > 0
|
||||||
$: parentIssue = issue?.$lookup?.attachedTo
|
$: parentIssue = issue?.$lookup?.attachedTo
|
||||||
|
|
||||||
@ -195,12 +194,12 @@
|
|||||||
{#if !embedded}
|
{#if !embedded}
|
||||||
<ParentsNavigator element={issue} />
|
<ParentsNavigator element={issue} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if embedded && issueId}
|
{#if embedded}
|
||||||
<DocNavLink noUnderline object={issue}>
|
<DocNavLink noUnderline object={issue}>
|
||||||
<div class="title">{issueId}</div>
|
<div class="title">{issue.identifier}</div>
|
||||||
</DocNavLink>
|
</DocNavLink>
|
||||||
{:else if issueId}
|
{:else}
|
||||||
<div class="title not-active">{issueId}</div>
|
<div class="title not-active">{issue.identifier}</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if (projectType?.tasks.length ?? 0) > 1 && taskType !== undefined}
|
{#if (projectType?.tasks.length ?? 0) > 1 && taskType !== undefined}
|
||||||
@ -223,9 +222,7 @@
|
|||||||
|
|
||||||
<svelte:fragment slot="utils">
|
<svelte:fragment slot="utils">
|
||||||
<Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showMenu} />
|
<Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showMenu} />
|
||||||
{#if issueId}
|
<CopyToClipboard issueUrl={generateIssueShortLink(issue.identifier)} />
|
||||||
<CopyToClipboard issueUrl={generateIssueShortLink(issueId)} />
|
|
||||||
{/if}
|
|
||||||
<Button
|
<Button
|
||||||
icon={setting.icon.Setting}
|
icon={setting.icon.Setting}
|
||||||
kind={'icon'}
|
kind={'icon'}
|
||||||
@ -298,7 +295,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<span slot="actions-label" class="select-text">
|
<span slot="actions-label" class="select-text">
|
||||||
{#if issueId}{issueId}{/if}
|
{issue.identifier}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<svelte:fragment slot="custom-attributes">
|
<svelte:fragment slot="custom-attributes">
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
tooltip
|
tooltip
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import { statusStore } from '@hcengineering/view-resources'
|
import { statusStore } from '@hcengineering/view-resources'
|
||||||
import { getIssueId, issueLinkFragmentProvider } from '../../../issues'
|
import { issueLinkFragmentProvider } from '../../../issues'
|
||||||
import tracker from '../../../plugin'
|
import tracker from '../../../plugin'
|
||||||
import { listIssueStatusOrder } from '../../../utils'
|
import { listIssueStatusOrder } from '../../../utils'
|
||||||
import IssueStatusIcon from '../IssueStatusIcon.svelte'
|
import IssueStatusIcon from '../IssueStatusIcon.svelte'
|
||||||
@ -75,7 +75,6 @@
|
|||||||
{
|
{
|
||||||
sort: { modifiedOn: SortingOrder.Descending },
|
sort: { modifiedOn: SortingOrder.Descending },
|
||||||
lookup: {
|
lookup: {
|
||||||
space: tracker.class.Project,
|
|
||||||
status: [tracker.class.IssueStatus, { category: core.class.StatusCategory }]
|
status: [tracker.class.IssueStatus, { category: core.class.StatusCategory }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +122,7 @@
|
|||||||
id: iss._id,
|
id: iss._id,
|
||||||
icon,
|
icon,
|
||||||
isSelected: iss._id === issue._id,
|
isSelected: iss._id === issue._id,
|
||||||
...(project !== undefined ? { text: `${getIssueId(project, iss)} ${iss.title}` } : undefined),
|
text: `${iss.identifier} ${iss.title}`,
|
||||||
...(color !== undefined ? { iconColor: getPlatformColorDef(color, $themeStore.dark).icon } : undefined),
|
...(color !== undefined ? { iconColor: getPlatformColorDef(color, $themeStore.dark).icon } : undefined),
|
||||||
category:
|
category:
|
||||||
category !== undefined
|
category !== undefined
|
||||||
@ -151,9 +150,7 @@
|
|||||||
<IssueStatusIcon space={parentIssue.space} value={parentStatus} size="small" />
|
<IssueStatusIcon space={parentIssue.space} value={parentStatus} size="small" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if issue.$lookup?.space}
|
<span class="overflow-label flex-no-shrink mr-2">{parentIssue.identifier}</span>
|
||||||
<span class="overflow-label flex-no-shrink mr-2">{getIssueId(issue.$lookup.space, parentIssue)}</span>
|
|
||||||
{/if}
|
|
||||||
<span class="overflow-label issue-title">{parentIssue.title}</span>
|
<span class="overflow-label issue-title">{parentIssue.title}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,14 +16,13 @@
|
|||||||
import core, { IdMap, Ref, SortingOrder, StatusCategory, WithLookup, toIdMap } from '@hcengineering/core'
|
import core, { IdMap, Ref, SortingOrder, StatusCategory, WithLookup, toIdMap } from '@hcengineering/core'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import task, { getStates } from '@hcengineering/task'
|
import task, { getStates } from '@hcengineering/task'
|
||||||
|
import { typeStore } from '@hcengineering/task-resources'
|
||||||
import { Issue, Project } from '@hcengineering/tracker'
|
import { Issue, Project } from '@hcengineering/tracker'
|
||||||
import { Button, ButtonKind, ButtonSize, ProgressCircle, SelectPopup, showPanel } from '@hcengineering/ui'
|
import { Button, ButtonKind, ButtonSize, ProgressCircle, SelectPopup, showPanel } from '@hcengineering/ui'
|
||||||
import { statusStore } from '@hcengineering/view-resources'
|
import { statusStore } from '@hcengineering/view-resources'
|
||||||
import { getIssueId } from '../../../issues'
|
|
||||||
import tracker from '../../../plugin'
|
import tracker from '../../../plugin'
|
||||||
import { listIssueStatusOrder } from '../../../utils'
|
import { listIssueStatusOrder } from '../../../utils'
|
||||||
import IssueStatusIcon from '../IssueStatusIcon.svelte'
|
import IssueStatusIcon from '../IssueStatusIcon.svelte'
|
||||||
import { typeStore } from '@hcengineering/task-resources'
|
|
||||||
|
|
||||||
export let value: WithLookup<Issue>
|
export let value: WithLookup<Issue>
|
||||||
export let currentProject: Project | undefined = undefined
|
export let currentProject: Project | undefined = undefined
|
||||||
@ -116,7 +115,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: subIssuesValue = _subIssues.map((iss) => {
|
$: subIssuesValue = _subIssues.map((iss) => {
|
||||||
const text = project != null ? `${getIssueId(project, iss)} ${iss.title}` : iss.title
|
const text = `${iss.identifier} ${iss.title}`
|
||||||
const c = $statusStore.byId.get(iss.status)?.category
|
const c = $statusStore.byId.get(iss.status)?.category
|
||||||
const category = c !== undefined ? categories.get(c) : undefined
|
const category = c !== undefined ? categories.get(c) : undefined
|
||||||
return {
|
return {
|
||||||
|
@ -13,19 +13,17 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Issue, Project } from '@hcengineering/tracker'
|
import { Issue } from '@hcengineering/tracker'
|
||||||
import { statusStore } from '@hcengineering/view-resources'
|
import { statusStore } from '@hcengineering/view-resources'
|
||||||
|
|
||||||
import IssueStatusIcon from '../IssueStatusIcon.svelte'
|
import IssueStatusIcon from '../IssueStatusIcon.svelte'
|
||||||
import { getIssueId } from '../../../issues'
|
|
||||||
|
|
||||||
export let project: Project | undefined
|
|
||||||
export let issue: Issue
|
export let issue: Issue
|
||||||
export let size: 'small' | 'medium' | 'large' = 'small'
|
export let size: 'small' | 'medium' | 'large' = 'small'
|
||||||
|
|
||||||
$: status = $statusStore.byId.get(issue.status)
|
$: status = $statusStore.byId.get(issue.status)
|
||||||
$: huge = size === 'medium' || size === 'large'
|
$: huge = size === 'medium' || size === 'large'
|
||||||
$: text = project ? `${getIssueId(project, issue)} ${issue.title}` : issue.title
|
$: text = `${issue.identifier} ${issue.title}`
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex-row-center">
|
<div class="flex-row-center">
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
import { Issue } from '@hcengineering/tracker'
|
import { Issue } from '@hcengineering/tracker'
|
||||||
import { ListView, deviceOptionsStore as deviceInfo, getEventPositionElement, showPopup } from '@hcengineering/ui'
|
import { ListView, deviceOptionsStore as deviceInfo, getEventPositionElement, showPopup } from '@hcengineering/ui'
|
||||||
import { ContextMenu, FixedColumn, ListSelectionProvider } from '@hcengineering/view-resources'
|
import { ContextMenu, FixedColumn, ListSelectionProvider } from '@hcengineering/view-resources'
|
||||||
import { getIssueId } from '../../../issues'
|
|
||||||
import tracker from '../../../plugin'
|
import tracker from '../../../plugin'
|
||||||
import { activeProjects } from '../../../utils'
|
import { activeProjects } from '../../../utils'
|
||||||
import EstimationEditor from './EstimationEditor.svelte'
|
import EstimationEditor from './EstimationEditor.svelte'
|
||||||
@ -52,9 +51,7 @@
|
|||||||
>
|
>
|
||||||
<div class="flex-row-center clear-mins gap-2 flex-grow mr-4" class:p-text={twoRows}>
|
<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'}>
|
<FixedColumn key={'estimation_issue'} justify={'left'} addClass={'fs-bold'}>
|
||||||
{#if currentProject}
|
{issue.identifier}
|
||||||
{getIssueId(currentProject, issue)}
|
|
||||||
{/if}
|
|
||||||
</FixedColumn>
|
</FixedColumn>
|
||||||
<span class="overflow-label fs-bold caption-color" title={issue.title}>
|
<span class="overflow-label fs-bold caption-color" title={issue.title}>
|
||||||
{issue.title}
|
{issue.title}
|
||||||
|
@ -14,23 +14,22 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact from '@hcengineering/contact'
|
import contact from '@hcengineering/contact'
|
||||||
import { Ref, Space, WithLookup } from '@hcengineering/core'
|
|
||||||
import { UserBox } from '@hcengineering/contact-resources'
|
import { UserBox } from '@hcengineering/contact-resources'
|
||||||
|
import { Ref, Space, WithLookup } from '@hcengineering/core'
|
||||||
import { Project, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
|
import { Project, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
|
||||||
import {
|
import {
|
||||||
|
DatePresenter,
|
||||||
|
ListView,
|
||||||
deviceOptionsStore as deviceInfo,
|
deviceOptionsStore as deviceInfo,
|
||||||
eventToHTMLElement,
|
eventToHTMLElement,
|
||||||
getEventPositionElement,
|
getEventPositionElement,
|
||||||
ListView,
|
showPopup
|
||||||
showPopup,
|
|
||||||
DatePresenter
|
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import { ContextMenu, FixedColumn, ListSelectionProvider } from '@hcengineering/view-resources'
|
import { ContextMenu, FixedColumn, ListSelectionProvider } from '@hcengineering/view-resources'
|
||||||
import { getIssueId } from '../../../issues'
|
|
||||||
import tracker from '../../../plugin'
|
import tracker from '../../../plugin'
|
||||||
|
import { activeProjects } from '../../../utils'
|
||||||
import TimePresenter from './TimePresenter.svelte'
|
import TimePresenter from './TimePresenter.svelte'
|
||||||
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
||||||
import { activeProjects } from '../../../utils'
|
|
||||||
|
|
||||||
export let reports: WithLookup<TimeSpendReport>[]
|
export let reports: WithLookup<TimeSpendReport>[]
|
||||||
|
|
||||||
@ -86,8 +85,8 @@
|
|||||||
>
|
>
|
||||||
<div class="flex-row-center clear-mins gap-2 flex-grow mr-4" class:p-text={twoRows}>
|
<div class="flex-row-center clear-mins gap-2 flex-grow mr-4" class:p-text={twoRows}>
|
||||||
<FixedColumn key={'timespend_issue'} justify={'left'} addClass={'fs-bold'}>
|
<FixedColumn key={'timespend_issue'} justify={'left'} addClass={'fs-bold'}>
|
||||||
{#if currentProject && report.$lookup?.attachedTo}
|
{#if report.$lookup?.attachedTo}
|
||||||
{getIssueId(currentProject, report.$lookup?.attachedTo)}
|
{report.$lookup?.attachedTo?.identifier}
|
||||||
{/if}
|
{/if}
|
||||||
</FixedColumn>
|
</FixedColumn>
|
||||||
{#if report.$lookup?.attachedTo?.title}
|
{#if report.$lookup?.attachedTo?.title}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import presentation, { Card } from '@hcengineering/presentation'
|
|
||||||
import EditBox from '@hcengineering/ui/src/components/EditBox.svelte'
|
|
||||||
import { createEventDispatcher } from 'svelte'
|
|
||||||
import tracker from '../../plugin'
|
|
||||||
|
|
||||||
export let identifier: string
|
|
||||||
export let projectsIdentifiers: Set<string>
|
|
||||||
|
|
||||||
let newIdentifier = identifier
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
function save () {
|
|
||||||
dispatch('close', newIdentifier)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
label={projectsIdentifiers.has(newIdentifier) ? tracker.string.IdentifierExists : tracker.string.ProjectIdentifier}
|
|
||||||
okLabel={presentation.string.Save}
|
|
||||||
okAction={save}
|
|
||||||
canSave={!!newIdentifier && newIdentifier !== identifier && !projectsIdentifiers.has(newIdentifier)}
|
|
||||||
on:close={() => {
|
|
||||||
dispatch('close')
|
|
||||||
}}
|
|
||||||
on:changeContent
|
|
||||||
>
|
|
||||||
<div class="float-left-box">
|
|
||||||
<div class="float-left p-2">
|
|
||||||
<EditBox bind:value={newIdentifier} uppercase />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
@ -130,9 +130,6 @@
|
|||||||
if (projectData.defaultTimeReportDay !== project?.defaultTimeReportDay) {
|
if (projectData.defaultTimeReportDay !== project?.defaultTimeReportDay) {
|
||||||
update.defaultTimeReportDay = projectData.defaultTimeReportDay
|
update.defaultTimeReportDay = projectData.defaultTimeReportDay
|
||||||
}
|
}
|
||||||
if (projectData.identifier.toUpperCase() !== project?.identifier) {
|
|
||||||
update.identifier = projectData.identifier.toUpperCase()
|
|
||||||
}
|
|
||||||
if (projectData.members.length !== project?.members.length) {
|
if (projectData.members.length !== project?.members.length) {
|
||||||
update.members = projectData.members
|
update.members = projectData.members
|
||||||
} else {
|
} else {
|
||||||
@ -180,7 +177,6 @@
|
|||||||
close(projectId)
|
close(projectId)
|
||||||
} else {
|
} else {
|
||||||
isSaving = false
|
isSaving = false
|
||||||
changeIdentity(changeIdentityRef)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,13 +192,6 @@
|
|||||||
}
|
}
|
||||||
showPopup(IconPicker, { icon, color, icons }, 'top', update, update)
|
showPopup(IconPicker, { icon, color, icons }, 'top', update, update)
|
||||||
}
|
}
|
||||||
function changeIdentity (element: HTMLElement): void {
|
|
||||||
showPopup(ChangeIdentity, { identifier, projectsIdentifiers }, element, (result) => {
|
|
||||||
if (result != null) {
|
|
||||||
identifier = result.toLocaleUpperCase()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function close (id?: Ref<Project>): void {
|
function close (id?: Ref<Project>): void {
|
||||||
dispatch('close', id)
|
dispatch('close', id)
|
||||||
@ -286,17 +275,7 @@
|
|||||||
kind={'large-style'}
|
kind={'large-style'}
|
||||||
uppercase
|
uppercase
|
||||||
/>
|
/>
|
||||||
{#if !isNew}
|
{#if !isSaving && projectsIdentifiers.has(identifier.toUpperCase())}
|
||||||
<div class="ml-1">
|
|
||||||
<Button
|
|
||||||
size={'small'}
|
|
||||||
icon={IconEdit}
|
|
||||||
on:click={(ev) => {
|
|
||||||
changeIdentity(eventToHTMLElement(ev))
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{:else if !isSaving && projectsIdentifiers.has(identifier.toUpperCase())}
|
|
||||||
<div class="absolute overflow-label duplicated-identifier">
|
<div class="absolute overflow-label duplicated-identifier">
|
||||||
<Label label={tracker.string.IdentifierExists} />
|
<Label label={tracker.string.IdentifierExists} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,21 +14,21 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import core, {
|
import core, {
|
||||||
|
ClassifierKind,
|
||||||
|
DOMAIN_CONFIGURATION,
|
||||||
|
DOMAIN_MODEL,
|
||||||
|
getCurrentAccount,
|
||||||
|
toIdMap,
|
||||||
type AttachedDoc,
|
type AttachedDoc,
|
||||||
type Class,
|
type Class,
|
||||||
ClassifierKind,
|
|
||||||
type Client,
|
type Client,
|
||||||
type Doc,
|
type Doc,
|
||||||
type DocumentQuery,
|
type DocumentQuery,
|
||||||
DOMAIN_MODEL,
|
|
||||||
getCurrentAccount,
|
|
||||||
type Ref,
|
type Ref,
|
||||||
type RelatedDocument,
|
type RelatedDocument,
|
||||||
toIdMap,
|
type TxOperations
|
||||||
type TxOperations,
|
|
||||||
DOMAIN_CONFIGURATION
|
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { type Resources, translate } from '@hcengineering/platform'
|
import { translate, type Resources } from '@hcengineering/platform'
|
||||||
import { getClient, MessageBox, type ObjectSearchResult } from '@hcengineering/presentation'
|
import { getClient, MessageBox, type ObjectSearchResult } from '@hcengineering/presentation'
|
||||||
import { type Issue, type Milestone, type Project } from '@hcengineering/tracker'
|
import { type Issue, type Milestone, type Project } from '@hcengineering/tracker'
|
||||||
import { getCurrentLocation, navigate, showPopup, themeStore } from '@hcengineering/ui'
|
import { getCurrentLocation, navigate, showPopup, themeStore } from '@hcengineering/ui'
|
||||||
@ -45,23 +45,22 @@ import ProjectComponents from './components/components/ProjectComponents.svelte'
|
|||||||
import CreateIssue from './components/CreateIssue.svelte'
|
import CreateIssue from './components/CreateIssue.svelte'
|
||||||
import EditRelatedTargets from './components/EditRelatedTargets.svelte'
|
import EditRelatedTargets from './components/EditRelatedTargets.svelte'
|
||||||
import EditRelatedTargetsPopup from './components/EditRelatedTargetsPopup.svelte'
|
import EditRelatedTargetsPopup from './components/EditRelatedTargetsPopup.svelte'
|
||||||
import SettingsRelatedTargets from './components/SettingsRelatedTargets.svelte'
|
|
||||||
import Inbox from './components/inbox/Inbox.svelte'
|
import Inbox from './components/inbox/Inbox.svelte'
|
||||||
import AssigneeEditor from './components/issues/AssigneeEditor.svelte'
|
import AssigneeEditor from './components/issues/AssigneeEditor.svelte'
|
||||||
import DueDatePresenter from './components/issues/DueDatePresenter.svelte'
|
import DueDatePresenter from './components/issues/DueDatePresenter.svelte'
|
||||||
import EditIssue from './components/issues/edit/EditIssue.svelte'
|
import EditIssue from './components/issues/edit/EditIssue.svelte'
|
||||||
import IssueItem from './components/issues/IssueItem.svelte'
|
import IssueItem from './components/issues/IssueItem.svelte'
|
||||||
import IssueSearchIcon from './components/issues/IssueSearchIcon.svelte'
|
|
||||||
import IssuePresenter from './components/issues/IssuePresenter.svelte'
|
import IssuePresenter from './components/issues/IssuePresenter.svelte'
|
||||||
import IssuePreview from './components/issues/IssuePreview.svelte'
|
import IssuePreview from './components/issues/IssuePreview.svelte'
|
||||||
import Issues from './components/issues/Issues.svelte'
|
import Issues from './components/issues/Issues.svelte'
|
||||||
|
import IssueSearchIcon from './components/issues/IssueSearchIcon.svelte'
|
||||||
import IssuesView from './components/issues/IssuesView.svelte'
|
import IssuesView from './components/issues/IssuesView.svelte'
|
||||||
import KanbanView from './components/issues/KanbanView.svelte'
|
import KanbanView from './components/issues/KanbanView.svelte'
|
||||||
import ModificationDatePresenter from './components/issues/ModificationDatePresenter.svelte'
|
import ModificationDatePresenter from './components/issues/ModificationDatePresenter.svelte'
|
||||||
import NotificationIssuePresenter from './components/issues/NotificationIssuePresenter.svelte'
|
import NotificationIssuePresenter from './components/issues/NotificationIssuePresenter.svelte'
|
||||||
import PriorityEditor from './components/issues/PriorityEditor.svelte'
|
import PriorityEditor from './components/issues/PriorityEditor.svelte'
|
||||||
import PriorityInlineEditor from './components/issues/PriorityInlineEditor.svelte'
|
|
||||||
import PriorityFilterValuePresenter from './components/issues/PriorityFilterValuePresenter.svelte'
|
import PriorityFilterValuePresenter from './components/issues/PriorityFilterValuePresenter.svelte'
|
||||||
|
import PriorityInlineEditor from './components/issues/PriorityInlineEditor.svelte'
|
||||||
import PriorityPresenter from './components/issues/PriorityPresenter.svelte'
|
import PriorityPresenter from './components/issues/PriorityPresenter.svelte'
|
||||||
import PriorityRefPresenter from './components/issues/PriorityRefPresenter.svelte'
|
import PriorityRefPresenter from './components/issues/PriorityRefPresenter.svelte'
|
||||||
import RelatedIssueSelector from './components/issues/related/RelatedIssueSelector.svelte'
|
import RelatedIssueSelector from './components/issues/related/RelatedIssueSelector.svelte'
|
||||||
@ -75,21 +74,21 @@ import MilestoneDatePresenter from './components/milestones/MilestoneDatePresent
|
|||||||
import MyIssues from './components/myissues/MyIssues.svelte'
|
import MyIssues from './components/myissues/MyIssues.svelte'
|
||||||
import NewIssueHeader from './components/NewIssueHeader.svelte'
|
import NewIssueHeader from './components/NewIssueHeader.svelte'
|
||||||
import NopeComponent from './components/NopeComponent.svelte'
|
import NopeComponent from './components/NopeComponent.svelte'
|
||||||
|
import MembersArrayEditor from './components/projects/MembersArrayEditor.svelte'
|
||||||
import ProjectFilterValuePresenter from './components/projects/ProjectFilterValuePresenter.svelte'
|
import ProjectFilterValuePresenter from './components/projects/ProjectFilterValuePresenter.svelte'
|
||||||
import RelationsPopup from './components/RelationsPopup.svelte'
|
import RelationsPopup from './components/RelationsPopup.svelte'
|
||||||
import SetDueDateActionPopup from './components/SetDueDateActionPopup.svelte'
|
import SetDueDateActionPopup from './components/SetDueDateActionPopup.svelte'
|
||||||
import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.svelte'
|
import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.svelte'
|
||||||
|
import SettingsRelatedTargets from './components/SettingsRelatedTargets.svelte'
|
||||||
import CreateIssueTemplate from './components/templates/CreateIssueTemplate.svelte'
|
import CreateIssueTemplate from './components/templates/CreateIssueTemplate.svelte'
|
||||||
import MembersArrayEditor from './components/projects/MembersArrayEditor.svelte'
|
|
||||||
import {
|
import {
|
||||||
getIssueId,
|
getIssueTitle,
|
||||||
|
getTitle,
|
||||||
issueIdentifierProvider,
|
issueIdentifierProvider,
|
||||||
issueIdProvider,
|
|
||||||
issueLinkFragmentProvider,
|
issueLinkFragmentProvider,
|
||||||
issueLinkProvider,
|
issueLinkProvider,
|
||||||
getIssueTitle,
|
issueTitleProvider,
|
||||||
resolveLocation,
|
resolveLocation
|
||||||
issueTitleProvider
|
|
||||||
} from './issues'
|
} from './issues'
|
||||||
import tracker from './plugin'
|
import tracker from './plugin'
|
||||||
|
|
||||||
@ -98,9 +97,9 @@ import MilestonePresenter from './components/milestones/MilestonePresenter.svelt
|
|||||||
import Milestones from './components/milestones/Milestones.svelte'
|
import Milestones from './components/milestones/Milestones.svelte'
|
||||||
import MilestoneSelector from './components/milestones/MilestoneSelector.svelte'
|
import MilestoneSelector from './components/milestones/MilestoneSelector.svelte'
|
||||||
import MilestoneStatusEditor from './components/milestones/MilestoneStatusEditor.svelte'
|
import MilestoneStatusEditor from './components/milestones/MilestoneStatusEditor.svelte'
|
||||||
|
import MilestoneStatusIcon from './components/milestones/MilestoneStatusIcon.svelte'
|
||||||
import MilestoneStatusPresenter from './components/milestones/MilestoneStatusPresenter.svelte'
|
import MilestoneStatusPresenter from './components/milestones/MilestoneStatusPresenter.svelte'
|
||||||
import MilestoneTitlePresenter from './components/milestones/MilestoneTitlePresenter.svelte'
|
import MilestoneTitlePresenter from './components/milestones/MilestoneTitlePresenter.svelte'
|
||||||
import MilestoneStatusIcon from './components/milestones/MilestoneStatusIcon.svelte'
|
|
||||||
|
|
||||||
import SubIssuesSelector from './components/issues/edit/SubIssuesSelector.svelte'
|
import SubIssuesSelector from './components/issues/edit/SubIssuesSelector.svelte'
|
||||||
import EstimationEditor from './components/issues/timereport/EstimationEditor.svelte'
|
import EstimationEditor from './components/issues/timereport/EstimationEditor.svelte'
|
||||||
@ -125,9 +124,9 @@ import {
|
|||||||
getAllMilestones,
|
getAllMilestones,
|
||||||
getAllPriority,
|
getAllPriority,
|
||||||
getComponentTitle,
|
getComponentTitle,
|
||||||
|
getIssueChatTitle,
|
||||||
getIssueStatusCategories,
|
getIssueStatusCategories,
|
||||||
getMilestoneTitle,
|
getMilestoneTitle,
|
||||||
getIssueChatTitle,
|
|
||||||
getVisibleFilters,
|
getVisibleFilters,
|
||||||
issuePrioritySort,
|
issuePrioritySort,
|
||||||
issueStatusSort,
|
issueStatusSort,
|
||||||
@ -141,7 +140,9 @@ import PriorityIcon from './components/activity/PriorityIcon.svelte'
|
|||||||
import StatusIcon from './components/activity/StatusIcon.svelte'
|
import StatusIcon from './components/activity/StatusIcon.svelte'
|
||||||
import TxIssueCreated from './components/activity/TxIssueCreated.svelte'
|
import TxIssueCreated from './components/activity/TxIssueCreated.svelte'
|
||||||
import DeleteComponentPresenter from './components/components/DeleteComponentPresenter.svelte'
|
import DeleteComponentPresenter from './components/components/DeleteComponentPresenter.svelte'
|
||||||
|
import IssueStatusIcon from './components/issues/IssueStatusIcon.svelte'
|
||||||
import MoveIssues from './components/issues/Move.svelte'
|
import MoveIssues from './components/issues/Move.svelte'
|
||||||
|
import PriorityIconPresenter from './components/issues/PriorityIconPresenter.svelte'
|
||||||
import StatusRefPresenter from './components/issues/StatusRefPresenter.svelte'
|
import StatusRefPresenter from './components/issues/StatusRefPresenter.svelte'
|
||||||
import TimeSpendReportPopup from './components/issues/timereport/TimeSpendReportPopup.svelte'
|
import TimeSpendReportPopup from './components/issues/timereport/TimeSpendReportPopup.svelte'
|
||||||
import IssueStatistics from './components/milestones/IssueStatistics.svelte'
|
import IssueStatistics from './components/milestones/IssueStatistics.svelte'
|
||||||
@ -150,21 +151,19 @@ import MilestoneRefPresenter from './components/milestones/MilestoneRefPresenter
|
|||||||
import CreateProject from './components/projects/CreateProject.svelte'
|
import CreateProject from './components/projects/CreateProject.svelte'
|
||||||
import ProjectPresenter from './components/projects/ProjectPresenter.svelte'
|
import ProjectPresenter from './components/projects/ProjectPresenter.svelte'
|
||||||
import ProjectSpacePresenter from './components/projects/ProjectSpacePresenter.svelte'
|
import ProjectSpacePresenter from './components/projects/ProjectSpacePresenter.svelte'
|
||||||
import IssueStatusIcon from './components/issues/IssueStatusIcon.svelte'
|
|
||||||
import PriorityIconPresenter from './components/issues/PriorityIconPresenter.svelte'
|
|
||||||
|
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
|
|
||||||
|
import { settingId } from '@hcengineering/setting'
|
||||||
import EstimationValueEditor from './components/issues/timereport/EstimationValueEditor.svelte'
|
import EstimationValueEditor from './components/issues/timereport/EstimationValueEditor.svelte'
|
||||||
import TimePresenter from './components/issues/timereport/TimePresenter.svelte'
|
import TimePresenter from './components/issues/timereport/TimePresenter.svelte'
|
||||||
import { settingId } from '@hcengineering/setting'
|
|
||||||
|
|
||||||
export { default as AssigneeEditor } from './components/issues/AssigneeEditor.svelte'
|
export { default as AssigneeEditor } from './components/issues/AssigneeEditor.svelte'
|
||||||
export { default as SubIssueList } from './components/issues/edit/SubIssueList.svelte'
|
export { default as SubIssueList } from './components/issues/edit/SubIssueList.svelte'
|
||||||
export { default as IssueStatusIcon } from './components/issues/IssueStatusIcon.svelte'
|
export { default as IssueStatusIcon } from './components/issues/IssueStatusIcon.svelte'
|
||||||
export { default as StatusPresenter } from './components/issues/StatusPresenter.svelte'
|
export { default as StatusPresenter } from './components/issues/StatusPresenter.svelte'
|
||||||
|
|
||||||
export { CreateProject, IssuePresenter, PriorityEditor, StatusEditor, TitlePresenter, activeProjects }
|
export { activeProjects, CreateProject, IssuePresenter, PriorityEditor, StatusEditor, TitlePresenter }
|
||||||
|
|
||||||
export async function queryIssue<D extends Issue> (
|
export async function queryIssue<D extends Issue> (
|
||||||
_class: Ref<Class<D>>,
|
_class: Ref<Class<D>>,
|
||||||
@ -172,9 +171,7 @@ export async function queryIssue<D extends Issue> (
|
|||||||
search: string,
|
search: string,
|
||||||
filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }
|
filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }
|
||||||
): Promise<ObjectSearchResult[]> {
|
): Promise<ObjectSearchResult[]> {
|
||||||
const projects = await client.findAll<Project>(tracker.class.Project, {})
|
const q: DocumentQuery<Issue> = { identifier: { $like: `%${search}%` } }
|
||||||
|
|
||||||
const q: DocumentQuery<Issue> = { title: { $like: `%${search}%` } }
|
|
||||||
if (filter?.in !== undefined || filter?.nin !== undefined) {
|
if (filter?.in !== undefined || filter?.nin !== undefined) {
|
||||||
q._id = {}
|
q._id = {}
|
||||||
if (filter.in !== undefined) {
|
if (filter.in !== undefined) {
|
||||||
@ -185,40 +182,28 @@ export async function queryIssue<D extends Issue> (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const named = toIdMap(
|
const numbered = toIdMap(
|
||||||
await client.findAll<Issue>(_class, q, {
|
await client.findAll<Issue>(_class, q, {
|
||||||
limit: 200,
|
limit: 200
|
||||||
lookup: { space: tracker.class.Project }
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
for (const currentProject of projects) {
|
|
||||||
const nids: number[] = []
|
const q2: DocumentQuery<Issue> = { title: { $like: `%${search}%` } }
|
||||||
for (let n = 0; n <= currentProject.sequence; n++) {
|
if (q._id !== undefined) {
|
||||||
const v = `${currentProject.identifier}-${n}`
|
q2._id = q._id
|
||||||
if (v.includes(search)) {
|
}
|
||||||
nids.push(n)
|
const named = await client.findAll<Issue>(_class, q2, { limit: 200 })
|
||||||
}
|
for (const d of named) {
|
||||||
}
|
if (d.identifier.includes(search) || d.title.includes(search)) {
|
||||||
if (nids.length > 0) {
|
if (!numbered.has(d._id)) {
|
||||||
const q2: DocumentQuery<Issue> = { number: { $in: nids } }
|
numbered.set(d._id, d)
|
||||||
if (q._id !== undefined) {
|
|
||||||
q2._id = q._id
|
|
||||||
}
|
|
||||||
const numbered = await client.findAll<Issue>(_class, q2, { limit: 200, lookup: { space: tracker.class.Project } })
|
|
||||||
for (const d of numbered) {
|
|
||||||
const shortId = `${projects.find((it) => it._id === d.space)?.identifier ?? ''}-${d.number}`
|
|
||||||
if (shortId.includes(search) || d.title.includes(search)) {
|
|
||||||
if (!named.has(d._id)) {
|
|
||||||
named.set(d._id, d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(named.values()).map((e) => ({
|
return Array.from(numbered.values()).map((e) => ({
|
||||||
doc: e,
|
doc: e,
|
||||||
title: getIssueId(e.$lookup?.space as Project, e),
|
title: e.identifier,
|
||||||
icon: tracker.icon.TrackerApplication,
|
icon: tracker.icon.TrackerApplication,
|
||||||
component: IssueItem
|
component: IssueItem
|
||||||
}))
|
}))
|
||||||
@ -533,7 +518,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
IssueTitleProvider: issueTitleProvider,
|
IssueTitleProvider: issueTitleProvider,
|
||||||
ComponentTitleProvider: getComponentTitle,
|
ComponentTitleProvider: getComponentTitle,
|
||||||
MilestoneTitleProvider: getMilestoneTitle,
|
MilestoneTitleProvider: getMilestoneTitle,
|
||||||
GetIssueId: issueIdProvider,
|
GetIssueId: getTitle,
|
||||||
GetIssueLink: issueLinkProvider,
|
GetIssueLink: issueLinkProvider,
|
||||||
GetIssueLinkFragment: issueLinkFragmentProvider,
|
GetIssueLinkFragment: issueLinkFragmentProvider,
|
||||||
GetIssueTitle: getIssueTitle,
|
GetIssueTitle: getIssueTitle,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { type Doc, type DocumentUpdate, type Ref, type RelatedDocument, type TxOperations } from '@hcengineering/core'
|
import { type Doc, type DocumentUpdate, type Ref, type RelatedDocument, type TxOperations } from '@hcengineering/core'
|
||||||
import presentation, { getClient } from '@hcengineering/presentation'
|
|
||||||
import { getMetadata } from '@hcengineering/platform'
|
import { getMetadata } from '@hcengineering/platform'
|
||||||
import { type Component, type Issue, type Project, type Milestone, trackerId } from '@hcengineering/tracker'
|
import presentation, { getClient } from '@hcengineering/presentation'
|
||||||
import { type Location, type ResolvedLocation, getPanelURI, getCurrentResolvedLocation } from '@hcengineering/ui'
|
import { trackerId, type Component, type Issue, type Milestone } from '@hcengineering/tracker'
|
||||||
|
import { getCurrentResolvedLocation, getPanelURI, type Location, type ResolvedLocation } from '@hcengineering/ui'
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
import { workbenchId } from '@hcengineering/workbench'
|
||||||
import { writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
import tracker from './plugin'
|
import tracker from './plugin'
|
||||||
@ -10,22 +10,14 @@ import tracker from './plugin'
|
|||||||
export const activeComponent = writable<Ref<Component> | undefined>(undefined)
|
export const activeComponent = writable<Ref<Component> | undefined>(undefined)
|
||||||
export const activeMilestone = writable<Ref<Milestone> | undefined>(undefined)
|
export const activeMilestone = writable<Ref<Milestone> | undefined>(undefined)
|
||||||
|
|
||||||
export function getIssueId (project: Project, issue: Issue): string {
|
|
||||||
return `${project.identifier}-${issue.number}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isIssueId (shortLink: string): boolean {
|
export function isIssueId (shortLink: string): boolean {
|
||||||
return /^\S+-\d+$/.test(shortLink)
|
return /^\S+-\d+$/.test(shortLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function issueIdentifierProvider (client: TxOperations, ref: Ref<Doc>): Promise<string> {
|
export async function issueIdentifierProvider (client: TxOperations, ref: Ref<Doc>): Promise<string> {
|
||||||
const object = await client.findOne(
|
const object = await client.findOne(tracker.class.Issue, { _id: ref as Ref<Issue> })
|
||||||
tracker.class.Issue,
|
if (object === undefined) throw new Error(`Issue project not found, _id: ${ref}`)
|
||||||
{ _id: ref as Ref<Issue> },
|
return object.identifier
|
||||||
{ lookup: { space: tracker.class.Project } }
|
|
||||||
)
|
|
||||||
if (object?.$lookup?.space === undefined) throw new Error(`Issue project not found, _id: ${ref}`)
|
|
||||||
return getIssueId(object.$lookup.space, object)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function issueTitleProvider (client: TxOperations, ref: Ref<Doc>): Promise<string> {
|
export async function issueTitleProvider (client: TxOperations, ref: Ref<Doc>): Promise<string> {
|
||||||
@ -42,22 +34,15 @@ export async function issueTitleProvider (client: TxOperations, ref: Ref<Doc>):
|
|||||||
return await getIssueTitle(object)
|
return await getIssueTitle(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTitle (doc: Doc): Promise<string> {
|
export async function getTitle (doc: Doc): Promise<string> {
|
||||||
const client = getClient()
|
|
||||||
const issue = doc as Issue
|
const issue = doc as Issue
|
||||||
const object = await client.findOne(tracker.class.Project, { _id: issue.space })
|
return issue.identifier
|
||||||
if (object === undefined) return `?-${issue.number}`
|
|
||||||
return getIssueId(object, issue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateIssuePanelUri (issue: Issue): string {
|
export function generateIssuePanelUri (issue: Issue): string {
|
||||||
return getPanelURI(tracker.component.EditIssue, issue._id, issue._class, 'content')
|
return getPanelURI(tracker.component.EditIssue, issue._id, issue._class, 'content')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function issueIdProvider (doc: Doc): Promise<string> {
|
|
||||||
return await getTitle(doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function issueLinkFragmentProvider (doc: Doc): Promise<Location> {
|
export async function issueLinkFragmentProvider (doc: Doc): Promise<Location> {
|
||||||
const loc = getCurrentResolvedLocation()
|
const loc = getCurrentResolvedLocation()
|
||||||
loc.path.length = 2
|
loc.path.length = 2
|
||||||
@ -85,21 +70,8 @@ export function generateIssueShortLink (issueId: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function generateIssueLocation (loc: Location, issueId: string): Promise<ResolvedLocation | undefined> {
|
export async function generateIssueLocation (loc: Location, issueId: string): Promise<ResolvedLocation | undefined> {
|
||||||
const tokens = issueId.split('-')
|
|
||||||
if (tokens.length < 2) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
const projectId = tokens[0]
|
|
||||||
const issueNumber = Number(tokens[1])
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const project = await client.findOne(tracker.class.Project, { identifier: projectId })
|
const issue = await client.findOne(tracker.class.Issue, { identifier: issueId })
|
||||||
if (project === undefined) {
|
|
||||||
console.error(
|
|
||||||
`Could not find project ${projectId}. Make sure you are in correct workspace and the project was not deleted or renamed.`
|
|
||||||
)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
const issue = await client.findOne(tracker.class.Issue, { number: issueNumber, space: project._id })
|
|
||||||
if (issue === undefined) {
|
if (issue === undefined) {
|
||||||
console.error(`Could not find issue ${issueId}.`)
|
console.error(`Could not find issue ${issueId}.`)
|
||||||
return undefined
|
return undefined
|
||||||
@ -112,7 +84,7 @@ export async function generateIssueLocation (loc: Location, issueId: string): Pr
|
|||||||
fragment: generateIssuePanelUri(issue)
|
fragment: generateIssuePanelUri(issue)
|
||||||
},
|
},
|
||||||
defaultLocation: {
|
defaultLocation: {
|
||||||
path: [appComponent, workspace, trackerId, project._id, 'issues'],
|
path: [appComponent, workspace, trackerId, issue.space, 'issues'],
|
||||||
fragment: generateIssuePanelUri(issue)
|
fragment: generateIssuePanelUri(issue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,6 +612,7 @@ export async function moveIssueToSpace (
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
const number = (incResult as any).object.sequence
|
||||||
await updateIssuesOnMove(
|
await updateIssuesOnMove(
|
||||||
client,
|
client,
|
||||||
applyOps,
|
applyOps,
|
||||||
@ -620,7 +621,8 @@ export async function moveIssueToSpace (
|
|||||||
{
|
{
|
||||||
...updates.get(doc._id),
|
...updates.get(doc._id),
|
||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
number: (incResult as any).object.sequence
|
number,
|
||||||
|
identifier: `${space.identifier}-${number}`
|
||||||
},
|
},
|
||||||
updates
|
updates
|
||||||
)
|
)
|
||||||
|
@ -57,11 +57,10 @@ async function updateSubIssues (
|
|||||||
*/
|
*/
|
||||||
export async function issueHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
export async function issueHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
||||||
const issue = doc as Issue
|
const issue = doc as Issue
|
||||||
const issueId = await getIssueId(issue, control)
|
|
||||||
const front = getMetadata(serverCore.metadata.FrontUrl) ?? ''
|
const front = getMetadata(serverCore.metadata.FrontUrl) ?? ''
|
||||||
const path = `${workbenchId}/${control.workspace.workspaceUrl}/${trackerId}/${issueId}`
|
const path = `${workbenchId}/${control.workspace.workspaceUrl}/${trackerId}/${issue.identifier}`
|
||||||
const link = concatLink(front, path)
|
const link = concatLink(front, path)
|
||||||
return `<a href="${link}">${issueId}</a> ${issue.title}`
|
return `<a href="${link}">${issue.identifier}</a> ${issue.title}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,11 +75,9 @@ export async function getIssueId (doc: Issue, control: TriggerControl): Promise<
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export async function issueTextPresenter (doc: Doc, control: TriggerControl): Promise<string> {
|
export async function issueTextPresenter (doc: Doc): Promise<string> {
|
||||||
const issue = doc as Issue
|
const issue = doc as Issue
|
||||||
const issueId = await getIssueId(issue, control)
|
return `${issue.identifier} ${issue.title}`
|
||||||
|
|
||||||
return `${issueId} ${issue.title}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSamePerson (control: TriggerControl, assignee: Ref<Person>, target: Ref<Account>): boolean {
|
function isSamePerson (control: TriggerControl, assignee: Ref<Person>, target: Ref<Account>): boolean {
|
||||||
@ -100,7 +97,7 @@ export async function getIssueNotificationContent (
|
|||||||
): Promise<NotificationContent> {
|
): Promise<NotificationContent> {
|
||||||
const issue = doc as Issue
|
const issue = doc as Issue
|
||||||
|
|
||||||
const issueShortName = await issueTextPresenter(doc, control)
|
const issueShortName = await issueTextPresenter(doc)
|
||||||
const issueTitle = `${issueShortName}: ${issue.title}`
|
const issueTitle = `${issueShortName}: ${issue.title}`
|
||||||
|
|
||||||
const title = tracker.string.IssueNotificationTitle
|
const title = tracker.string.IssueNotificationTitle
|
||||||
|
@ -102,9 +102,9 @@ export const contentStageId = 'cnt-v2b'
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export const fieldStateId = 'fld-v11'
|
export const fieldStateId = 'fld-v12'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export const fullTextPushStageId = 'fts-v9_2'
|
export const fullTextPushStageId = 'fts-v10b'
|
||||||
|
@ -90,11 +90,6 @@ export class EditProjectPage extends CommonTrackerPage {
|
|||||||
if (data.title != null) {
|
if (data.title != null) {
|
||||||
await this.inputTitle.fill(data.title)
|
await this.inputTitle.fill(data.title)
|
||||||
}
|
}
|
||||||
if (data.identifier != null) {
|
|
||||||
await this.buttonEditIdentifier.click()
|
|
||||||
await this.inputEditProjectIdentifier.fill(data.identifier)
|
|
||||||
await this.buttonEditProjectIdentifier.click()
|
|
||||||
}
|
|
||||||
if (data.description != null) {
|
if (data.description != null) {
|
||||||
await this.inputDescription.fill(data.description)
|
await this.inputDescription.fill(data.description)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,6 @@ test.describe('Tracker Projects tests', () => {
|
|||||||
}
|
}
|
||||||
const updateProjectData: NewProject = {
|
const updateProjectData: NewProject = {
|
||||||
title: 'UpdateProject',
|
title: 'UpdateProject',
|
||||||
identifier: 'UPDAT',
|
|
||||||
description: 'Updated Project description',
|
description: 'Updated Project description',
|
||||||
private: true,
|
private: true,
|
||||||
defaultAssigneeForIssues: 'Chen Rosamund',
|
defaultAssigneeForIssues: 'Chen Rosamund',
|
||||||
|
Loading…
Reference in New Issue
Block a user