mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-09 09:20:54 +00:00
parent
ccd2048ad0
commit
f1fad745bd
@ -1,10 +0,0 @@
|
||||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@anticrm/workspace",
|
||||
"comment": "",
|
||||
"type": "none"
|
||||
}
|
||||
],
|
||||
"packageName": "@anticrm/workspace"
|
||||
}
|
@ -394,6 +394,8 @@ export function createModel (builder: Builder): void {
|
||||
props: { kind: 'list', size: 'small', justify: 'center' }
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TitlePresenter, props: { shouldUseMargin: true, fixed: 'left' } },
|
||||
{ key: '', presenter: tracker.component.SubIssuesSelector, props: {} },
|
||||
{ key: '', presenter: tracker.component.GrowPresenter, props: {} },
|
||||
{ key: '', presenter: tracker.component.DueDatePresenter, props: { kind: 'list' } },
|
||||
{
|
||||
key: '',
|
||||
|
@ -38,7 +38,8 @@ export default mergeIds(trackerId, tracker, {
|
||||
component: {
|
||||
// Required to pass build without errorsF
|
||||
Nope: '' as AnyComponent,
|
||||
SprintSelector: '' as AnyComponent
|
||||
SprintSelector: '' as AnyComponent,
|
||||
SubIssuesSelector: '' as AnyComponent
|
||||
},
|
||||
app: {
|
||||
Tracker: '' as Ref<Application>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@anticrm/core",
|
||||
"version": "0.6.16",
|
||||
"version": "0.6.17",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
|
@ -0,0 +1,16 @@
|
||||
<span class="root" />
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
flex-shrink: 10;
|
||||
|
||||
&.with-margin {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -16,7 +16,7 @@
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import { Class, Doc, FindOptions, getObjectValue, Ref, WithLookup } from '@anticrm/core'
|
||||
import notification from '@anticrm/notification'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
|
||||
import {
|
||||
Button,
|
||||
@ -36,6 +36,7 @@
|
||||
import tracker from '../../plugin'
|
||||
import { IssuesGroupByKeys, issuesGroupEditorMap, IssuesOrderByKeys, issuesSortOrderMap } from '../../utils'
|
||||
import CreateIssue from '../CreateIssue.svelte'
|
||||
import GrowPresenter from './GrowPresenter.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let currentSpace: Ref<Team> | undefined = undefined
|
||||
@ -59,10 +60,19 @@
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus,
|
||||
space: tracker.class.Team
|
||||
space: tracker.class.Team,
|
||||
_id: {
|
||||
subIssues: tracker.class.Issue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const spaceQuery = createQuery()
|
||||
let currentTeam: Team | undefined
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
})
|
||||
|
||||
let personPresenter: AttributeModel
|
||||
|
||||
$: isCollapsedMap = Object.fromEntries(categories.map((category) => [category, false]))
|
||||
@ -175,6 +185,7 @@
|
||||
shouldShowPlaceholder={true}
|
||||
isInteractive={false}
|
||||
avatarSize={'x-small'}
|
||||
{currentSpace}
|
||||
/>
|
||||
{:else if headerComponent}
|
||||
<Component
|
||||
@ -186,7 +197,8 @@
|
||||
statuses: groupByKey === 'status' ? statuses : undefined,
|
||||
issues: groupedIssues[category],
|
||||
size: 'inline',
|
||||
kind: 'list'
|
||||
kind: 'list',
|
||||
currentSpace
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
@ -244,6 +256,8 @@
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
groupBy={groupByKey}
|
||||
{...attributeModel.props}
|
||||
{statuses}
|
||||
{currentTeam}
|
||||
/>
|
||||
</div>
|
||||
{:else if attributeModelIndex === 1}
|
||||
@ -259,10 +273,12 @@
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
groupBy={groupByKey}
|
||||
{...attributeModel.props}
|
||||
{statuses}
|
||||
{currentTeam}
|
||||
/>
|
||||
</FixedColumn>
|
||||
</div>
|
||||
{:else if attributeModelIndex === 3}
|
||||
{:else if attributeModelIndex === 3 || attributeModel.presenter === GrowPresenter}
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
@ -281,6 +297,8 @@
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
groupBy={groupByKey}
|
||||
{...attributeModel.props}
|
||||
{statuses}
|
||||
{currentTeam}
|
||||
/>
|
||||
</FixedColumn>
|
||||
{:else}
|
||||
@ -291,6 +309,8 @@
|
||||
issueId={docObject._id}
|
||||
groupBy={groupByKey}
|
||||
{...attributeModel.props}
|
||||
{statuses}
|
||||
{currentTeam}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@ -367,12 +387,7 @@
|
||||
|
||||
.priorityPresenter,
|
||||
.issuePresenter {
|
||||
min-width: 0;
|
||||
// min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
// .grow-cell {
|
||||
// flex-grow: 1;
|
||||
// flex-shrink: 0;
|
||||
// min-width: 0;
|
||||
// }
|
||||
</style>
|
||||
|
@ -252,7 +252,7 @@
|
||||
</div>
|
||||
<div class="buttons-group xsmall-gap states-bar">
|
||||
{#if issue && issueStatuses && issue.subIssues > 0}
|
||||
<SubIssuesSelector {issue} {currentTeam} {issueStatuses} />
|
||||
<SubIssuesSelector value={issue} {currentTeam} statuses={issueStatuses} />
|
||||
{/if}
|
||||
<PriorityEditor value={issue} isEditable={true} kind={'link-bordered'} size={'inline'} justify={'center'} />
|
||||
<ProjectEditor
|
||||
|
@ -58,7 +58,10 @@
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus,
|
||||
space: tracker.class.Team,
|
||||
sprint: tracker.class.Sprint
|
||||
sprint: tracker.class.Sprint,
|
||||
_id: {
|
||||
subIssues: tracker.class.Issue
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -30,8 +30,8 @@
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
flex-grow: 0;
|
||||
min-width: 7rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
flex-shrink: 10;
|
||||
|
@ -21,9 +21,9 @@
|
||||
import tracker from '../../../plugin'
|
||||
import { getIssueId } from '../../../issues'
|
||||
|
||||
export let issue: WithLookup<Issue>
|
||||
export let value: WithLookup<Issue>
|
||||
export let currentTeam: Team | undefined
|
||||
export let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
export let statuses: WithLookup<IssueStatus>[] | undefined
|
||||
|
||||
export let kind: ButtonKind = 'link-bordered'
|
||||
export let size: ButtonSize = 'inline'
|
||||
@ -36,18 +36,18 @@
|
||||
let doneStatus: Ref<Doc> | undefined
|
||||
let countComplate: number = 0
|
||||
|
||||
$: if (issue.$lookup?.subIssues !== undefined) {
|
||||
subIssues = issue.$lookup.subIssues as Issue[]
|
||||
subIssues.sort((a, b) => a.rank.localeCompare(b.rank))
|
||||
$: if (value.$lookup?.subIssues !== undefined) {
|
||||
subIssues = value.$lookup.subIssues as Issue[]
|
||||
subIssues.sort((a, b) => (a.rank ?? '').localeCompare(b.rank ?? ''))
|
||||
}
|
||||
$: if (issueStatuses && subIssues) {
|
||||
doneStatus = issueStatuses.find((s) => s.category === tracker.issueStatusCategory.Completed)?._id ?? undefined
|
||||
$: if (statuses && subIssues) {
|
||||
doneStatus = statuses.find((s) => s.category === tracker.issueStatusCategory.Completed)?._id ?? undefined
|
||||
if (doneStatus) countComplate = subIssues.filter((si) => si.status === doneStatus).length
|
||||
}
|
||||
$: hasSubIssues = (subIssues?.length ?? 0) > 0
|
||||
|
||||
function getIssueStatusIcon (issue: Issue) {
|
||||
const status = issueStatuses?.find((s) => issue.status === s._id)
|
||||
const status = statuses?.find((s) => issue.status === s._id)
|
||||
const category = status?.$lookup?.category
|
||||
const color = status?.color ?? category?.color
|
||||
|
||||
@ -58,8 +58,8 @@
|
||||
}
|
||||
|
||||
function openIssue (target: Ref<Issue>) {
|
||||
if (target !== issue._id) {
|
||||
showPanel(tracker.component.EditIssue, target, issue._class, 'content')
|
||||
if (target !== value._id) {
|
||||
showPanel(tracker.component.EditIssue, target, value._class, 'content')
|
||||
}
|
||||
}
|
||||
function showSubIssues () {
|
||||
@ -71,7 +71,7 @@
|
||||
value: subIssues.map((iss) => {
|
||||
const text = currentTeam ? `${getIssueId(currentTeam, iss)} ${iss.title}` : iss.title
|
||||
|
||||
return { id: iss._id, text, isSelected: iss._id === issue._id, ...getIssueStatusIcon(iss) }
|
||||
return { id: iss._id, text, isSelected: iss._id === value._id, ...getIssueStatusIcon(iss) }
|
||||
}),
|
||||
width: 'large'
|
||||
},
|
||||
|
@ -82,7 +82,7 @@
|
||||
function hourFloor (value: number): number {
|
||||
const days = Math.ceil(value)
|
||||
const hours = value - days
|
||||
return days + Math.floor(hours * 10) / 10
|
||||
return days + Math.floor(hours * 100) / 100
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -44,17 +44,19 @@
|
||||
style:stroke-dasharray={lenghtC}
|
||||
style:stroke-dashoffset={dashOffset === 0 ? 0 : dashOffset + 3}
|
||||
/>
|
||||
<circle
|
||||
cx={8}
|
||||
cy={8}
|
||||
r={7}
|
||||
class="progress-circle"
|
||||
style:stroke={primary ? 'var(--primary-bg-color)' : color}
|
||||
style:opacity={dashOffset === 0 ? 0 : 1}
|
||||
style:transform={'rotate(-82deg)'}
|
||||
style:stroke-dasharray={lenghtC}
|
||||
style:stroke-dashoffset={dashOffset === 0 ? lenghtC : lenghtC - dashOffset + 1}
|
||||
/>
|
||||
{#if min !== max && min !== value}
|
||||
<circle
|
||||
cx={8}
|
||||
cy={8}
|
||||
r={7}
|
||||
class="progress-circle"
|
||||
style:stroke={primary ? 'var(--primary-bg-color)' : color}
|
||||
style:opacity={dashOffset === 0 ? 0 : 1}
|
||||
style:transform={'rotate(-82deg)'}
|
||||
style:stroke-dasharray={lenghtC}
|
||||
style:stroke-dashoffset={dashOffset === 0 ? lenghtC : lenghtC - dashOffset + 1}
|
||||
/>
|
||||
{/if}
|
||||
</svg>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -78,7 +78,7 @@
|
||||
okLabel={value === undefined ? presentation.string.Create : presentation.string.Save}
|
||||
>
|
||||
<div class="flex-row-center gap-2">
|
||||
<EditBox bind:value={data.value} {placeholder} format={'number'} kind={'editbox'} {maxWidth} focus />
|
||||
<EditBox focus bind:value={data.value} {placeholder} format={'number'} kind={'editbox'} {maxWidth} />
|
||||
<UserBox
|
||||
_class={contact.class.Employee}
|
||||
label={contact.string.Employee}
|
||||
|
@ -4,15 +4,16 @@
|
||||
import { Project } from '@anticrm/tracker'
|
||||
import { Button, EditBox, Icon, showPopup } from '@anticrm/ui'
|
||||
import { DocAttributeBar } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { activeProject } from '../../issues'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesView from '../issues/IssuesView.svelte'
|
||||
import ProjectPopup from './ProjectPopup.svelte'
|
||||
import { activeProject } from '../../issues'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let project: Project
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function change (field: string, value: any) {
|
||||
await client.update(project, { [field]: value })
|
||||
@ -21,6 +22,7 @@
|
||||
showPopup(ProjectPopup, { _class: tracker.class.Project }, evt.target as HTMLElement, (value) => {
|
||||
if (value != null) {
|
||||
project = value
|
||||
dispatch('project', project._id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = '100%'
|
||||
export let onlyIcon: boolean = false
|
||||
export let groupBy: string | undefined
|
||||
export let groupBy: string | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Project } from '@anticrm/tracker'
|
||||
import { closePopup, closeTooltip, location } from '@anticrm/ui'
|
||||
import { closePopup, closeTooltip, getCurrentLocation, location, navigate } from '@anticrm/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { ProjectsViewMode } from '../../utils'
|
||||
@ -43,10 +43,8 @@
|
||||
|
||||
const projectQuery = createQuery()
|
||||
$: if (projectId !== undefined) {
|
||||
console.log('call query for', projectId)
|
||||
projectQuery.query(tracker.class.Project, { _id: projectId }, (result) => {
|
||||
project = result.shift()
|
||||
console.log('recieve result for', projectId, project)
|
||||
})
|
||||
} else {
|
||||
projectQuery.unsubscribe()
|
||||
@ -55,7 +53,14 @@
|
||||
</script>
|
||||
|
||||
{#if project}
|
||||
<EditProject {project} />
|
||||
<EditProject
|
||||
{project}
|
||||
on:project={(evt) => {
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[5] = evt.detail
|
||||
navigate(loc)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<ProjectBrowser {label} {query} {search} {mode} />
|
||||
{/if}
|
||||
|
@ -18,6 +18,7 @@
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Issue, Sprint } from '@anticrm/tracker'
|
||||
import { ButtonKind, ButtonShape, ButtonSize, isWeekend, Label, tooltip } from '@anticrm/ui'
|
||||
import DatePresenter from '@anticrm/ui/src/components/calendar/DatePresenter.svelte'
|
||||
import { activeSprint } from '../../issues'
|
||||
import tracker from '../../plugin'
|
||||
import EstimationProgressCircle from '../issues/timereport/EstimationProgressCircle.svelte'
|
||||
@ -58,13 +59,29 @@
|
||||
$: ids = new Set(issues?.map((it) => it._id) ?? [])
|
||||
|
||||
$: noParents = issues?.filter((it) => !ids.has(it.attachedTo as Ref<Issue>))
|
||||
$: totalEstimation = (noParents ?? [{ estimation: 0 }])
|
||||
.map((it) => it.estimation)
|
||||
$: totalEstimation = (noParents ?? [{ estimation: 0, childInfo: [] } as unknown as Issue])
|
||||
.map((it) => {
|
||||
if (it.childInfo?.length > 0) {
|
||||
const cEstimation = it.childInfo.map((ct) => ct.estimation).reduce((a, b) => a + b, 0)
|
||||
if (cEstimation !== 0) {
|
||||
return cEstimation
|
||||
}
|
||||
}
|
||||
return it.estimation
|
||||
})
|
||||
.reduce((it, cur) => {
|
||||
return it + cur
|
||||
})
|
||||
$: totalReported = (noParents ?? [{ reportedTime: 0 }])
|
||||
.map((it) => it.reportedTime)
|
||||
$: totalReported = (noParents ?? [{ reportedTime: 0, childInfo: [] } as unknown as Issue])
|
||||
.map((it) => {
|
||||
if (it.childInfo?.length > 0) {
|
||||
const cReported = it.childInfo.map((ct) => ct.reportedTime).reduce((a, b) => a + b, 0)
|
||||
if (cReported !== 0) {
|
||||
return cReported
|
||||
}
|
||||
}
|
||||
return it.reportedTime
|
||||
})
|
||||
.reduce((it, cur) => {
|
||||
return it + cur
|
||||
})
|
||||
@ -77,11 +94,12 @@
|
||||
})
|
||||
}
|
||||
function getDayOfSprint (startDate: number, now: number): number {
|
||||
const days = Math.floor(Math.abs((1 + now - startDate) / 1000 / 60 / 60 / 24)) + 1
|
||||
const days = Math.floor(Math.abs((1 + now - startDate) / 1000 / 60 / 60 / 24))
|
||||
const stDate = new Date(startDate)
|
||||
const stDateDate = stDate.getDate()
|
||||
const stTime = stDate.getTime()
|
||||
const ds = Array.from(Array(days).keys()).map((it) => stDateDate + it)
|
||||
return ds.filter((it) => !isWeekend(new Date(stDate.setDate(it)))).length
|
||||
return ds.filter((it) => !isWeekend(new Date(new Date(stTime).setDate(it)))).length
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -106,22 +124,29 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if sprint}
|
||||
{@const now = Date.now()}
|
||||
<div class="flex-row-center">
|
||||
<DatePresenter value={sprint.startDate} kind={'transparent'} />
|
||||
<span class="p-1"> / </span><DatePresenter value={sprint.targetDate} kind={'transparent'} />
|
||||
</div>
|
||||
<div class="flex-row-center ml-2">
|
||||
<!-- Active sprint in time -->
|
||||
<Label
|
||||
label={tracker.string.SprintPassed}
|
||||
params={{
|
||||
from:
|
||||
now < sprint.startDate
|
||||
? 0
|
||||
: now > sprint.targetDate
|
||||
? getDayOfSprint(sprint.startDate, sprint.targetDate)
|
||||
: getDayOfSprint(sprint.startDate, now),
|
||||
to: getDayOfSprint(sprint.startDate, sprint.targetDate)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if issues}
|
||||
{#if sprint}
|
||||
{@const now = Date.now()}
|
||||
{#if sprint.startDate < now && now < sprint.targetDate}
|
||||
<!-- Active sprint in time -->
|
||||
<div class="ml-2">
|
||||
<Label
|
||||
label={tracker.string.SprintPassed}
|
||||
params={{
|
||||
from: getDayOfSprint(sprint.startDate, now),
|
||||
to: getDayOfSprint(sprint.startDate, sprint.targetDate) - 1
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<!-- <Label label={tracker.string.SprintDay} value={}/> -->
|
||||
<div class="ml-4 flex-row-center">
|
||||
<div class="mr-2">
|
||||
|
@ -72,6 +72,8 @@ import SprintEditor from './components/sprints/SprintEditor.svelte'
|
||||
import ReportedTimeEditor from './components/issues/timereport/ReportedTimeEditor.svelte'
|
||||
import TimeSpendReport from './components/issues/timereport/TimeSpendReport.svelte'
|
||||
import EstimationEditor from './components/issues/timereport/EstimationEditor.svelte'
|
||||
import SubIssuesSelector from './components/issues/edit/SubIssuesSelector.svelte'
|
||||
import GrowPresenter from './components/issues/GrowPresenter.svelte'
|
||||
|
||||
export async function queryIssue<D extends Issue> (
|
||||
_class: Ref<Class<D>>,
|
||||
@ -177,7 +179,9 @@ export default async (): Promise<Resources> => ({
|
||||
SprintEditor,
|
||||
ReportedTimeEditor,
|
||||
TimeSpendReport,
|
||||
EstimationEditor
|
||||
EstimationEditor,
|
||||
SubIssuesSelector,
|
||||
GrowPresenter
|
||||
},
|
||||
completion: {
|
||||
IssueQuery: async (client: Client, query: string) => await queryIssue(tracker.class.Issue, client, query)
|
||||
|
@ -278,7 +278,8 @@ export default mergeIds(trackerId, tracker, {
|
||||
SprintTitlePresenter: '' as AnyComponent,
|
||||
ReportedTimeEditor: '' as AnyComponent,
|
||||
TimeSpendReport: '' as AnyComponent,
|
||||
EstimationEditor: '' as AnyComponent
|
||||
EstimationEditor: '' as AnyComponent,
|
||||
GrowPresenter: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
IssueTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>) => Promise<string>>
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
<div
|
||||
class="flex-no-shrink"
|
||||
style="{justify !== '' ? `text-align: ${justify}; ` : ''}min-width: var(--fixed-{key});"
|
||||
style="{justify !== '' ? `text-align: ${justify}; ` : ''} min-width: var(--fixed-{key});"
|
||||
use:resizeObserver={(element) => {
|
||||
cWidth = element.clientWidth
|
||||
}}
|
||||
|
@ -212,7 +212,9 @@
|
||||
on:click={() => changeSorting(attribute.sortingKey)}
|
||||
>
|
||||
<div class="antiTable-cells">
|
||||
<Label label={attribute.label} />
|
||||
{#if attribute.label}
|
||||
<Label label={attribute.label} />
|
||||
{/if}
|
||||
{#if attribute.sortingKey === sortKey}
|
||||
<div class="icon">
|
||||
{#if sortOrder === SortingOrder.Ascending}
|
||||
|
@ -16,11 +16,13 @@
|
||||
import core, {
|
||||
DocumentUpdate,
|
||||
Ref,
|
||||
Space,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxCUD,
|
||||
TxProcessor,
|
||||
TxRemoveDoc,
|
||||
TxUpdateDoc,
|
||||
WithLookup
|
||||
} from '@anticrm/core'
|
||||
@ -63,19 +65,8 @@ export async function OnIssueUpdate (tx: Tx, control: TriggerControl): Promise<T
|
||||
if (control.hierarchy.isDerived(createTx.objectClass, tracker.class.Issue)) {
|
||||
const issue = TxProcessor.createDoc2Doc(createTx)
|
||||
const res: Tx[] = []
|
||||
for (const pinfo of issue.parents) {
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(tracker.class.Issue, issue.space, pinfo.parentId, {
|
||||
$push: {
|
||||
childInfo: {
|
||||
childId: issue._id,
|
||||
estimation: issue.estimation,
|
||||
reportedTime: issue.reportedTime
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
await updateIssueParentEstimations(issue, res, control, [], issue.parents)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +76,29 @@ export async function OnIssueUpdate (tx: Tx, control: TriggerControl): Promise<T
|
||||
return await doIssueUpdate(updateTx, control)
|
||||
}
|
||||
}
|
||||
if (actualTx._class === core.class.TxRemoveDoc) {
|
||||
const removeTx = actualTx as TxRemoveDoc<Issue>
|
||||
if (control.hierarchy.isDerived(removeTx.objectClass, tracker.class.Issue)) {
|
||||
const parentIssue = await control.findAll(tracker.class.Issue, {
|
||||
'childInfo.childId': removeTx.objectId
|
||||
})
|
||||
const res: Tx[] = []
|
||||
const parents: IssueParentInfo[] = parentIssue.map((it) => ({ parentId: it._id, parentTitle: it.title }))
|
||||
await updateIssueParentEstimations(
|
||||
{
|
||||
_id: removeTx.objectId,
|
||||
estimation: 0,
|
||||
reportedTime: 0,
|
||||
space: removeTx.space
|
||||
},
|
||||
res,
|
||||
control,
|
||||
parents,
|
||||
[]
|
||||
)
|
||||
return res
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
@ -252,7 +266,12 @@ async function doIssueUpdate (updateTx: TxUpdateDoc<Issue>, control: TriggerCont
|
||||
return res
|
||||
}
|
||||
function updateIssueParentEstimations (
|
||||
issue: WithLookup<Issue>,
|
||||
issue: {
|
||||
_id: Ref<Issue>
|
||||
space: Ref<Space>
|
||||
estimation: number
|
||||
reportedTime: number
|
||||
},
|
||||
res: Tx[],
|
||||
control: TriggerControl,
|
||||
sourceParents: IssueParentInfo[],
|
||||
|
@ -70,9 +70,11 @@ function isLookupSort<T extends Doc> (sort: SortingQuery<T> | undefined): boolea
|
||||
|
||||
interface LookupStep {
|
||||
from: string
|
||||
localField: string
|
||||
foreignField: string
|
||||
localField?: string
|
||||
foreignField?: string
|
||||
as: string
|
||||
let?: any
|
||||
pipeline?: any
|
||||
}
|
||||
|
||||
abstract class MongoAdapterBase extends TxProcessor {
|
||||
@ -193,12 +195,23 @@ abstract class MongoAdapterBase extends TxProcessor {
|
||||
_class = value
|
||||
}
|
||||
const domain = this.hierarchy.getDomain(_class)
|
||||
const desc = this.hierarchy.getDescendants(_class)
|
||||
if (domain !== DOMAIN_MODEL) {
|
||||
const step = {
|
||||
const asVal = as.split('.').join('') + '_lookup'
|
||||
const step: LookupStep = {
|
||||
from: domain,
|
||||
localField: fullKey,
|
||||
foreignField: attr,
|
||||
as: as.split('.').join('') + '_lookup'
|
||||
// localField: fullKey,
|
||||
// foreignField: attr,
|
||||
let: { docId: '$' + fullKey },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
_class: { $in: desc },
|
||||
$expr: { $eq: ['$$docId', '$' + attr] }
|
||||
}
|
||||
}
|
||||
],
|
||||
as: asVal
|
||||
}
|
||||
result.push(step)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user