diff --git a/dev/generator/src/issues.ts b/dev/generator/src/issues.ts index a9457fe986..c6544cf303 100644 --- a/dev/generator/src/issues.ts +++ b/dev/generator/src/issues.ts @@ -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 = { 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 { estimation: object.estimation, reports: 0, relations: [], - childInfo: [] + childInfo: [], + workDayLength: object.workDayLength, + defaultTimeReportDay: object.defaultTimeReportDay } await client.addCollection( tracker.class.Issue, diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index f5b4f08306..beaf0b9954 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -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 + + 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, diff --git a/models/tracker/src/migration.ts b/models/tracker/src/migration.ts index 4b7223cd8e..d704be5935 100644 --- a/models/tracker/src/migration.ts +++ b/models/tracker/src/migration.ts @@ -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 { 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 { await Promise.all(teams.map((team) => fixTeamIssueStatusesOrder(tx, team))) } +async function upgradeTeamSettings (tx: TxOperations): Promise { + 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 { const teams = await tx.findAll(tracker.class.Team, { issueStatuses: undefined }) @@ -178,6 +203,25 @@ async function upgradeIssueStatuses (tx: TxOperations): Promise { } } +async function upgradeIssueTimeReportSettings (tx: TxOperations): Promise { + 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 { let { updated } = await client.update( DOMAIN_TRACKER, @@ -305,10 +349,12 @@ async function createDefaults (tx: TxOperations): Promise { async function upgradeTeams (tx: TxOperations): Promise { await upgradeTeamIssueStatuses(tx) await fixTeamsIssueStatusesOrder(tx) + await upgradeTeamSettings(tx) } async function upgradeIssues (tx: TxOperations): Promise { await upgradeIssueStatuses(tx) + await upgradeIssueTimeReportSettings(tx) const issues = await tx.findAll(tracker.class.Issue, { $or: [{ blockedBy: { $exists: true } }, { relatedIssue: { $exists: true } }] diff --git a/models/tracker/src/plugin.ts b/models/tracker/src/plugin.ts index 5331b7250a..ded36b4648 100644 --- a/models/tracker/src/plugin.ts +++ b/models/tracker/src/plugin.ts @@ -56,6 +56,7 @@ export default mergeIds(trackerId, tracker, { actionImpl: { CopyToClipboard: '' as ViewAction, EditWorkflowStatuses: '' as ViewAction, + EditTeam: '' as ViewAction, DeleteSprint: '' as ViewAction }, action: { diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts index 28966eedff..07663710a2 100644 --- a/packages/ui/src/types.ts +++ b/packages/ui/src/types.ts @@ -191,7 +191,7 @@ export interface DropdownTextItem { } export interface DropdownIntlItem { - id: string + id: string | number label: IntlString } diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index a37f04f0a7..c34b060a61 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -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": {} } diff --git a/plugins/tracker-assets/lang/ru.json b/plugins/tracker-assets/lang/ru.json index 60e765c75a..5c34fcf797 100644 --- a/plugins/tracker-assets/lang/ru.json +++ b/plugins/tracker-assets/lang/ru.json @@ -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": {} } diff --git a/plugins/tracker-resources/src/components/CreateIssue.svelte b/plugins/tracker-resources/src/components/CreateIssue.svelte index 3ddd5671f3..c863f5c5da 100644 --- a/plugins/tracker-resources/src/components/CreateIssue.svelte +++ b/plugins/tracker-resources/src/components/CreateIssue.svelte @@ -40,7 +40,9 @@ IssueTemplateChild, Project, Sprint, - Team + Team, + TimeReportDayType, + WorkDayLength } from '@hcengineering/tracker' import { ActionIcon, @@ -91,6 +93,7 @@ let issueStatuses: WithLookup[] | undefined let labels: TagReference[] = draft?.labels || [] let objectId: Ref = draft?.issueId || generateId() + let currentTeam: Team | undefined function toIssue (initials: AttachedData, draft: IssueDraft | null): AttachedData { if (draft == null) { @@ -100,7 +103,29 @@ return { ...initials, ...issue } } - let object: AttachedData = originalIssue + const defaultIssue = { + title: '', + description: '', + assignee, + project, + sprint, + number: 0, + rank: '', + status: '' as Ref, + 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, - 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, - 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( diff --git a/plugins/tracker-resources/src/components/issues/edit/CreateSubIssue.svelte b/plugins/tracker-resources/src/components/issues/edit/CreateSubIssue.svelte index 9be011b827..7dd3fd531e 100644 --- a/plugins/tracker-resources/src/components/issues/edit/CreateSubIssue.svelte +++ b/plugins/tracker-resources/src/components/issues/edit/CreateSubIssue.svelte @@ -65,7 +65,9 @@ estimation: 0, reportedTime: 0, reports: 0, - childInfo: [] + childInfo: [], + workDayLength: currentTeam.workDayLength, + defaultTimeReportDay: currentTeam.defaultTimeReportDay } } diff --git a/plugins/tracker-resources/src/components/issues/timereport/EstimationPopup.svelte b/plugins/tracker-resources/src/components/issues/timereport/EstimationPopup.svelte index b0b0c441a3..887d8c92f1 100644 --- a/plugins/tracker-resources/src/components/issues/timereport/EstimationPopup.svelte +++ b/plugins/tracker-resources/src/components/issues/timereport/EstimationPopup.svelte @@ -46,6 +46,7 @@ let currentTeam: Team | undefined let issueStatuses: WithLookup[] | 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) ) }} diff --git a/plugins/tracker-resources/src/components/issues/timereport/EstimationPresenter.svelte b/plugins/tracker-resources/src/components/issues/timereport/EstimationPresenter.svelte deleted file mode 100644 index 86e3b47646..0000000000 --- a/plugins/tracker-resources/src/components/issues/timereport/EstimationPresenter.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - -{value}d diff --git a/plugins/tracker-resources/src/components/issues/timereport/EstimationStatsPresenter.svelte b/plugins/tracker-resources/src/components/issues/timereport/EstimationStatsPresenter.svelte index 0727f52edb..0b65f444d7 100644 --- a/plugins/tracker-resources/src/components/issues/timereport/EstimationStatsPresenter.svelte +++ b/plugins/tracker-resources/src/components/issues/timereport/EstimationStatsPresenter.svelte @@ -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 + $: 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}
0}> -
- (
{:else if value.reportedTime === 0} -