@kernhq/module-tracker 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernhq/module-tracker",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Kern tracker module: projects, work item types, custom fields, workflows, issues, KQL, cycles, views, reports, intake, time tracking (server + client skeleton)",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { formatDuration } from './format.js'
3
+
4
+ describe('formatDuration', () => {
5
+ it('keeps minutes below an hour', () => {
6
+ // A timer running for twenty minutes used to read "0h".
7
+ expect(formatDuration(60)).toBe('1m')
8
+ expect(formatDuration(20 * 60)).toBe('20m')
9
+ expect(formatDuration(59 * 60)).toBe('59m')
10
+ })
11
+
12
+ it('keeps minutes below a day', () => {
13
+ // 90 logged minutes used to read "2h", which is not a rounding, it is wrong.
14
+ expect(formatDuration(90 * 60)).toBe('1h 30m')
15
+ expect(formatDuration(2 * 3600)).toBe('2h')
16
+ expect(formatDuration(7 * 3600 + 45 * 60)).toBe('7h 45m')
17
+ })
18
+
19
+ it('drops minutes above a day, where they stop being the point', () => {
20
+ expect(formatDuration(26 * 3600)).toBe('1d 2h')
21
+ expect(formatDuration(48 * 3600)).toBe('2d')
22
+ expect(formatDuration(26 * 3600 + 30 * 60)).toBe('1d 2h')
23
+ })
24
+
25
+ it('says nothing rather than something wrong for nothing at all', () => {
26
+ expect(formatDuration(0)).toBe('0h')
27
+ expect(formatDuration(-5)).toBe('0h')
28
+ })
29
+ })
@@ -153,12 +153,25 @@ export function daysUntil(dueDate: string, today = new Date()): number {
153
153
  return Math.round((due - start) / 86_400_000)
154
154
  }
155
155
 
156
- /** Seconds of logged time as the board footer shows it ("0h", "3h", "1d 2h"). */
156
+ /**
157
+ * Seconds of time as somebody would say it: `45m`, `1h 30m`, `3h`, `1d 2h`.
158
+ *
159
+ * Minutes matter below a day. Rounding to whole hours reported 90 logged minutes as "2h" and a
160
+ * timer running for twenty as "0h" — for an estimate that is a rounding, for time somebody tracked
161
+ * it is wrong, and the same function shows both.
162
+ *
163
+ * Above a day, minutes stop being the point and are dropped.
164
+ */
157
165
  export function formatDuration(seconds: number): string {
158
166
  if (seconds <= 0) return '0h'
159
- const hours = Math.round(seconds / 3600)
160
- if (hours < 24) return `${hours}h`
161
- const days = Math.floor(hours / 24)
162
- const rest = hours % 24
163
- return rest ? `${days}d ${rest}h` : `${days}d`
167
+ const totalMinutes = Math.round(seconds / 60)
168
+ if (totalMinutes < 60) return `${totalMinutes}m`
169
+
170
+ const totalHours = Math.floor(totalMinutes / 60)
171
+ const minutes = totalMinutes % 60
172
+ if (totalHours < 24) return minutes ? `${totalHours}h ${minutes}m` : `${totalHours}h`
173
+
174
+ const days = Math.floor(totalHours / 24)
175
+ const hours = totalHours % 24
176
+ return hours ? `${days}d ${hours}h` : `${days}d`
164
177
  }
@@ -55,6 +55,8 @@ export type {
55
55
  StatusHistoryEntry,
56
56
  StatusInfo,
57
57
  SystemFieldId,
58
+ Timer,
59
+ TimesheetRow,
58
60
  UpdateIssue,
59
61
  Version,
60
62
  View,
@@ -62,4 +64,5 @@ export type {
62
64
  ViewLayout,
63
65
  Workflow,
64
66
  WorkItemType,
67
+ Worklog,
65
68
  } from '../contract/index.js'