mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-26 04:23:58 +03:00
TSK-425: Supported team settings (#2406)
Signed-off-by: Anton Brechka <anton.brechka@ezthera.com>
This commit is contained in:
parent
a843530bb2
commit
d2345cd931
@ -11,7 +11,14 @@ import core, {
|
||||
SortingOrder,
|
||||
WorkspaceId
|
||||
} from '@hcengineering/core'
|
||||
import tracker, { calcRank, Issue, IssuePriority, IssueStatus } from '../../../plugins/tracker/lib'
|
||||
import tracker, {
|
||||
calcRank,
|
||||
Issue,
|
||||
IssuePriority,
|
||||
IssueStatus,
|
||||
TimeReportDayType,
|
||||
WorkDayLength
|
||||
} from '../../../plugins/tracker/lib'
|
||||
|
||||
import { connect } from './connect'
|
||||
|
||||
@ -35,7 +42,9 @@ const object: AttachedData<Issue> = {
|
||||
reportedTime: 0,
|
||||
estimation: 0,
|
||||
reports: 0,
|
||||
childInfo: []
|
||||
childInfo: [],
|
||||
workDayLength: WorkDayLength.EIGHT_HOURS,
|
||||
defaultTimeReportDay: TimeReportDayType.PreviousWorkDay
|
||||
}
|
||||
|
||||
export interface IssueOptions {
|
||||
@ -97,7 +106,9 @@ async function genIssue (client: TxOperations): Promise<void> {
|
||||
estimation: object.estimation,
|
||||
reports: 0,
|
||||
relations: [],
|
||||
childInfo: []
|
||||
childInfo: [],
|
||||
workDayLength: object.workDayLength,
|
||||
defaultTimeReportDay: object.defaultTimeReportDay
|
||||
}
|
||||
await client.addCollection(
|
||||
tracker.class.Issue,
|
||||
|
@ -68,8 +68,10 @@ import {
|
||||
Sprint,
|
||||
SprintStatus,
|
||||
Team,
|
||||
TimeReportDayType,
|
||||
TimeSpendReport,
|
||||
trackerId
|
||||
trackerId,
|
||||
WorkDayLength
|
||||
} from '@hcengineering/tracker'
|
||||
import { KeyBinding } from '@hcengineering/view'
|
||||
import tracker from './plugin'
|
||||
@ -173,6 +175,9 @@ export class TTeam extends TSpace implements Team {
|
||||
|
||||
@Prop(TypeRef(tracker.class.IssueStatus), tracker.string.DefaultIssueStatus)
|
||||
defaultIssueStatus!: Ref<IssueStatus>
|
||||
|
||||
declare workDayLength: WorkDayLength
|
||||
declare defaultTimeReportDay: TimeReportDayType
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,6 +260,9 @@ export class TIssue extends TAttachedDoc implements Issue {
|
||||
reports!: number
|
||||
|
||||
declare childInfo: IssueChildInfo[]
|
||||
|
||||
declare workDayLength: WorkDayLength
|
||||
declare defaultTimeReportDay: TimeReportDayType
|
||||
}
|
||||
|
||||
/**
|
||||
@ -858,6 +866,26 @@ export function createModel (builder: Builder): void {
|
||||
tracker.action.EditWorkflowStatuses
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: tracker.actionImpl.EditTeam,
|
||||
label: tracker.string.EditTeam,
|
||||
icon: contact.icon.Edit,
|
||||
input: 'focus',
|
||||
category: tracker.category.Tracker,
|
||||
target: tracker.class.Team,
|
||||
query: {
|
||||
archived: false
|
||||
},
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
}
|
||||
},
|
||||
tracker.action.EditTeam
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ActionCategory,
|
||||
core.space.Model,
|
||||
|
@ -15,7 +15,15 @@
|
||||
|
||||
import core, { Doc, DocumentUpdate, generateId, Ref, SortingOrder, TxOperations, TxResult } from '@hcengineering/core'
|
||||
import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
|
||||
import { IssueStatus, IssueStatusCategory, Team, genRanks, Issue } from '@hcengineering/tracker'
|
||||
import {
|
||||
IssueStatus,
|
||||
IssueStatusCategory,
|
||||
Team,
|
||||
genRanks,
|
||||
Issue,
|
||||
TimeReportDayType,
|
||||
WorkDayLength
|
||||
} from '@hcengineering/tracker'
|
||||
import tags from '@hcengineering/tags'
|
||||
import { DOMAIN_TRACKER } from '.'
|
||||
import tracker from './plugin'
|
||||
@ -99,7 +107,9 @@ async function createDefaultTeam (tx: TxOperations): Promise<void> {
|
||||
identifier: 'TSK',
|
||||
sequence: 0,
|
||||
issueStatuses: 0,
|
||||
defaultIssueStatus: defaultStatusId
|
||||
defaultIssueStatus: defaultStatusId,
|
||||
defaultTimeReportDay: TimeReportDayType.PreviousWorkDay,
|
||||
workDayLength: WorkDayLength.EIGHT_HOURS
|
||||
},
|
||||
tracker.team.DefaultTeam
|
||||
)
|
||||
@ -127,6 +137,21 @@ async function fixTeamsIssueStatusesOrder (tx: TxOperations): Promise<void> {
|
||||
await Promise.all(teams.map((team) => fixTeamIssueStatusesOrder(tx, team)))
|
||||
}
|
||||
|
||||
async function upgradeTeamSettings (tx: TxOperations): Promise<void> {
|
||||
const teams = await tx.findAll(tracker.class.Team, {
|
||||
defaultTimeReportDay: { $exists: false },
|
||||
workDayLength: { $exists: false }
|
||||
})
|
||||
await Promise.all(
|
||||
teams.map((team) =>
|
||||
tx.update(team, {
|
||||
defaultTimeReportDay: TimeReportDayType.PreviousWorkDay,
|
||||
workDayLength: WorkDayLength.EIGHT_HOURS
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
async function upgradeTeamIssueStatuses (tx: TxOperations): Promise<void> {
|
||||
const teams = await tx.findAll(tracker.class.Team, { issueStatuses: undefined })
|
||||
|
||||
@ -178,6 +203,25 @@ async function upgradeIssueStatuses (tx: TxOperations): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function upgradeIssueTimeReportSettings (tx: TxOperations): Promise<void> {
|
||||
const issues = await tx.findAll(tracker.class.Issue, {
|
||||
defaultTimeReportDay: { $exists: false },
|
||||
workDayLength: { $exists: false }
|
||||
})
|
||||
|
||||
const teams = await tx.findAll(tracker.class.Team, {
|
||||
_id: { $in: Array.from(new Set(issues.map((issue) => issue.space))) }
|
||||
})
|
||||
const teamsById = new Map(teams.map((team) => [team._id, team]))
|
||||
|
||||
await Promise.all(
|
||||
issues.map((issue) => {
|
||||
const team = teamsById.get(issue.space)
|
||||
return tx.update(issue, { defaultTimeReportDay: team?.defaultTimeReportDay, workDayLength: team?.workDayLength })
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateParentIssues (client: MigrationClient): Promise<void> {
|
||||
let { updated } = await client.update(
|
||||
DOMAIN_TRACKER,
|
||||
@ -305,10 +349,12 @@ async function createDefaults (tx: TxOperations): Promise<void> {
|
||||
async function upgradeTeams (tx: TxOperations): Promise<void> {
|
||||
await upgradeTeamIssueStatuses(tx)
|
||||
await fixTeamsIssueStatusesOrder(tx)
|
||||
await upgradeTeamSettings(tx)
|
||||
}
|
||||
|
||||
async function upgradeIssues (tx: TxOperations): Promise<void> {
|
||||
await upgradeIssueStatuses(tx)
|
||||
await upgradeIssueTimeReportSettings(tx)
|
||||
|
||||
const issues = await tx.findAll(tracker.class.Issue, {
|
||||
$or: [{ blockedBy: { $exists: true } }, { relatedIssue: { $exists: true } }]
|
||||
|
@ -56,6 +56,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
actionImpl: {
|
||||
CopyToClipboard: '' as ViewAction,
|
||||
EditWorkflowStatuses: '' as ViewAction,
|
||||
EditTeam: '' as ViewAction,
|
||||
DeleteSprint: '' as ViewAction
|
||||
},
|
||||
action: {
|
||||
|
@ -191,7 +191,7 @@ export interface DropdownTextItem {
|
||||
}
|
||||
|
||||
export interface DropdownIntlItem {
|
||||
id: string
|
||||
id: string | number
|
||||
label: IntlString
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,7 @@
|
||||
|
||||
"EditIssue": "Edit {title}",
|
||||
"EditWorkflowStatuses": "Edit issue statuses",
|
||||
"EditTeam": "Edit team",
|
||||
"ManageWorkflowStatuses": "Manage issue statuses within team",
|
||||
"AddWorkflowStatus": "Add issue status",
|
||||
"EditWorkflowStatus": "Edit issue status",
|
||||
@ -230,6 +231,7 @@
|
||||
"TimeSpendReportValueTooltip": "Reported time in man days",
|
||||
"TimeSpendReportDescription": "Description",
|
||||
"TimeSpendValue": "{value}d",
|
||||
"TimeSpendHours": "{value}h",
|
||||
"SprintPassed": "{from}d/{to}d",
|
||||
"ChildEstimation": "Subissues Estimation",
|
||||
"ChildReportedTime": "Subissues Time",
|
||||
@ -249,9 +251,14 @@
|
||||
"TemplateReplace": "You you replace applied process?",
|
||||
"TemplateReplaceConfirm": "All changes to template values will be lost.",
|
||||
|
||||
"WorkDayCurrent": "Current Working Day",
|
||||
"WorkDayPrevious": "Previous Working Day",
|
||||
"WorkDayLabel": "Select Working Day Type"
|
||||
"CurrentWorkDay": "Current Working Day",
|
||||
"PreviousWorkDay": "Previous Working Day",
|
||||
"TimeReportDayTypeLabel": "Select time report day type",
|
||||
"DefaultTimeReportDay": "Select default day for time report",
|
||||
|
||||
"WorkDayLength": "Select length of working day",
|
||||
"SevenHoursLength": "Seven Hours",
|
||||
"EightHoursLength": "Eight Hours"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
@ -174,6 +174,7 @@
|
||||
|
||||
"EditIssue": "Редактирование {title}",
|
||||
"EditWorkflowStatuses": "Редактировать статусы задач",
|
||||
"EditTeam": "Редактировать команду",
|
||||
"ManageWorkflowStatuses": "Управлять статусами задач для команды",
|
||||
"AddWorkflowStatus": "Добавить статус задачи",
|
||||
"EditWorkflowStatus": "Редактировать статус задачи",
|
||||
@ -230,6 +231,7 @@
|
||||
"TimeSpendReportValueTooltip": "Затраченное время в человеко днях",
|
||||
"TimeSpendReportDescription": "Описание",
|
||||
"TimeSpendValue": "{value}d",
|
||||
"TimeSpendHours": "{value}h",
|
||||
"SprintPassed": "{from}d/{to}d",
|
||||
"ChildEstimation": "Оценка подзадач",
|
||||
"ChildReportedTime": "Время водзадач",
|
||||
@ -249,9 +251,14 @@
|
||||
"TemplateReplace": "Вы хотите заменить выбранный процесс?",
|
||||
"TemplateReplaceConfirm": "Все внесенные изменения в задачу будут потеряны",
|
||||
|
||||
"WorkDayCurrent": "Текущий Рабочий День",
|
||||
"WorkDayPrevious": "Предыдущий Рабочий День",
|
||||
"WorkDayLabel": "Выберите Тип Рабочего Дня"
|
||||
"CurrentWorkDay": "Текущий Рабочий День",
|
||||
"PreviousWorkDay": "Предыдущий Рабочий День",
|
||||
"TimeReportDayTypeLabel": "Выберите тип дня для временного отчета",
|
||||
"DefaultTimeReportDay": "Выберите дeнь для временного отчета по умолчанию",
|
||||
|
||||
"WorkDayLength": "Выберите длину рабочего дня",
|
||||
"SevenHoursLength": "Семь Часов",
|
||||
"EightHoursLength": "Восемь Часов"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
@ -40,7 +40,9 @@
|
||||
IssueTemplateChild,
|
||||
Project,
|
||||
Sprint,
|
||||
Team
|
||||
Team,
|
||||
TimeReportDayType,
|
||||
WorkDayLength
|
||||
} from '@hcengineering/tracker'
|
||||
import {
|
||||
ActionIcon,
|
||||
@ -91,6 +93,7 @@
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
let labels: TagReference[] = draft?.labels || []
|
||||
let objectId: Ref<Issue> = draft?.issueId || generateId()
|
||||
let currentTeam: Team | undefined
|
||||
|
||||
function toIssue (initials: AttachedData<Issue>, draft: IssueDraft | null): AttachedData<Issue> {
|
||||
if (draft == null) {
|
||||
@ -100,7 +103,29 @@
|
||||
return { ...initials, ...issue }
|
||||
}
|
||||
|
||||
let object: AttachedData<Issue> = originalIssue
|
||||
const defaultIssue = {
|
||||
title: '',
|
||||
description: '',
|
||||
assignee,
|
||||
project,
|
||||
sprint,
|
||||
number: 0,
|
||||
rank: '',
|
||||
status: '' as Ref<IssueStatus>,
|
||||
priority,
|
||||
dueDate: null,
|
||||
comments: 0,
|
||||
subIssues: 0,
|
||||
parents: [],
|
||||
reportedTime: 0,
|
||||
estimation: 0,
|
||||
reports: 0,
|
||||
childInfo: [],
|
||||
workDayLength: currentTeam?.workDayLength ?? WorkDayLength.EIGHT_HOURS,
|
||||
defaultTimeReportDay: currentTeam?.defaultTimeReportDay ?? TimeReportDayType.PreviousWorkDay
|
||||
}
|
||||
|
||||
let object = originalIssue
|
||||
? {
|
||||
...originalIssue,
|
||||
title: `${originalIssue.title} (copy)`,
|
||||
@ -110,51 +135,19 @@
|
||||
reports: 0,
|
||||
childInfo: []
|
||||
}
|
||||
: toIssue(
|
||||
{
|
||||
title: '',
|
||||
description: '',
|
||||
assignee,
|
||||
project,
|
||||
sprint,
|
||||
number: 0,
|
||||
rank: '',
|
||||
status: '' as Ref<IssueStatus>,
|
||||
priority,
|
||||
dueDate: null,
|
||||
comments: 0,
|
||||
subIssues: 0,
|
||||
parents: [],
|
||||
reportedTime: 0,
|
||||
estimation: 0,
|
||||
reports: 0,
|
||||
childInfo: []
|
||||
},
|
||||
draft
|
||||
)
|
||||
: toIssue(defaultIssue, draft)
|
||||
|
||||
$: {
|
||||
defaultIssue.workDayLength = currentTeam?.workDayLength ?? WorkDayLength.EIGHT_HOURS
|
||||
defaultIssue.defaultTimeReportDay = currentTeam?.defaultTimeReportDay ?? TimeReportDayType.PreviousWorkDay
|
||||
object.workDayLength = defaultIssue.workDayLength
|
||||
object.defaultTimeReportDay = defaultIssue.defaultTimeReportDay
|
||||
}
|
||||
|
||||
function resetObject (): void {
|
||||
templateId = undefined
|
||||
template = undefined
|
||||
object = {
|
||||
title: '',
|
||||
description: '',
|
||||
assignee,
|
||||
project,
|
||||
sprint,
|
||||
number: 0,
|
||||
rank: '',
|
||||
status: '' as Ref<IssueStatus>,
|
||||
priority,
|
||||
dueDate: null,
|
||||
comments: 0,
|
||||
subIssues: 0,
|
||||
parents: [],
|
||||
reportedTime: 0,
|
||||
estimation: 0,
|
||||
reports: 0,
|
||||
childInfo: []
|
||||
}
|
||||
object = { ...defaultIssue }
|
||||
subIssues = []
|
||||
}
|
||||
|
||||
@ -221,6 +214,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const statusesQuery = createQuery()
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
|
||||
@ -244,6 +238,9 @@
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: _space }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
})
|
||||
|
||||
async function setPropsFromOriginalIssue () {
|
||||
if (!originalIssue) {
|
||||
@ -418,7 +415,9 @@
|
||||
estimation: object.estimation,
|
||||
reports: 0,
|
||||
relations: relatedTo !== undefined ? [{ _id: relatedTo._id, _class: relatedTo._class }] : [],
|
||||
childInfo: []
|
||||
childInfo: [],
|
||||
workDayLength: object.workDayLength,
|
||||
defaultTimeReportDay: object.defaultTimeReportDay
|
||||
}
|
||||
|
||||
await client.addCollection(
|
||||
@ -490,7 +489,9 @@
|
||||
estimation: subIssue.estimation,
|
||||
reports: 0,
|
||||
relations: [],
|
||||
childInfo: []
|
||||
childInfo: [],
|
||||
workDayLength: object.workDayLength,
|
||||
defaultTimeReportDay: object.defaultTimeReportDay
|
||||
}
|
||||
|
||||
await client.addCollection(
|
||||
|
@ -65,7 +65,9 @@
|
||||
estimation: 0,
|
||||
reportedTime: 0,
|
||||
reports: 0,
|
||||
childInfo: []
|
||||
childInfo: [],
|
||||
workDayLength: currentTeam.workDayLength,
|
||||
defaultTimeReportDay: currentTeam.defaultTimeReportDay
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@
|
||||
let currentTeam: Team | undefined
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
|
||||
$: defaultTimeReportDay = object.defaultTimeReportDay
|
||||
$: query.query(
|
||||
object._class,
|
||||
{ _id: object._id },
|
||||
@ -140,7 +141,13 @@
|
||||
on:click={(event) => {
|
||||
showPopup(
|
||||
TimeSpendReportPopup,
|
||||
{ issueId: object._id, issueClass: object._class, space: object.space, assignee: object.assignee },
|
||||
{
|
||||
issueId: object._id,
|
||||
issueClass: object._class,
|
||||
space: object.space,
|
||||
assignee: object.assignee,
|
||||
defaultTimeReportDay
|
||||
},
|
||||
eventToHTMLElement(event)
|
||||
)
|
||||
}}
|
||||
|
@ -1,20 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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">
|
||||
export let value: number
|
||||
</script>
|
||||
|
||||
<span class="lines-limit-2 select-text">{value}d</span>
|
@ -16,12 +16,13 @@
|
||||
import { AttachedData } from '@hcengineering/core'
|
||||
|
||||
import { Issue } from '@hcengineering/tracker'
|
||||
import { floorFractionDigits, Label } from '@hcengineering/ui'
|
||||
import tracker from '../../../plugin'
|
||||
import { floorFractionDigits } from '@hcengineering/ui'
|
||||
import EstimationProgressCircle from './EstimationProgressCircle.svelte'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
|
||||
export let value: Issue | AttachedData<Issue>
|
||||
|
||||
$: workDayLength = value.workDayLength
|
||||
$: childReportTime = floorFractionDigits(
|
||||
value.reportedTime + (value.childInfo ?? []).map((it) => it.reportedTime).reduce((a, b) => a + b, 0),
|
||||
3
|
||||
@ -43,21 +44,18 @@
|
||||
{@const reportDiff = floorFractionDigits(rchildReportTime - value.reportedTime, 3)}
|
||||
{#if reportDiff !== 0 && value.reportedTime !== 0}
|
||||
<div class="flex flex-nowrap mr-1" class:showError={reportDiff > 0}>
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: rchildReportTime }} />
|
||||
<TimePresenter value={rchildReportTime} {workDayLength} />
|
||||
</div>
|
||||
<div class="romColor">
|
||||
(<Label
|
||||
label={tracker.string.TimeSpendValue}
|
||||
params={{ value: floorFractionDigits(value.reportedTime, 3) }}
|
||||
/>)
|
||||
(<TimePresenter value={value.reportedTime} {workDayLength} />)
|
||||
</div>
|
||||
{:else if value.reportedTime === 0}
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: childReportTime }} />
|
||||
<TimePresenter value={childReportTime} {workDayLength} />
|
||||
{:else}
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: floorFractionDigits(value.reportedTime, 3) }} />
|
||||
<TimePresenter value={value.reportedTime} {workDayLength} />
|
||||
{/if}
|
||||
{:else}
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: floorFractionDigits(value.reportedTime, 3) }} />
|
||||
<TimePresenter value={value.reportedTime} {workDayLength} />
|
||||
{/if}
|
||||
<div class="p-1">/</div>
|
||||
{/if}
|
||||
@ -66,21 +64,18 @@
|
||||
{@const estimationDiff = childEstTime - Math.round(value.estimation)}
|
||||
{#if estimationDiff !== 0}
|
||||
<div class="flex flex-nowrap mr-1" class:showWarning={estimationDiff !== 0}>
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: childEstTime }} />
|
||||
<TimePresenter value={childEstTime} {workDayLength} />
|
||||
</div>
|
||||
{#if value.estimation !== 0}
|
||||
<div class="romColor">
|
||||
(<Label
|
||||
label={tracker.string.TimeSpendValue}
|
||||
params={{ value: floorFractionDigits(value.estimation, 3) }}
|
||||
/>)
|
||||
(<TimePresenter value={value.estimation} {workDayLength} />)
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: floorFractionDigits(value.estimation, 3) }} />
|
||||
<TimePresenter value={value.estimation} {workDayLength} />
|
||||
{/if}
|
||||
{:else}
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: floorFractionDigits(value.estimation, 3) }} />
|
||||
<TimePresenter value={value.estimation} {workDayLength} />
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -19,6 +19,7 @@
|
||||
import { ActionIcon, eventToHTMLElement, floorFractionDigits, IconAdd, Label, showPopup } from '@hcengineering/ui'
|
||||
import ReportsPopup from './ReportsPopup.svelte'
|
||||
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
|
||||
// export let label: IntlString
|
||||
export let placeholder: IntlString
|
||||
@ -26,10 +27,19 @@
|
||||
export let value: number
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
|
||||
$: defaultTimeReportDay = object.defaultTimeReportDay
|
||||
$: workDayLength = object.workDayLength
|
||||
|
||||
function addTimeReport (event: MouseEvent): void {
|
||||
showPopup(
|
||||
TimeSpendReportPopup,
|
||||
{ issueId: object._id, issueClass: object._class, space: object.space, assignee: object.assignee },
|
||||
{
|
||||
issueId: object._id,
|
||||
defaultTimeReportDay,
|
||||
issueClass: object._class,
|
||||
space: object.space,
|
||||
assignee: object.assignee
|
||||
},
|
||||
eventToHTMLElement(event)
|
||||
)
|
||||
}
|
||||
@ -46,9 +56,9 @@
|
||||
<div id="ReportedTimeEditor" class="link-container flex-between" on:click={showReports}>
|
||||
{#if value !== undefined}
|
||||
<span class="overflow-label">
|
||||
{floorFractionDigits(value, 3)}
|
||||
<TimePresenter {value} {workDayLength} />
|
||||
{#if childTime !== 0}
|
||||
/ {childTime}
|
||||
/ <TimePresenter value={childTime} {workDayLength} />
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
@ -60,9 +70,9 @@
|
||||
</div>
|
||||
{:else if value !== undefined}
|
||||
<span class="overflow-label">
|
||||
{floorFractionDigits(value, 3)}
|
||||
<TimePresenter {value} {workDayLength} />
|
||||
{#if childTime !== 0}
|
||||
/ {childTime}
|
||||
/ <TimePresenter value={childTime} {workDayLength} />
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
|
@ -25,6 +25,8 @@
|
||||
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
||||
export let issue: Issue
|
||||
|
||||
$: defaultTimeReportDay = issue.defaultTimeReportDay
|
||||
|
||||
export function canClose (): boolean {
|
||||
return true
|
||||
}
|
||||
@ -37,7 +39,13 @@
|
||||
function addReport (event: MouseEvent): void {
|
||||
showPopup(
|
||||
TimeSpendReportPopup,
|
||||
{ issueId: issue._id, issueClass: issue._class, space: issue.space, assignee: issue.assignee },
|
||||
{
|
||||
issueId: issue._id,
|
||||
issueClass: issue._class,
|
||||
space: issue.space,
|
||||
assignee: issue.assignee,
|
||||
defaultTimeReportDay
|
||||
},
|
||||
eventToHTMLElement(event)
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
<!--
|
||||
// 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 { WorkDayLength } from '@hcengineering/tracker'
|
||||
import { floorFractionDigits, Label, tooltip } from '@hcengineering/ui'
|
||||
import tracker from '../../../plugin'
|
||||
|
||||
export let id: string | undefined = undefined
|
||||
export let kind: 'link' | undefined = undefined
|
||||
export let workDayLength: WorkDayLength = WorkDayLength.EIGHT_HOURS
|
||||
export let value: number
|
||||
</script>
|
||||
|
||||
<span
|
||||
{id}
|
||||
class:link={kind === 'link'}
|
||||
on:click
|
||||
use:tooltip={{
|
||||
component: Label,
|
||||
props: { label: tracker.string.TimeSpendHours, params: { value: floorFractionDigits(value * workDayLength, 2) } }
|
||||
}}
|
||||
>
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: floorFractionDigits(value, 3) }} />
|
||||
</span>
|
||||
|
||||
<style lang="scss">
|
||||
.link {
|
||||
white-space: nowrap;
|
||||
|
||||
font-size: 0.8125rem;
|
||||
color: var(--content-color);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:active {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -13,34 +13,32 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { TimeReportDayType } from '@hcengineering/tracker'
|
||||
import { DropdownIntlItem, DropdownLabelsIntl } from '@hcengineering/ui'
|
||||
import tracker from '../../../plugin'
|
||||
import { WorkDaysType } from '../../../types'
|
||||
import { getWorkDate, getWorkDayType } from '../../../utils'
|
||||
import WorkDaysIcon from './WorkDaysIcon.svelte'
|
||||
import TimeReportDayIcon from './TimeReportDayIcon.svelte'
|
||||
|
||||
export let dateTimestamp: number
|
||||
export let label = tracker.string.TimeReportDayTypeLabel
|
||||
export let selected: TimeReportDayType | undefined
|
||||
|
||||
const workDaysDropdownItems: DropdownIntlItem[] = [
|
||||
{
|
||||
id: WorkDaysType.CURRENT,
|
||||
label: tracker.string.WorkDayCurrent
|
||||
id: TimeReportDayType.CurrentWorkDay,
|
||||
label: tracker.string.CurrentWorkDay
|
||||
},
|
||||
{
|
||||
id: WorkDaysType.PREVIOUS,
|
||||
label: tracker.string.WorkDayPrevious
|
||||
id: TimeReportDayType.PreviousWorkDay,
|
||||
label: tracker.string.PreviousWorkDay
|
||||
}
|
||||
]
|
||||
|
||||
$: selectedWorkDayType = dateTimestamp ? getWorkDayType(dateTimestamp) : undefined
|
||||
</script>
|
||||
|
||||
<DropdownLabelsIntl
|
||||
kind="link-bordered"
|
||||
icon={WorkDaysIcon}
|
||||
icon={TimeReportDayIcon}
|
||||
shouldUpdateUndefined={false}
|
||||
label={tracker.string.WorkDayLabel}
|
||||
{label}
|
||||
items={workDaysDropdownItems}
|
||||
bind:selected={selectedWorkDayType}
|
||||
on:selected={({ detail }) => (dateTimestamp = getWorkDate(detail))}
|
||||
bind:selected
|
||||
on:selected
|
||||
/>
|
@ -13,75 +13,40 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@hcengineering/contact'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import type { TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, floorFractionDigits, Label, showPopup, tooltip } from '@hcengineering/ui'
|
||||
import view, { AttributeModel } from '@hcengineering/view'
|
||||
import { getObjectPresenter } from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
import { Issue, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
||||
|
||||
export let value: WithLookup<TimeSpendReport>
|
||||
const client = getClient()
|
||||
let presenter: AttributeModel
|
||||
|
||||
getObjectPresenter(client, contact.class.Employee, { key: '' }).then((p) => {
|
||||
presenter = p
|
||||
})
|
||||
$: issue = value.$lookup?.attachedTo
|
||||
$: if (!issue) {
|
||||
client.findOne(value.attachedToClass, { _id: value.attachedTo }).then((r) => {
|
||||
issue = r as Issue
|
||||
})
|
||||
}
|
||||
$: workDayLength = issue?.workDayLength
|
||||
$: defaultTimeReportDay = issue?.defaultTimeReportDay
|
||||
|
||||
function editSpendReport (event: MouseEvent): void {
|
||||
showPopup(
|
||||
TimeSpendReportPopup,
|
||||
{ issue: value.attachedTo, issueClass: value.attachedToClass, value, assignee: value.employee },
|
||||
{
|
||||
issue: value.attachedTo,
|
||||
issueClass: value.attachedToClass,
|
||||
value,
|
||||
assignee: value.employee,
|
||||
defaultTimeReportDay
|
||||
},
|
||||
eventToHTMLElement(event)
|
||||
)
|
||||
}
|
||||
|
||||
let employee: Employee | undefined | null = value.$lookup?.employee ?? null
|
||||
$: if (employee === undefined) {
|
||||
client.findOne(value.attachedToClass, { _id: value.attachedTo }).then((r) => {
|
||||
employee = r as Employee
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value && value.value}
|
||||
<span
|
||||
id="TimeSpendReportValue"
|
||||
class="issuePresenterRoot flex-row-center"
|
||||
on:click={editSpendReport}
|
||||
use:tooltip={value.employee
|
||||
? {
|
||||
label: tracker.string.TimeSpendReport,
|
||||
component: view.component.ObjectPresenter,
|
||||
props: {
|
||||
objectId: value.employee,
|
||||
_class: contact.class.Employee,
|
||||
value: value.$lookup?.employee
|
||||
}
|
||||
}
|
||||
: undefined}
|
||||
>
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: floorFractionDigits(value.value, 3) }} />
|
||||
</span>
|
||||
<TimePresenter id="TimeSpendReportValue" kind="link" value={value.value} {workDayLength} on:click={editSpendReport} />
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.issuePresenterRoot {
|
||||
white-space: nowrap;
|
||||
|
||||
font-size: 0.8125rem;
|
||||
color: var(--content-color);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:active {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -17,11 +17,11 @@
|
||||
import { AttachedData, Class, DocumentUpdate, Ref, Space } from '@hcengineering/core'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import presentation, { Card, getClient, UserBox } from '@hcengineering/presentation'
|
||||
import { Issue, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { Issue, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { DatePresenter, EditBox } from '@hcengineering/ui'
|
||||
import tracker from '../../../plugin'
|
||||
import { getWorkDate, WorkDaysType } from '../../../utils'
|
||||
import WorkDaysDropdown from './WorkDaysDropdown.svelte'
|
||||
import { getTimeReportDate, getTimeReportDayType } from '../../../utils'
|
||||
import TimeReportDayDropdown from './TimeReportDayDropdown.svelte'
|
||||
|
||||
export let issueId: Ref<Issue>
|
||||
export let issueClass: Ref<Class<Issue>>
|
||||
@ -30,17 +30,21 @@
|
||||
|
||||
export let value: TimeSpendReport | undefined
|
||||
export let placeholder: IntlString = tracker.string.TimeSpendReportValue
|
||||
export let defaultTimeReportDay = TimeReportDayType.PreviousWorkDay
|
||||
|
||||
const data = {
|
||||
date: value?.date ?? getTimeReportDate(defaultTimeReportDay),
|
||||
description: value?.description ?? '',
|
||||
value: value?.value,
|
||||
employee: value?.employee ?? assignee ?? null
|
||||
}
|
||||
|
||||
let selectedTimeReportDay = getTimeReportDayType(data.date)
|
||||
|
||||
export function canClose (): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
const data = {
|
||||
date: value?.date ?? getWorkDate(WorkDaysType.PREVIOUS),
|
||||
description: value?.description ?? '',
|
||||
value: value?.value,
|
||||
employee: value?.employee ?? assignee ?? null
|
||||
}
|
||||
async function create (): Promise<void> {
|
||||
if (value === undefined) {
|
||||
getClient().addCollection(
|
||||
@ -88,8 +92,16 @@
|
||||
bind:value={data.employee}
|
||||
showNavigate={false}
|
||||
/>
|
||||
<WorkDaysDropdown bind:dateTimestamp={data.date} />
|
||||
<DatePresenter kind={'link'} bind:value={data.date} editable />
|
||||
<TimeReportDayDropdown
|
||||
bind:selected={selectedTimeReportDay}
|
||||
on:selected={({ detail }) => (data.date = getTimeReportDate(detail))}
|
||||
/>
|
||||
<DatePresenter
|
||||
kind={'link'}
|
||||
bind:value={data.date}
|
||||
editable
|
||||
on:change={({ detail }) => (selectedTimeReportDay = getTimeReportDayType(detail))}
|
||||
/>
|
||||
</div>
|
||||
<EditBox bind:value={data.description} placeholder={tracker.string.TimeSpendReportDescription} kind={'editbox'} />
|
||||
</Card>
|
||||
|
@ -18,6 +18,7 @@
|
||||
import { Issue, Team, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { floorFractionDigits, Label, Scroller, Spinner } from '@hcengineering/ui'
|
||||
import tracker from '../../../plugin'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
import TimeSpendReportsList from './TimeSpendReportsList.svelte'
|
||||
|
||||
export let issue: Issue
|
||||
@ -28,6 +29,7 @@
|
||||
|
||||
let reports: TimeSpendReport[] | undefined
|
||||
|
||||
$: workDayLength = issue.workDayLength
|
||||
$: subIssuesQuery.query(tracker.class.TimeSpendReport, query, async (result) => (reports = result), {
|
||||
sort: { modifiedOn: SortingOrder.Descending },
|
||||
lookup: {
|
||||
@ -40,8 +42,10 @@
|
||||
</script>
|
||||
|
||||
{#if reports}
|
||||
<Label label={tracker.string.ReportedTime} />: {reportedTime}
|
||||
<Label label={tracker.string.TimeSpendReports} />: {total}
|
||||
<span class="overflow-label flex-nowrap">
|
||||
<Label label={tracker.string.ReportedTime} />: <TimePresenter value={reportedTime} {workDayLength} />
|
||||
<Label label={tracker.string.TimeSpendReports} />: <TimePresenter value={total} {workDayLength} />
|
||||
</span>
|
||||
<div class="h-50">
|
||||
<Scroller>
|
||||
<TimeSpendReportsList {reports} {teams} />
|
||||
|
@ -16,19 +16,13 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Doc, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import UserBox from '@hcengineering/presentation/src/components/UserBox.svelte'
|
||||
import { Team, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import {
|
||||
eventToHTMLElement,
|
||||
floorFractionDigits,
|
||||
getEventPositionElement,
|
||||
ListView,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { Team, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import { eventToHTMLElement, getEventPositionElement, ListView, showPopup } from '@hcengineering/ui'
|
||||
import DatePresenter from '@hcengineering/ui/src/components/calendar/DatePresenter.svelte'
|
||||
import { ContextMenu, FixedColumn, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
|
||||
import { getIssueId } from '../../../issues'
|
||||
import tracker from '../../../plugin'
|
||||
import EstimationPresenter from './EstimationPresenter.svelte'
|
||||
import TimePresenter from './TimePresenter.svelte'
|
||||
import TimeSpendReportPopup from './TimeSpendReportPopup.svelte'
|
||||
|
||||
export let reports: WithLookup<TimeSpendReport>[]
|
||||
@ -52,10 +46,20 @@
|
||||
}
|
||||
const toTeamId = (ref: Ref<Space>) => ref as Ref<Team>
|
||||
|
||||
function editSpendReport (event: MouseEvent, value: TimeSpendReport): void {
|
||||
function editSpendReport (
|
||||
event: MouseEvent,
|
||||
value: TimeSpendReport,
|
||||
defaultTimeReportDay: TimeReportDayType | undefined
|
||||
): void {
|
||||
showPopup(
|
||||
TimeSpendReportPopup,
|
||||
{ issue: value.attachedTo, issueClass: value.attachedToClass, value, assignee: value.employee },
|
||||
{
|
||||
issue: value.attachedTo,
|
||||
issueClass: value.attachedToClass,
|
||||
value,
|
||||
assignee: value.employee,
|
||||
defaultTimeReportDay
|
||||
},
|
||||
eventToHTMLElement(event)
|
||||
)
|
||||
}
|
||||
@ -75,7 +79,7 @@
|
||||
on:focus={() => {
|
||||
listProvider.updateFocus(report)
|
||||
}}
|
||||
on:click={(evt) => editSpendReport(evt, report)}
|
||||
on:click={(evt) => editSpendReport(evt, report, currentTeam?.defaultTimeReportDay)}
|
||||
>
|
||||
<div class="flex-row-center clear-mins gap-2 p-2">
|
||||
<span class="issuePresenter">
|
||||
@ -104,7 +108,7 @@
|
||||
readonly
|
||||
showNavigate={false}
|
||||
/>
|
||||
<EstimationPresenter value={floorFractionDigits(report.value, 3)} />
|
||||
<TimePresenter value={report.value} workDayLength={currentTeam?.workDayLength} />
|
||||
<DatePresenter value={report.date} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, IssueTemplate, Sprint } from '@hcengineering/tracker'
|
||||
import { Issue, IssueStatus, IssueTemplate, Sprint, Team } from '@hcengineering/tracker'
|
||||
import { ButtonKind, ButtonSize, ButtonShape, floorFractionDigits } from '@hcengineering/ui'
|
||||
import { Label, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import DatePresenter from '@hcengineering/ui/src/components/calendar/DatePresenter.svelte'
|
||||
@ -24,6 +24,7 @@
|
||||
import tracker from '../../plugin'
|
||||
import { getDayOfSprint } from '../../utils'
|
||||
import EstimationProgressCircle from '../issues/timereport/EstimationProgressCircle.svelte'
|
||||
import TimePresenter from '../issues/timereport/TimePresenter.svelte'
|
||||
import SprintSelector from './SprintSelector.svelte'
|
||||
|
||||
export let value: Issue | IssueTemplate
|
||||
@ -42,6 +43,13 @@
|
||||
export let enlargedText: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
const spaceQuery = createQuery()
|
||||
|
||||
let currentTeam: Team | undefined
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: value.space }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
})
|
||||
$: workDayLength = currentTeam?.workDayLength
|
||||
|
||||
const handleSprintIdChanged = async (newSprintId: Ref<Sprint> | null | undefined) => {
|
||||
if (!isEditable || newSprintId === undefined || value.sprint === newSprintId) {
|
||||
@ -157,23 +165,21 @@
|
||||
<div class="flex-row-center" class:minus-margin-space={kind === 'list-header'} class:text-sm={twoRows}>
|
||||
{#if sprint}
|
||||
{@const now = Date.now()}
|
||||
{@const sprintDaysFrom =
|
||||
now < sprint.startDate
|
||||
? 0
|
||||
: now > sprint.targetDate
|
||||
? getDayOfSprint(sprint.startDate, sprint.targetDate)
|
||||
: getDayOfSprint(sprint.startDate, now)}
|
||||
{@const sprintDaysTo = getDayOfSprint(sprint.startDate, sprint.targetDate)}
|
||||
<DatePresenter value={sprint.startDate} kind={'transparent'} />
|
||||
<span class="p-1"> / </span>
|
||||
<DatePresenter value={sprint.targetDate} kind={'transparent'} />
|
||||
<div class="w-2 min-w-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)
|
||||
}}
|
||||
/>
|
||||
<TimePresenter value={sprintDaysFrom} {workDayLength} />
|
||||
/
|
||||
<TimePresenter value={sprintDaysTo} {workDayLength} />
|
||||
{/if}
|
||||
{#if issues}
|
||||
<!-- <Label label={tracker.string.SprintDay} value={}/> -->
|
||||
@ -186,10 +192,10 @@
|
||||
<EstimationProgressCircle value={totalReported} max={totalEstimation} />
|
||||
<div class="w-2 min-w-2" />
|
||||
{#if totalReported > 0}
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: totalReported }} />
|
||||
<TimePresenter value={totalReported} {workDayLength} />
|
||||
/
|
||||
{/if}
|
||||
<Label label={tracker.string.TimeSpendValue} params={{ value: totalEstimation }} />
|
||||
<TimePresenter value={totalEstimation} {workDayLength} />
|
||||
{#if sprint?.capacity}
|
||||
<Label label={tracker.string.CapacityValue} params={{ value: sprint?.capacity }} />
|
||||
{/if}
|
||||
|
@ -14,25 +14,56 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Button, EditBox, eventToHTMLElement, Label, showPopup, ToggleWithLabel } from '@hcengineering/ui'
|
||||
import { getClient, SpaceCreateCard } from '@hcengineering/presentation'
|
||||
import {
|
||||
Button,
|
||||
DropdownIntlItem,
|
||||
DropdownLabelsIntl,
|
||||
EditBox,
|
||||
eventToHTMLElement,
|
||||
Label,
|
||||
showPopup,
|
||||
ToggleWithLabel
|
||||
} from '@hcengineering/ui'
|
||||
import presentation, { Card, getClient } from '@hcengineering/presentation'
|
||||
import core, { getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { IssueStatus } from '@hcengineering/tracker'
|
||||
import { IssueStatus, Team, TimeReportDayType, WorkDayLength } from '@hcengineering/tracker'
|
||||
import { StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
import tracker from '../../plugin'
|
||||
import TeamIconChooser from './TeamIconChooser.svelte'
|
||||
import TimeReportDayDropdown from '../issues/timereport/TimeReportDayDropdown.svelte'
|
||||
|
||||
let name: string = ''
|
||||
let description: string = ''
|
||||
let isPrivate: boolean = false
|
||||
let icon: Asset | undefined = undefined
|
||||
export let team: Team | undefined = undefined
|
||||
|
||||
let name: string = team?.name ?? ''
|
||||
let description: string = team?.description ?? ''
|
||||
let isPrivate: boolean = team?.private ?? false
|
||||
let icon: Asset | undefined = team?.icon ?? undefined
|
||||
let selectedWorkDayType: TimeReportDayType | undefined =
|
||||
team?.defaultTimeReportDay ?? TimeReportDayType.PreviousWorkDay
|
||||
let selectedWorkDayLength: WorkDayLength | undefined = team?.workDayLength ?? WorkDayLength.EIGHT_HOURS
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const workDayLengthItems: DropdownIntlItem[] = [
|
||||
{
|
||||
id: WorkDayLength.SEVEN_HOURS,
|
||||
label: tracker.string.SevenHoursLength
|
||||
},
|
||||
{
|
||||
id: WorkDayLength.EIGHT_HOURS,
|
||||
label: tracker.string.EightHoursLength
|
||||
}
|
||||
]
|
||||
|
||||
async function createTeam () {
|
||||
await client.createDoc(tracker.class.Team, core.space.Space, {
|
||||
$: isNew = !team
|
||||
|
||||
async function handleSave () {
|
||||
isNew ? createTeam() : updateTeam()
|
||||
}
|
||||
|
||||
function getTeamData () {
|
||||
return {
|
||||
name,
|
||||
description,
|
||||
private: isPrivate,
|
||||
@ -42,8 +73,31 @@
|
||||
sequence: 0,
|
||||
issueStatuses: 0,
|
||||
defaultIssueStatus: '' as Ref<IssueStatus>,
|
||||
icon
|
||||
})
|
||||
icon,
|
||||
defaultTimeReportDay: selectedWorkDayType ?? TimeReportDayType.PreviousWorkDay,
|
||||
workDayLength: selectedWorkDayLength ?? WorkDayLength.EIGHT_HOURS
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTeam () {
|
||||
const teamData = getTeamData()
|
||||
// update team doc
|
||||
await client.update(team!, teamData)
|
||||
|
||||
// update issues related to team
|
||||
const issuesByTeam = await client.findAll(tracker.class.Issue, { space: team!._id })
|
||||
await Promise.all(
|
||||
issuesByTeam.map((issue) =>
|
||||
client.update(issue, {
|
||||
defaultTimeReportDay: teamData.defaultTimeReportDay,
|
||||
workDayLength: teamData.workDayLength
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
async function createTeam () {
|
||||
await client.createDoc(tracker.class.Team, core.space.Space, getTeamData())
|
||||
}
|
||||
|
||||
function chooseIcon (ev: MouseEvent) {
|
||||
@ -55,10 +109,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<SpaceCreateCard
|
||||
label={tracker.string.NewTeam}
|
||||
okAction={createTeam}
|
||||
canSave={name.length > 0}
|
||||
<Card
|
||||
label={isNew ? tracker.string.NewTeam : tracker.string.EditTeam}
|
||||
okLabel={isNew ? presentation.string.Create : presentation.string.Edit}
|
||||
okAction={handleSave}
|
||||
canSave={name.length > 0 && !!selectedWorkDayType && !!selectedWorkDayLength}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
@ -81,4 +136,24 @@
|
||||
</div>
|
||||
<Button icon={icon ?? tracker.icon.Home} kind="no-border" size="medium" on:click={chooseIcon} />
|
||||
</div>
|
||||
</SpaceCreateCard>
|
||||
|
||||
<div class="flex-between">
|
||||
<div class="caption">
|
||||
<Label label={tracker.string.DefaultTimeReportDay} />
|
||||
</div>
|
||||
<TimeReportDayDropdown bind:selected={selectedWorkDayType} label={tracker.string.DefaultTimeReportDay} />
|
||||
</div>
|
||||
|
||||
<div class="flex-between">
|
||||
<div class="caption">
|
||||
<Label label={tracker.string.WorkDayLength} />
|
||||
</div>
|
||||
<DropdownLabelsIntl
|
||||
kind="link-bordered"
|
||||
label={tracker.string.WorkDayLength}
|
||||
items={workDayLengthItems}
|
||||
shouldUpdateUndefined={false}
|
||||
bind:selected={selectedWorkDayLength}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -164,6 +164,12 @@ async function editWorkflowStatuses (team: Team | undefined): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function editTeam (team: Team | undefined): Promise<void> {
|
||||
if (team !== undefined) {
|
||||
showPopup(CreateTeam, { team })
|
||||
}
|
||||
}
|
||||
|
||||
async function moveAndDeleteSprint (client: TxOperations, oldSprint: Sprint, newSprint?: Sprint): Promise<void> {
|
||||
const noSprintLabel = await translate(tracker.string.NoSprint, {})
|
||||
|
||||
@ -282,6 +288,7 @@ export default async (): Promise<Resources> => ({
|
||||
},
|
||||
actionImpl: {
|
||||
EditWorkflowStatuses: editWorkflowStatuses,
|
||||
EditTeam: editTeam,
|
||||
DeleteSprint: deleteSprint
|
||||
},
|
||||
resolver: {
|
||||
|
@ -90,6 +90,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
DefaultIssueStatus: '' as IntlString,
|
||||
IssueStatuses: '' as IntlString,
|
||||
EditWorkflowStatuses: '' as IntlString,
|
||||
EditTeam: '' as IntlString,
|
||||
ManageWorkflowStatuses: '' as IntlString,
|
||||
AddWorkflowStatus: '' as IntlString,
|
||||
EditWorkflowStatus: '' as IntlString,
|
||||
@ -248,6 +249,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
TimeSpendReportValue: '' as IntlString,
|
||||
TimeSpendReportDescription: '' as IntlString,
|
||||
TimeSpendValue: '' as IntlString,
|
||||
TimeSpendHours: '' as IntlString,
|
||||
SprintPassed: '' as IntlString,
|
||||
|
||||
ChildEstimation: '' as IntlString,
|
||||
@ -265,9 +267,14 @@ export default mergeIds(trackerId, tracker, {
|
||||
TemplateReplace: '' as IntlString,
|
||||
TemplateReplaceConfirm: '' as IntlString,
|
||||
|
||||
WorkDayCurrent: '' as IntlString,
|
||||
WorkDayPrevious: '' as IntlString,
|
||||
WorkDayLabel: '' as IntlString
|
||||
CurrentWorkDay: '' as IntlString,
|
||||
PreviousWorkDay: '' as IntlString,
|
||||
TimeReportDayTypeLabel: '' as IntlString,
|
||||
DefaultTimeReportDay: '' as IntlString,
|
||||
|
||||
WorkDayLength: '' as IntlString,
|
||||
SevenHoursLength: '' as IntlString,
|
||||
EightHoursLength: '' as IntlString
|
||||
},
|
||||
component: {
|
||||
NopeComponent: '' as AnyComponent,
|
||||
|
@ -104,8 +104,3 @@ export const issuesGroupBySorting: Record<IssuesGrouping, SortingQuery<Issue>> =
|
||||
[IssuesGrouping.Sprint]: { '$lookup.sprint.label': SortingOrder.Ascending },
|
||||
[IssuesGrouping.NoGrouping]: {}
|
||||
}
|
||||
|
||||
export enum WorkDaysType {
|
||||
CURRENT = 'current',
|
||||
PREVIOUS = 'previous'
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ import {
|
||||
ProjectStatus,
|
||||
Sprint,
|
||||
SprintStatus,
|
||||
Team
|
||||
Team,
|
||||
TimeReportDayType
|
||||
} from '@hcengineering/tracker'
|
||||
import { ViewOptionModel } from '@hcengineering/view-resources'
|
||||
import {
|
||||
@ -39,13 +40,7 @@ import {
|
||||
MILLISECONDS_IN_WEEK
|
||||
} from '@hcengineering/ui'
|
||||
import tracker from './plugin'
|
||||
import {
|
||||
defaultPriorities,
|
||||
defaultProjectStatuses,
|
||||
defaultSprintStatuses,
|
||||
issuePriorities,
|
||||
WorkDaysType
|
||||
} from './types'
|
||||
import { defaultPriorities, defaultProjectStatuses, defaultSprintStatuses, issuePriorities } from './types'
|
||||
|
||||
export * from './types'
|
||||
|
||||
@ -649,13 +644,14 @@ export async function moveIssuesToAnotherSprint (
|
||||
}
|
||||
}
|
||||
|
||||
export function getWorkDate (type: WorkDaysType): number {
|
||||
export function getTimeReportDate (type: TimeReportDayType): number {
|
||||
const date = new Date(Date.now())
|
||||
if (type === WorkDaysType.PREVIOUS) {
|
||||
|
||||
if (type === TimeReportDayType.PreviousWorkDay) {
|
||||
date.setDate(date.getDate() - 1)
|
||||
}
|
||||
|
||||
// if currentDate is day off then set date to last working day
|
||||
// if date is day off then set date to last working day
|
||||
while (isWeekend(date)) {
|
||||
date.setDate(date.getDate() - 1)
|
||||
}
|
||||
@ -663,14 +659,14 @@ export function getWorkDate (type: WorkDaysType): number {
|
||||
return date.valueOf()
|
||||
}
|
||||
|
||||
export function getWorkDayType (timestamp: number): WorkDaysType | undefined {
|
||||
export function getTimeReportDayType (timestamp: number): TimeReportDayType | undefined {
|
||||
const date = new Date(timestamp)
|
||||
const currentWorkDate = new Date(getWorkDate(WorkDaysType.CURRENT))
|
||||
const previousWorkDate = new Date(getWorkDate(WorkDaysType.PREVIOUS))
|
||||
const currentWorkDate = new Date(getTimeReportDate(TimeReportDayType.CurrentWorkDay))
|
||||
const previousWorkDate = new Date(getTimeReportDate(TimeReportDayType.PreviousWorkDay))
|
||||
|
||||
if (areDatesEqual(date, currentWorkDate)) {
|
||||
return WorkDaysType.CURRENT
|
||||
return TimeReportDayType.CurrentWorkDay
|
||||
} else if (areDatesEqual(date, previousWorkDate)) {
|
||||
return WorkDaysType.PREVIOUS
|
||||
return TimeReportDayType.PreviousWorkDay
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,24 @@ export interface Team extends Space {
|
||||
issueStatuses: number
|
||||
defaultIssueStatus: Ref<IssueStatus>
|
||||
icon?: Asset
|
||||
workDayLength: WorkDayLength
|
||||
defaultTimeReportDay: TimeReportDayType
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum TimeReportDayType {
|
||||
CurrentWorkDay = 'CurrentWorkDay',
|
||||
PreviousWorkDay = 'PreviousWorkDay'
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum WorkDayLength {
|
||||
SEVEN_HOURS = 7,
|
||||
EIGHT_HOURS = 8
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,6 +193,9 @@ export interface Issue extends AttachedDoc {
|
||||
|
||||
childInfo: IssueChildInfo[]
|
||||
|
||||
workDayLength: WorkDayLength
|
||||
defaultTimeReportDay: TimeReportDayType
|
||||
|
||||
template?: {
|
||||
// A template issue is based on
|
||||
template: Ref<IssueTemplate>
|
||||
@ -465,6 +486,7 @@ export default plugin(trackerId, {
|
||||
Relations: '' as Ref<Action>,
|
||||
NewSubIssue: '' as Ref<Action>,
|
||||
EditWorkflowStatuses: '' as Ref<Action>,
|
||||
EditTeam: '' as Ref<Action>,
|
||||
SetSprint: '' as Ref<Action>
|
||||
},
|
||||
team: {
|
||||
|
Loading…
Reference in New Issue
Block a user