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