UBER-1083: Use hours and minutes to present less than a day durations ()

Signed-off-by: Petr Vyazovetskiy <develop.pit@gmail.com>
This commit is contained in:
Pete Anøther 2023-12-01 04:27:56 -03:00 committed by GitHub
parent 32e39a3723
commit e361325a73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 122 additions and 22 deletions
packages
presentation/src
ui/src
plugins
tracker-assets/lang
tracker-resources/src
components/issues/timereport
plugin.ts
tests

View File

@ -11,7 +11,7 @@ export interface KeyedAttribute {
export { updateAttribute } from '@hcengineering/core'
export function getAttribute (client: Client, object: any, key: KeyedAttribute): any {
// Check if attr is mixin and return it's value
// Check if attr is mixin and return its value
if (client.getHierarchy().isMixin(key.attr.attributeOf)) {
return (client.getHierarchy().as(object, key.attr.attributeOf) as any)[key.key]
} else {

View File

@ -73,6 +73,7 @@ export function checkAdaptiveMatching (size: WidthType | null, limit: WidthType)
return size !== null ? range.has(size) : false
}
// TODO: Fix naming, since it doesn't floor (floorFractionDigits(2.5) === 3.0)
export function floorFractionDigits (n: number | string, amount: number): number {
return Number(Number(n).toFixed(amount))
}

View File

@ -237,7 +237,9 @@
"TimeSpendReportValueTooltip": "Spent time in hours",
"TimeSpendReportDescription": "Description",
"TimeSpendValue": "{value}d",
"TimeSpendDays": "{value}d",
"TimeSpendHours": "{value}h",
"TimeSpendMinutes": "{value}m",
"ChildEstimation": "Subissues Estimation",
"ChildReportedTime": "Subissues Time",
"CapacityValue": "of {value}d",

View File

@ -237,7 +237,9 @@
"TimeSpendReportValueTooltip": "Затраченное время в человеко днях",
"TimeSpendReportDescription": "Описание",
"TimeSpendValue": "{value}d",
"TimeSpendDays": "{value}d",
"TimeSpendHours": "{value}h",
"TimeSpendMinutes": "{value}m",
"ChildEstimation": "Оценка подзадач",
"ChildReportedTime": "Время подзадач",
"CapacityValue": "из {value}d",

View File

@ -13,13 +13,46 @@
// limitations under the License.
-->
<script lang="ts">
import { floorFractionDigits, Label, tooltip } from '@hcengineering/ui'
import { Label, tooltip, themeStore } from '@hcengineering/ui'
import tracker from '../../../plugin'
import { translate } from '@hcengineering/platform'
export let id: string | undefined = undefined
export let kind: 'link' | undefined = undefined
export let value: number
export let noSymbol: boolean = false
// TODO: Make configurable?
const hoursInWorkingDay = 8
let label = ''
$: days = Math.floor(value / hoursInWorkingDay)
$: hours = Math.floor(value % hoursInWorkingDay)
$: minutes = Math.floor((value % 1) * 60)
$: Promise.all([
days > 0 ? translate(tracker.string.TimeSpendDays, { value: days }, $themeStore.language) : Promise.resolve(false),
hours > 0
? translate(tracker.string.TimeSpendHours, { value: hours }, $themeStore.language)
: Promise.resolve(false),
minutes > 0
? translate(tracker.string.TimeSpendMinutes, { value: minutes }, $themeStore.language)
: Promise.resolve(false)
])
.then(([days, hours, minutes]) =>
[
...(days === false ? [] : [days]),
...(hours === false ? [] : [hours]),
...(minutes === false ? [] : [minutes])
].join(' ')
)
.then((l) => (l === '' ? translate(tracker.string.TimeSpendHours, { value: 0 }, $themeStore.language) : l))
.then((l) => {
label = l
})
.catch((err) => {
console.error(err)
})
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
@ -30,16 +63,13 @@
on:click
use:tooltip={{
component: Label,
props: { label: tracker.string.TimeSpendHours, params: { value: floorFractionDigits(value, 2) } }
props: {
label: tracker.string.TimeSpendValue,
params: { value }
}
}}
>
{#if noSymbol}
{floorFractionDigits(value, 2)}
{:else if value > 0 && value < 8}
<Label label={tracker.string.TimeSpendHours} params={{ value: floorFractionDigits(value, 2) }} />
{:else}
<Label label={tracker.string.TimeSpendValue} params={{ value: floorFractionDigits(value / 8, 3) }} />
{/if}
{label}
</span>
<style lang="scss">

View File

@ -269,7 +269,9 @@ export default mergeIds(trackerId, tracker, {
TimeSpendReportValue: '' as IntlString,
TimeSpendReportDescription: '' as IntlString,
TimeSpendValue: '' as IntlString,
TimeSpendDays: '' as IntlString,
TimeSpendHours: '' as IntlString,
TimeSpendMinutes: '' as IntlString,
ChildEstimation: '' as IntlString,
ChildReportedTime: '' as IntlString,

View File

@ -4,7 +4,7 @@
./tool-local.sh create-account user1 -f John -l Appleseed -p 1234
./tool-local.sh confirm-email user1
# Create second user record in accounts
./tool-local.sh create-account user2 -f Kainin -l Numoin -p 1234
./tool-local.sh create-account user2 -f Kainin -l Dirak -p 1234
./tool-local.sh confirm-email user2
@ -18,4 +18,4 @@
./tool-local.sh assign-workspace user2 sanity-ws
./tool-local.sh configure sanity-ws --enable=*
./tool-local.sh configure sanity-ws --list
./tool-local.sh configure sanity-ws --list

View File

@ -44,6 +44,7 @@ export class TemplateDetailsPage extends CommonTrackerPage {
if (data.labels != null) {
await this.buttonAddLabel.click()
await expect(this.page.locator('div.menu-group span', { hasText: data.labels })).toBeVisible()
await this.inputTitle.click({ force: true })
}
if (data.component != null) {
await expect(this.buttonComponent).toHaveText(data.component)

View File

@ -83,5 +83,32 @@ test.describe('Tracker issue tests', () => {
...editIssue,
estimation: '1d'
})
const estimations = new Map([
['0', '0h'],
['1', '1h'],
['1.25', '1h 15m'],
['1.259', '1h 15m'],
['1.26', '1h 15m'],
['1.27', '1h 16m'],
['1.5', '1h 30m'],
['1.75', '1h 45m'],
['2', '2h'],
['7', '7h'],
['8', '1d'],
['9', '1d 1h'],
['9.5', '1d 1h 30m']
])
for (const [input, expected] of estimations.entries()) {
await issuesDetailsPage.editIssue({
estimation: input
})
await issuesDetailsPage.checkIssue({
...newIssue,
...editIssue,
estimation: expected
})
}
})
})

View File

@ -49,7 +49,7 @@ test.describe('Tracker template tests', () => {
test('Edit a Template', async ({ page }) => {
const newTemplate: NewIssue = {
title: 'Template for edit',
title: `Template for edit-${generateId()}`,
description: 'Created template for edit'
}
@ -70,6 +70,7 @@ test.describe('Tracker template tests', () => {
await trackerNavigationMenuPage.buttonTemplates.click()
const templatePage = new TemplatePage(page)
await templatePage.createNewTemplate(newTemplate)
await templatePage.openTemplate(newTemplate.title)
const templateDetailsPage = new TemplateDetailsPage(page)
@ -82,9 +83,32 @@ test.describe('Tracker template tests', () => {
})
await templateDetailsPage.checkCommentExist('Appleseed John created template')
await templateDetailsPage.checkCommentExist('Appleseed John changed priority to High')
await templateDetailsPage.checkCommentExist('Appleseed John changed assignee to Dirak Kainin')
await templateDetailsPage.checkCommentExist('Appleseed John changed estimation to 1d')
await templateDetailsPage.checkCommentExist('Appleseed John changed due date')
const estimations = new Map([
['0', '0h'],
['1', '1h'],
['1.25', '1h 15m'],
['1.259', '1h 15m'],
['1.26', '1h 15m'],
['1.27', '1h 16m'],
['1.5', '1h 30m'],
['1.75', '1h 45m'],
['2', '2h'],
['7', '7h'],
['8', '1d'],
['9', '1d 1h'],
['9.5', '1d 1h 30m']
])
for (const [input, expected] of estimations.entries()) {
await templateDetailsPage.editTemplate({
estimation: input
})
await templateDetailsPage.checkTemplate({
...newTemplate,
...editTemplate,
estimation: expected
})
}
})
})

View File

@ -213,9 +213,20 @@ export async function floorFractionDigits (n: number | string, amount: number):
}
export async function toTime (value: number): Promise<string> {
if (value > 0 && value < 8) {
return `${await floorFractionDigits(value, 2)}h`
} else {
return `${await floorFractionDigits(value / 8, 3)}d`
if (value <= 0) {
return '0h'
}
// TODO: Make configurable?
const hoursInWorkingDay = 8
const days = Math.floor(value / hoursInWorkingDay)
const hours = Math.floor(value % hoursInWorkingDay)
const minutes = Math.floor((value % 1) * 60)
return [
...(days > 0 ? [`${days}d`] : []),
...(hours > 0 ? [`${hours}h`] : []),
...(minutes > 0 ? [`${minutes}m`] : [])
].join(' ')
}