Fix estimation issues (#2256)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2022-08-23 16:39:21 +07:00
committed by GitHub
parent f1fad745bd
commit 43e6d19403
13 changed files with 112 additions and 23 deletions
@@ -75,7 +75,19 @@
let personPresenter: AttributeModel
$: isCollapsedMap = Object.fromEntries(categories.map((category) => [category, false]))
const isCollapsedMap: Record<any, boolean> = {}
$: {
const exkeys = new Set(Object.keys(isCollapsedMap))
for (const c of categories) {
if (!exkeys.delete(c)) {
isCollapsedMap[c] = false
}
}
for (const k of exkeys) {
delete isCollapsedMap[k]
}
}
$: combinedGroupedIssues = Object.values(groupedIssues).flat(1)
$: options = { ...baseOptions, sort: { [orderBy]: issuesSortOrderMap[orderBy] } } as FindOptions<Issue>
$: headerComponent = groupByKey === undefined || groupByKey === 'assignee' ? null : issuesGroupEditorMap[groupByKey]
@@ -75,6 +75,7 @@
<Label label={tracker.string.ChildEstimation} />:
<Scroller tableFade>
<TableBrowser
showFilterBar={false}
_class={tracker.class.Issue}
query={{ _id: { $in: childIds } }}
config={['', { key: '$lookup.attachedTo', presenter: ParentNamesPresenter }, 'estimation']}
@@ -86,6 +87,7 @@
<TableBrowser
_class={tracker.class.TimeSpendReport}
query={{ attachedTo: { $in: [object._id, ...childIds] } }}
showFilterBar={false}
config={[
'$lookup.attachedTo',
{ key: '$lookup.attachedTo', presenter: ParentNamesPresenter },
@@ -54,6 +54,7 @@
</svelte:fragment>
<Scroller tableFade>
<TableBrowser
showFilterBar={false}
_class={tracker.class.TimeSpendReport}
query={{ attachedTo: { $in: [issue._id, ...issue.childInfo.map((it) => it.childId)] } }}
config={[
@@ -2,11 +2,12 @@
import { getClient } from '@anticrm/presentation'
import { StyledTextBox } from '@anticrm/text-editor'
import { Sprint } from '@anticrm/tracker'
import { Button, EditBox, Icon, showPopup } from '@anticrm/ui'
import { Button, DatePresenter, EditBox, Icon, Label, showPopup } from '@anticrm/ui'
import { DocAttributeBar } from '@anticrm/view-resources'
import { onDestroy } from 'svelte'
import { activeSprint } from '../../issues'
import tracker from '../../plugin'
import { getDayOfSprint } from '../../utils'
import Expanded from '../icons/Expanded.svelte'
import IssuesView from '../issues/IssuesView.svelte'
import SprintPopup from './SprintPopup.svelte'
@@ -46,6 +47,33 @@
</Button>
</div>
</svelte:fragment>
<svelte:fragment slot="afterHeader">
{@const now = Date.now()}
<div class="p-1 ml-6 flex-row-center">
<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)
}}
/>
{#if sprint?.capacity}
<Label label={tracker.string.CapacityValue} params={{ value: sprint?.capacity }} />
{/if}
</div>
</div>
</svelte:fragment>
<svelte:fragment slot="aside">
<div class="flex-grow p-4 w-60 left-divider">
<div class="fs-title text-xl">
@@ -64,3 +92,9 @@
</div>
</svelte:fragment>
</IssuesView>
<style lang="scss">
.showWarning {
color: var(--warning-color) !important;
}
</style>
@@ -17,10 +17,11 @@
import { IntlString } from '@anticrm/platform'
import { createQuery, getClient } from '@anticrm/presentation'
import { Issue, Sprint } from '@anticrm/tracker'
import { ButtonKind, ButtonShape, ButtonSize, isWeekend, Label, tooltip } from '@anticrm/ui'
import { ButtonKind, ButtonShape, ButtonSize, Label, tooltip } from '@anticrm/ui'
import DatePresenter from '@anticrm/ui/src/components/calendar/DatePresenter.svelte'
import { activeSprint } from '../../issues'
import tracker from '../../plugin'
import { getDayOfSprint } from '../../utils'
import EstimationProgressCircle from '../issues/timereport/EstimationProgressCircle.svelte'
import SprintSelector from './SprintSelector.svelte'
@@ -93,14 +94,6 @@
sprint = res.shift()
})
}
function getDayOfSprint (startDate: number, now: number): number {
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(new Date(stTime).setDate(it)))).length
}
</script>
{#if (value.sprint && value.sprint !== $activeSprint && groupBy !== 'sprint') || shouldShowPlaceholder}
@@ -148,7 +141,7 @@
{/if}
{#if issues}
<!-- <Label label={tracker.string.SprintDay} value={}/> -->
<div class="ml-4 flex-row-center">
<div class="ml-4 flex-row-center" class:showWarning={totalEstimation > (sprint?.capacity ?? 0)}>
<div class="mr-2">
<EstimationProgressCircle value={totalReported} max={totalEstimation} />
</div>
@@ -157,5 +150,14 @@
/
{/if}
<Label label={tracker.string.TimeSpendValue} params={{ value: totalEstimation }} />
{#if sprint?.capacity}
<Label label={tracker.string.CapacityValue} params={{ value: sprint?.capacity }} />
{/if}
</div>
{/if}
<style lang="scss">
.showWarning {
color: var(--warning-color) !important;
}
</style>
@@ -15,9 +15,10 @@
<script lang="ts">
import type { Class, Doc, DocumentQuery, Ref } from '@anticrm/core'
import { ObjectCreate, ObjectPopup } from '@anticrm/presentation'
import { Sprint } from '@anticrm/tracker'
import { Sprint, SprintStatus } from '@anticrm/tracker'
import { Icon, Label } from '@anticrm/ui'
import { sprintStatusAssets } from '../../utils'
import SprintTitlePresenter from './SprintTitlePresenter.svelte'
export let _class: Ref<Class<Sprint>>
export let selected: Ref<Sprint> | undefined
export let sprintQuery: DocumentQuery<Sprint> = {}
@@ -31,6 +32,7 @@
update: (doc: Doc) => (doc as Sprint).label
}
: undefined
const getStatus = (sprint: Sprint): SprintStatus => sprint.status
</script>
<ObjectPopup
@@ -44,8 +46,21 @@
create={_create}
on:update
on:close
groupBy={'status'}
>
<svelte:fragment slot="item" let:item={sprint}>
<SprintTitlePresenter value={sprint} />
</svelte:fragment>
<svelte:fragment slot="category" let:item={sprint}>
{@const status = sprintStatusAssets[getStatus(sprint)]}
{#if status}
<div class="flex-row-center p-1">
<Icon icon={status.icon} size={'small'} />
<div class="ml-2">
<Label label={status.label} />
</div>
</div>
{/if}
</svelte:fragment>
</ObjectPopup>
+3 -1
View File
@@ -227,7 +227,9 @@ export default mergeIds(trackerId, tracker, {
SprintPassed: '' as IntlString,
ChildEstimation: '' as IntlString,
ChildReportedTime: '' as IntlString
ChildReportedTime: '' as IntlString,
Capacity: '' as IntlString,
CapacityValue: '' as IntlString
},
component: {
NopeComponent: '' as AnyComponent,
+12 -3
View File
@@ -29,7 +29,7 @@ import {
Team
} from '@anticrm/tracker'
import { ViewOptionModel } from '@anticrm/view-resources'
import { AnyComponent, AnySvelteComponent, getMillisecondsInMonth, MILLISECONDS_IN_WEEK } from '@anticrm/ui'
import { AnyComponent, AnySvelteComponent, getMillisecondsInMonth, isWeekend, MILLISECONDS_IN_WEEK } from '@anticrm/ui'
import tracker from './plugin'
import { defaultPriorities, defaultProjectStatuses, defaultSprintStatuses, issuePriorities } from './types'
@@ -555,8 +555,17 @@ export function getSprintDays (value: Sprint): string {
const st = new Date(value.startDate).getDate()
const days = Math.floor(Math.abs((1 + value.targetDate - value.startDate) / 1000 / 60 / 60 / 24)) + 1
const stDate = new Date(value.startDate)
const stTime = stDate.getTime()
let ds = Array.from(Array(days).keys()).map((it) => st + it)
ds = ds.filter((it) => ![0, 6].includes(new Date(stDate.setDate(it)).getDay()))
ds = ds.filter((it) => ![0, 6].includes(new Date(new Date(stTime).setDate(it)).getDay()))
return ds.join(' ')
}
export function getDayOfSprint (startDate: number, now: number): number {
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(new Date(stTime).setDate(it)))).length
}