mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-22 00:10:37 +00:00
Tracker: parent issues name (#2136)
Signed-off-by: Sergei Ogorelkov <sergei.ogorelkov@xored.com>
This commit is contained in:
parent
a74ae0ee89
commit
6f0bd0a6fe
@ -10,9 +10,10 @@ Tracker:
|
|||||||
|
|
||||||
- Remember view options
|
- Remember view options
|
||||||
- My issues
|
- My issues
|
||||||
|
- Names of parent issues
|
||||||
- Roadmap
|
- Roadmap
|
||||||
- Remember view options
|
|
||||||
- Context menus (Priority/Status/Assignee)
|
- Context menus (Priority/Status/Assignee)
|
||||||
|
-
|
||||||
|
|
||||||
Chunter:
|
Chunter:
|
||||||
|
|
||||||
|
@ -20,6 +20,6 @@ import serverTracker from '@anticrm/server-tracker'
|
|||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
export function createModel (builder: Builder): void {
|
||||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||||
trigger: serverTracker.trigger.OnIssueProjectUpdate
|
trigger: serverTracker.trigger.OnIssueUpdate
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ import task from '@anticrm/task'
|
|||||||
import {
|
import {
|
||||||
Document,
|
Document,
|
||||||
Issue,
|
Issue,
|
||||||
|
IssueParentInfo,
|
||||||
IssuePriority,
|
IssuePriority,
|
||||||
IssueStatus,
|
IssueStatus,
|
||||||
IssueStatusCategory,
|
IssueStatusCategory,
|
||||||
@ -180,6 +181,8 @@ export class TIssue extends TAttachedDoc implements Issue {
|
|||||||
@Prop(ArrOf(TypeRef(tracker.class.Issue)), tracker.string.RelatedTo)
|
@Prop(ArrOf(TypeRef(tracker.class.Issue)), tracker.string.RelatedTo)
|
||||||
relatedIssue!: Ref<Issue>[]
|
relatedIssue!: Ref<Issue>[]
|
||||||
|
|
||||||
|
parents!: IssueParentInfo[]
|
||||||
|
|
||||||
@Prop(Collection(chunter.class.Comment), tracker.string.Comments)
|
@Prop(Collection(chunter.class.Comment), tracker.string.Comments)
|
||||||
comments!: number
|
comments!: number
|
||||||
|
|
||||||
|
@ -225,6 +225,36 @@ async function migrateParentIssues (client: MigrationClient): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateIssueParentInfo (client: MigrationClient, parentIssue: Issue | null): Promise<void> {
|
||||||
|
const parents =
|
||||||
|
parentIssue === null ? [] : [{ parentId: parentIssue._id, parentTitle: parentIssue.title }, ...parentIssue.parents]
|
||||||
|
const migrationResult = await client.update<Issue>(
|
||||||
|
DOMAIN_TRACKER,
|
||||||
|
{
|
||||||
|
_class: tracker.class.Issue,
|
||||||
|
attachedTo: parentIssue?._id ?? tracker.ids.NoParent,
|
||||||
|
parents: { $exists: false }
|
||||||
|
},
|
||||||
|
{ parents }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (migrationResult.matched > 0) {
|
||||||
|
const subIssues = await client.find<Issue>(DOMAIN_TRACKER, {
|
||||||
|
_class: tracker.class.Issue,
|
||||||
|
attachedTo: parentIssue?._id ?? tracker.ids.NoParent,
|
||||||
|
subIssues: { $gt: 0 }
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const issue of subIssues) {
|
||||||
|
await updateIssueParentInfo(client, issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migrateIssueParentInfo (client: MigrationClient): Promise<void> {
|
||||||
|
await updateIssueParentInfo(client, null)
|
||||||
|
}
|
||||||
|
|
||||||
async function migrateIssueProjects (client: MigrationClient): Promise<void> {
|
async function migrateIssueProjects (client: MigrationClient): Promise<void> {
|
||||||
const issues = await client.find(DOMAIN_TRACKER, { _class: tracker.class.Issue, project: { $exists: false } })
|
const issues = await client.find(DOMAIN_TRACKER, { _class: tracker.class.Issue, project: { $exists: false } })
|
||||||
|
|
||||||
@ -288,6 +318,7 @@ async function upgradeProjects (tx: TxOperations): Promise<void> {
|
|||||||
export const trackerOperation: MigrateOperation = {
|
export const trackerOperation: MigrateOperation = {
|
||||||
async migrate (client: MigrationClient): Promise<void> {
|
async migrate (client: MigrationClient): Promise<void> {
|
||||||
await Promise.all([migrateIssueProjects(client), migrateParentIssues(client)])
|
await Promise.all([migrateIssueProjects(client), migrateParentIssues(client)])
|
||||||
|
await migrateIssueParentInfo(client)
|
||||||
},
|
},
|
||||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||||
const tx = new TxOperations(client, core.account.System)
|
const tx = new TxOperations(client, core.account.System)
|
||||||
|
@ -64,7 +64,8 @@
|
|||||||
priority: priority,
|
priority: priority,
|
||||||
dueDate: null,
|
dueDate: null,
|
||||||
comments: 0,
|
comments: 0,
|
||||||
subIssues: 0
|
subIssues: 0,
|
||||||
|
parents: []
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
@ -143,7 +144,10 @@
|
|||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
comments: 0,
|
comments: 0,
|
||||||
subIssues: 0,
|
subIssues: 0,
|
||||||
dueDate: object.dueDate
|
dueDate: object.dueDate,
|
||||||
|
parents: parentIssue
|
||||||
|
? [{ parentId: parentIssue._id, parentTitle: parentIssue.title }, ...parentIssue.parents]
|
||||||
|
: []
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.addCollection(
|
await client.addCollection(
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AttachedData, FindOptions, SortingOrder } from '@anticrm/core'
|
import { AttachedData, FindOptions, Ref, SortingOrder } from '@anticrm/core'
|
||||||
import { getClient, ObjectPopup } from '@anticrm/presentation'
|
import { getClient, ObjectPopup } from '@anticrm/presentation'
|
||||||
import { calcRank, Issue, IssueStatusCategory } from '@anticrm/tracker'
|
import { calcRank, Issue, IssueStatusCategory } from '@anticrm/tracker'
|
||||||
import { Icon } from '@anticrm/ui'
|
import { Icon } from '@anticrm/ui'
|
||||||
@ -25,7 +25,6 @@
|
|||||||
export let width: 'medium' | 'large' | 'full' = 'large'
|
export let width: 'medium' | 'large' | 'full' = 'large'
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const options: FindOptions<Issue> = {
|
const options: FindOptions<Issue> = {
|
||||||
lookup: { status: tracker.class.IssueStatus, space: tracker.class.Team },
|
lookup: { status: tracker.class.IssueStatus, space: tracker.class.Team },
|
||||||
@ -43,7 +42,12 @@
|
|||||||
async function onClose ({ detail: parentIssue }: CustomEvent<Issue | undefined | null>) {
|
async function onClose ({ detail: parentIssue }: CustomEvent<Issue | undefined | null>) {
|
||||||
const vv = Array.isArray(value) ? value : [value]
|
const vv = Array.isArray(value) ? value : [value]
|
||||||
for (const docValue of vv) {
|
for (const docValue of vv) {
|
||||||
if ('_id' in docValue && parentIssue !== undefined && parentIssue?._id !== docValue.attachedTo) {
|
if (
|
||||||
|
'_id' in docValue &&
|
||||||
|
parentIssue !== undefined &&
|
||||||
|
parentIssue?._id !== docValue.attachedTo &&
|
||||||
|
parentIssue?._id !== docValue._id
|
||||||
|
) {
|
||||||
let rank: string | null = null
|
let rank: string | null = null
|
||||||
|
|
||||||
if (parentIssue) {
|
if (parentIssue) {
|
||||||
@ -68,12 +72,24 @@
|
|||||||
|
|
||||||
$: selected = !Array.isArray(value) ? ('attachedTo' in value ? value.attachedTo : undefined) : undefined
|
$: selected = !Array.isArray(value) ? ('attachedTo' in value ? value.attachedTo : undefined) : undefined
|
||||||
$: ignoreObjects = !Array.isArray(value) ? ('_id' in value ? [value._id] : []) : undefined
|
$: ignoreObjects = !Array.isArray(value) ? ('_id' in value ? [value._id] : []) : undefined
|
||||||
|
$: docQuery = {
|
||||||
|
'parents.parentId': {
|
||||||
|
$nin: [
|
||||||
|
...new Set(
|
||||||
|
(Array.isArray(value) ? value : [value])
|
||||||
|
.map((issue) => ('_id' in issue ? issue._id : null))
|
||||||
|
.filter((x): x is Ref<Issue> => x !== null)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
$: updateIssueStatusCategories()
|
$: updateIssueStatusCategories()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ObjectPopup
|
<ObjectPopup
|
||||||
_class={tracker.class.Issue}
|
_class={tracker.class.Issue}
|
||||||
{options}
|
{options}
|
||||||
|
{docQuery}
|
||||||
{selected}
|
{selected}
|
||||||
multiSelect={false}
|
multiSelect={false}
|
||||||
allowDeselect={true}
|
allowDeselect={true}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
import AssigneePresenter from './AssigneePresenter.svelte'
|
import AssigneePresenter from './AssigneePresenter.svelte'
|
||||||
import SubIssuesSelector from './edit/SubIssuesSelector.svelte'
|
import SubIssuesSelector from './edit/SubIssuesSelector.svelte'
|
||||||
import IssuePresenter from './IssuePresenter.svelte'
|
import IssuePresenter from './IssuePresenter.svelte'
|
||||||
|
import ParentNamesPresenter from './ParentNamesPresenter.svelte'
|
||||||
import PriorityEditor from './PriorityEditor.svelte'
|
import PriorityEditor from './PriorityEditor.svelte'
|
||||||
|
|
||||||
export let currentSpace: Ref<Team> = tracker.team.DefaultTeam
|
export let currentSpace: Ref<Team> = tracker.team.DefaultTeam
|
||||||
@ -172,8 +173,11 @@
|
|||||||
showPanel(tracker.component.EditIssue, object._id, object._class, 'content')
|
showPanel(tracker.component.EditIssue, object._id, object._class, 'content')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex-col mr-6">
|
<div class="flex-col mr-8">
|
||||||
|
<div class="flex clear-mins names">
|
||||||
<IssuePresenter value={issue} />
|
<IssuePresenter value={issue} />
|
||||||
|
<ParentNamesPresenter value={issue} />
|
||||||
|
</div>
|
||||||
<span class="fs-bold caption-color mt-1 lines-limit-2">
|
<span class="fs-bold caption-color mt-1 lines-limit-2">
|
||||||
{object.title}
|
{object.title}
|
||||||
</span>
|
</span>
|
||||||
@ -210,6 +214,10 @@
|
|||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
.names {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding-bottom: 0.75rem;
|
padding-bottom: 0.75rem;
|
||||||
border-bottom: 1px solid var(--divider-color);
|
border-bottom: 1px solid var(--divider-color);
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Issue } from '@anticrm/tracker'
|
||||||
|
|
||||||
|
export let value: Issue | undefined
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if value}
|
||||||
|
<div class="root">
|
||||||
|
<span class="names">
|
||||||
|
{#each value.parents as parentInfo}
|
||||||
|
<span class="name">{parentInfo.parentTitle}</span>
|
||||||
|
{/each}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.names {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: var(--content-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.name::before {
|
||||||
|
content: '›';
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -14,25 +14,29 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Issue } from '@anticrm/tracker'
|
import type { Issue } from '@anticrm/tracker'
|
||||||
|
import ParentNamesPresenter from './ParentNamesPresenter.svelte'
|
||||||
|
|
||||||
export let value: Issue
|
export let value: Issue
|
||||||
export let shouldUseMargin: boolean = false
|
export let shouldUseMargin: boolean = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<span class="titleLabel flex-grow" class:mTitleLabelWithMargin={shouldUseMargin} title={value.title}
|
<span class="root" class:with-margin={shouldUseMargin} title={value.title}>
|
||||||
>{value.title}</span
|
<span class="name">{value.title}</span>
|
||||||
>
|
<ParentNamesPresenter {value} />
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.titleLabel {
|
.root {
|
||||||
flex-shrink: 10;
|
display: flex;
|
||||||
overflow: hidden;
|
flex-grow: 1;
|
||||||
|
min-width: 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
overflow: hidden;
|
||||||
|
flex-shrink: 10;
|
||||||
|
|
||||||
&.mTitleLabelWithMargin {
|
&.with-margin {
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,8 @@
|
|||||||
priority: IssuePriority.NoPriority,
|
priority: IssuePriority.NoPriority,
|
||||||
dueDate: null,
|
dueDate: null,
|
||||||
comments: 0,
|
comments: 0,
|
||||||
subIssues: 0
|
subIssues: 0,
|
||||||
|
parents: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +95,8 @@
|
|||||||
...newIssue,
|
...newIssue,
|
||||||
title: getTitle(newIssue.title),
|
title: getTitle(newIssue.title),
|
||||||
number: (incResult as any).object.sequence,
|
number: (incResult as any).object.sequence,
|
||||||
rank: calcRank(lastOne, undefined)
|
rank: calcRank(lastOne, undefined),
|
||||||
|
parents: [{ parentId: parentIssue._id, parentTitle: parentIssue.title }, ...parentIssue.parents]
|
||||||
}
|
}
|
||||||
|
|
||||||
const objectId = await client.addCollection(
|
const objectId = await client.addCollection(
|
||||||
|
@ -112,6 +112,7 @@ export interface Issue extends AttachedDoc {
|
|||||||
subIssues: number
|
subIssues: number
|
||||||
blockedBy?: Ref<Issue>[]
|
blockedBy?: Ref<Issue>[]
|
||||||
relatedIssue?: Ref<Issue>[]
|
relatedIssue?: Ref<Issue>[]
|
||||||
|
parents: IssueParentInfo[]
|
||||||
|
|
||||||
comments: number
|
comments: number
|
||||||
attachments?: number
|
attachments?: number
|
||||||
@ -124,6 +125,14 @@ export interface Issue extends AttachedDoc {
|
|||||||
rank: string
|
rank: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface IssueParentInfo {
|
||||||
|
parentId: Ref<Issue>
|
||||||
|
parentTitle: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -13,37 +13,27 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import core, { AttachedData, Tx, TxUpdateDoc } from '@anticrm/core'
|
import core, { DocumentUpdate, Ref, Tx, TxUpdateDoc } from '@anticrm/core'
|
||||||
import { extractTx, TriggerControl } from '@anticrm/server-core'
|
import { extractTx, TriggerControl } from '@anticrm/server-core'
|
||||||
import tracker, { Issue } from '@anticrm/tracker'
|
import tracker, { Issue } from '@anticrm/tracker'
|
||||||
|
|
||||||
async function updateSubIssues (
|
async function updateSubIssues (
|
||||||
|
updateTx: TxUpdateDoc<Issue>,
|
||||||
control: TriggerControl,
|
control: TriggerControl,
|
||||||
node: Issue,
|
update: DocumentUpdate<Issue> | ((node: Issue) => DocumentUpdate<Issue>)
|
||||||
update: Partial<AttachedData<Issue>>,
|
|
||||||
shouldSkip = false
|
|
||||||
): Promise<TxUpdateDoc<Issue>[]> {
|
): Promise<TxUpdateDoc<Issue>[]> {
|
||||||
let txes: TxUpdateDoc<Issue>[] = []
|
const subIssues = await control.findAll(tracker.class.Issue, { 'parents.parentId': updateTx.objectId })
|
||||||
|
|
||||||
if (!shouldSkip && Object.entries(update).some(([key, value]) => value !== node[key as keyof Issue])) {
|
return subIssues.map((issue) => {
|
||||||
txes.push(control.txFactory.createTxUpdateDoc(node._class, node.space, node._id, update))
|
const docUpdate = typeof update === 'function' ? update(issue) : update
|
||||||
}
|
return control.txFactory.createTxUpdateDoc(issue._class, issue.space, issue._id, docUpdate)
|
||||||
|
})
|
||||||
if (node.subIssues > 0) {
|
|
||||||
const subIssues = await control.findAll(tracker.class.Issue, { attachedTo: node._id })
|
|
||||||
|
|
||||||
for (const subIssue of subIssues) {
|
|
||||||
txes = txes.concat(await updateSubIssues(control, subIssue, update))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return txes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export async function OnIssueProjectUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
export async function OnIssueUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||||
const actualTx = extractTx(tx)
|
const actualTx = extractTx(tx)
|
||||||
if (actualTx._class !== core.class.TxUpdateDoc) {
|
if (actualTx._class !== core.class.TxUpdateDoc) {
|
||||||
return []
|
return []
|
||||||
@ -54,22 +44,56 @@ export async function OnIssueProjectUpdate (tx: Tx, control: TriggerControl): Pr
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(updateTx.operations, 'project')) {
|
const res: Tx[] = []
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const update: Partial<AttachedData<Issue>> = { project: updateTx.operations.project ?? null }
|
if (Object.prototype.hasOwnProperty.call(updateTx.operations, 'attachedTo')) {
|
||||||
const [node] = await control.findAll(
|
const [newParent] = await control.findAll(
|
||||||
updateTx.objectClass,
|
tracker.class.Issue,
|
||||||
{ _id: updateTx.objectId, subIssues: { $gt: 0 } },
|
{ _id: updateTx.operations.attachedTo as Ref<Issue> },
|
||||||
{ limit: 1 }
|
{ limit: 1 }
|
||||||
)
|
)
|
||||||
return node !== undefined ? await updateSubIssues(control, node, update, true) : []
|
const updatedParents =
|
||||||
|
newParent !== undefined ? [{ parentId: newParent._id, parentTitle: newParent.title }, ...newParent.parents] : []
|
||||||
|
|
||||||
|
function update (issue: Issue): DocumentUpdate<Issue> {
|
||||||
|
const parentInfoIndex = issue.parents.findIndex(({ parentId }) => parentId === updateTx.objectId)
|
||||||
|
return parentInfoIndex === -1
|
||||||
|
? {}
|
||||||
|
: { parents: [...issue.parents].slice(0, parentInfoIndex + 1).concat(updatedParents) }
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(
|
||||||
|
control.txFactory.createTxUpdateDoc(updateTx.objectClass, updateTx.objectSpace, updateTx.objectId, {
|
||||||
|
parents: updatedParents
|
||||||
|
}),
|
||||||
|
...(await updateSubIssues(updateTx, control, update))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(updateTx.operations, 'project')) {
|
||||||
|
res.push(...(await updateSubIssues(updateTx, control, { project: updateTx.operations.project })))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(updateTx.operations, 'title')) {
|
||||||
|
function update (issue: Issue): DocumentUpdate<Issue> {
|
||||||
|
const parentInfoIndex = issue.parents.findIndex(({ parentId }) => parentId === updateTx.objectId)
|
||||||
|
const updatedParentInfo = { ...issue.parents[parentInfoIndex], parentTitle: updateTx.operations.title as string }
|
||||||
|
const updatedParents = [...issue.parents]
|
||||||
|
|
||||||
|
updatedParents[parentInfoIndex] = updatedParentInfo
|
||||||
|
|
||||||
|
return { parents: updatedParents }
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(...(await updateSubIssues(updateTx, control, update)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
export default async () => ({
|
export default async () => ({
|
||||||
trigger: {
|
trigger: {
|
||||||
OnIssueProjectUpdate
|
OnIssueUpdate
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
26
server-plugins/tracker-resources/src/utils.ts
Normal file
26
server-plugins/tracker-resources/src/utils.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2022 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
export function arrayEquals<T> (first: Array<T>, second: Array<T>): boolean {
|
||||||
|
if (first === second) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first.length !== second.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return first.every((val, index) => val === second[index])
|
||||||
|
}
|
@ -27,6 +27,6 @@ export const serverTrackerId = 'server-tracker' as Plugin
|
|||||||
*/
|
*/
|
||||||
export default plugin(serverTrackerId, {
|
export default plugin(serverTrackerId, {
|
||||||
trigger: {
|
trigger: {
|
||||||
OnIssueProjectUpdate: '' as Resource<TriggerFunc>
|
OnIssueUpdate: '' as Resource<TriggerFunc>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user