@happyvertical/smrt-timesheets 0.55.0 → 0.55.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/AGENTS.md +8 -0
- package/dist/attendance.js +2 -2
- package/dist/chunks/{attendance-kUM0ANfE.js → attendance-4_GFyiq5.js} +3 -3
- package/dist/chunks/{attendance-kUM0ANfE.js.map → attendance-4_GFyiq5.js.map} +1 -1
- package/dist/chunks/{models-DNbDLJCb.js → models-C8pv3ICs.js} +2 -2
- package/dist/chunks/{models-DNbDLJCb.js.map → models-C8pv3ICs.js.map} +1 -1
- package/dist/chunks/{rollup-D18ToLCo.js → rollup-BvUuTKOe.js} +3 -3
- package/dist/chunks/{rollup-D18ToLCo.js.map → rollup-BvUuTKOe.js.map} +1 -1
- package/dist/chunks/{service-time-entry-BAkx99ks.js → service-time-entry-NSayW3DB.js} +2 -2
- package/dist/chunks/service-time-entry-NSayW3DB.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/manifest.json +26 -5
- package/dist/rollup.js +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/dist/svelte/components/TimeEntryApprovalQueue.svelte +21 -11
- package/dist/svelte/components/TimeEntryApprovalQueue.svelte.d.ts +8 -0
- package/dist/svelte/components/TimeEntryApprovalQueue.svelte.d.ts.map +1 -1
- package/dist/svelte/components/TimeEntryCard.svelte +10 -1
- package/dist/svelte/components/TimeEntryCard.svelte.d.ts +5 -1
- package/dist/svelte/components/TimeEntryCard.svelte.d.ts.map +1 -1
- package/dist/svelte/components/TimeEntryList.svelte +11 -1
- package/dist/svelte/components/TimeEntryList.svelte.d.ts +6 -1
- package/dist/svelte/components/TimeEntryList.svelte.d.ts.map +1 -1
- package/dist/svelte/components/TimeSummary.svelte +12 -4
- package/dist/svelte/components/TimeSummary.svelte.d.ts +3 -1
- package/dist/svelte/components/TimeSummary.svelte.d.ts.map +1 -1
- package/dist/svelte/components/utils.d.ts +6 -1
- package/dist/svelte/components/utils.d.ts.map +1 -1
- package/dist/svelte/components/utils.js +41 -9
- package/dist/svelte/index.d.ts +1 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -1
- package/package.json +7 -7
- package/dist/chunks/service-time-entry-BAkx99ks.js.map +0 -1
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
formatCurrency,
|
|
13
13
|
formatDate,
|
|
14
14
|
formatHours,
|
|
15
|
+
type HoursFormatter,
|
|
15
16
|
statusColors,
|
|
16
17
|
type TimeEntry,
|
|
17
18
|
} from './utils.js';
|
|
@@ -32,6 +33,10 @@ export interface Props {
|
|
|
32
33
|
onselect?: (id: string, selected: boolean) => void;
|
|
33
34
|
/** Currency code for formatting hourly rates and amounts. */
|
|
34
35
|
currency?: Currency;
|
|
36
|
+
/** Formats decimal hours; defaults to the legacy one-decimal display. */
|
|
37
|
+
hoursFormatter?: HoursFormatter;
|
|
38
|
+
/** Renders caller-owned evidence below the standard entry fields. */
|
|
39
|
+
details?: Snippet<[TimeEntry]>;
|
|
35
40
|
/** Renders action buttons on the right side of the card. */
|
|
36
41
|
actions?: Snippet;
|
|
37
42
|
}
|
|
@@ -44,6 +49,8 @@ let {
|
|
|
44
49
|
selected = false,
|
|
45
50
|
onselect,
|
|
46
51
|
currency = 'CAD',
|
|
52
|
+
hoursFormatter = formatHours,
|
|
53
|
+
details,
|
|
47
54
|
actions,
|
|
48
55
|
}: Props = $props();
|
|
49
56
|
|
|
@@ -140,7 +147,7 @@ function handleKeydown(event: KeyboardEvent) {
|
|
|
140
147
|
|
|
141
148
|
<div class="entry-footer">
|
|
142
149
|
<div class="hours">
|
|
143
|
-
<span class="hours-value">{
|
|
150
|
+
<span class="hours-value">{hoursFormatter(entry.hours)}</span>
|
|
144
151
|
{#if entry.hourlyRate}
|
|
145
152
|
<span class="rate">@ {formatCurrency(entry.hourlyRate, currency)}/hr</span>
|
|
146
153
|
{/if}
|
|
@@ -155,6 +162,8 @@ function handleKeydown(event: KeyboardEvent) {
|
|
|
155
162
|
{t(M['projects.time_entry_card.mileage'], { mileage: entry.mileage })}
|
|
156
163
|
</div>
|
|
157
164
|
{/if}
|
|
165
|
+
|
|
166
|
+
{@render details?.(entry)}
|
|
158
167
|
{/snippet}
|
|
159
168
|
|
|
160
169
|
<style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
import { type Currency, type TimeEntry } from './utils.js';
|
|
2
|
+
import { type Currency, type HoursFormatter, type TimeEntry } from './utils.js';
|
|
3
3
|
/** Props for TimeEntryCard component */
|
|
4
4
|
export interface Props {
|
|
5
5
|
/** Time entry data to display. */
|
|
@@ -16,6 +16,10 @@ export interface Props {
|
|
|
16
16
|
onselect?: (id: string, selected: boolean) => void;
|
|
17
17
|
/** Currency code for formatting hourly rates and amounts. */
|
|
18
18
|
currency?: Currency;
|
|
19
|
+
/** Formats decimal hours; defaults to the legacy one-decimal display. */
|
|
20
|
+
hoursFormatter?: HoursFormatter;
|
|
21
|
+
/** Renders caller-owned evidence below the standard entry fields. */
|
|
22
|
+
details?: Snippet<[TimeEntry]>;
|
|
19
23
|
/** Renders action buttons on the right side of the card. */
|
|
20
24
|
actions?: Snippet;
|
|
21
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeEntryCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeEntryCard.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EACL,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"TimeEntryCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeEntryCard.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EACL,KAAK,QAAQ,EAIb,KAAK,cAAc,EAEnB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAGpB,wCAAwC;AACxC,MAAM,WAAW,KAAK;IACpB,kCAAkC;IAClC,KAAK,EAAE,SAAS,CAAC;IACjB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAwHD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
8
|
+
import type { Snippet } from 'svelte';
|
|
8
9
|
import { M } from '../i18n.js';
|
|
9
10
|
import {
|
|
10
11
|
type Currency,
|
|
11
12
|
formatCurrency,
|
|
12
13
|
formatDate,
|
|
13
14
|
formatHours,
|
|
15
|
+
type HoursFormatter,
|
|
14
16
|
statusColors,
|
|
15
17
|
type TimeEntry,
|
|
16
18
|
} from './utils.js';
|
|
@@ -33,6 +35,10 @@ export interface Props {
|
|
|
33
35
|
baseHref?: string;
|
|
34
36
|
/** Currency code for formatting amounts. */
|
|
35
37
|
currency?: Currency;
|
|
38
|
+
/** Formats decimal hours; defaults to the legacy one-decimal display. */
|
|
39
|
+
hoursFormatter?: HoursFormatter;
|
|
40
|
+
/** Renders caller-owned evidence in each entry row. */
|
|
41
|
+
details?: Snippet<[TimeEntry]>;
|
|
36
42
|
/** Filter function to determine which entries can be selected */
|
|
37
43
|
canSelect?: (entry: TimeEntry) => boolean;
|
|
38
44
|
}
|
|
@@ -45,6 +51,8 @@ let {
|
|
|
45
51
|
emptyMessage = 'No time entries',
|
|
46
52
|
baseHref,
|
|
47
53
|
currency = 'CAD',
|
|
54
|
+
hoursFormatter = formatHours,
|
|
55
|
+
details,
|
|
48
56
|
canSelect = () => true,
|
|
49
57
|
}: Props = $props();
|
|
50
58
|
|
|
@@ -161,7 +169,7 @@ function getEntryHref(entry: TimeEntry): string | undefined {
|
|
|
161
169
|
</div>
|
|
162
170
|
|
|
163
171
|
<div class="hours-cell">
|
|
164
|
-
{
|
|
172
|
+
{hoursFormatter(entry.hours)}
|
|
165
173
|
</div>
|
|
166
174
|
|
|
167
175
|
<div class="status-cell">
|
|
@@ -175,6 +183,8 @@ function getEntryHref(entry: TimeEntry): string | undefined {
|
|
|
175
183
|
{formatCurrency(entry.amount, currency)}
|
|
176
184
|
</div>
|
|
177
185
|
{/if}
|
|
186
|
+
|
|
187
|
+
{@render details?.(entry)}
|
|
178
188
|
{/snippet}
|
|
179
189
|
|
|
180
190
|
<style>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type Currency, type HoursFormatter, type TimeEntry } from './utils.js';
|
|
2
3
|
/** Props for TimeEntryList component */
|
|
3
4
|
export interface Props {
|
|
4
5
|
/** Time entries to display in the list. */
|
|
@@ -15,6 +16,10 @@ export interface Props {
|
|
|
15
16
|
baseHref?: string;
|
|
16
17
|
/** Currency code for formatting amounts. */
|
|
17
18
|
currency?: Currency;
|
|
19
|
+
/** Formats decimal hours; defaults to the legacy one-decimal display. */
|
|
20
|
+
hoursFormatter?: HoursFormatter;
|
|
21
|
+
/** Renders caller-owned evidence in each entry row. */
|
|
22
|
+
details?: Snippet<[TimeEntry]>;
|
|
18
23
|
/** Filter function to determine which entries can be selected */
|
|
19
24
|
canSelect?: (entry: TimeEntry) => boolean;
|
|
20
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeEntryList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeEntryList.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TimeEntryList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeEntryList.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EACL,KAAK,QAAQ,EAIb,KAAK,cAAc,EAEnB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAGpB,wCAAwC;AACxC,MAAM,WAAW,KAAK;IACpB,2CAA2C;IAC3C,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,0CAA0C;IAC1C,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5C,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/B,iEAAiE;IACjE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC;CAC3C;AAgJD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
8
8
|
import { M } from '../i18n.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
type Currency,
|
|
11
|
+
formatCurrency,
|
|
12
|
+
formatHours,
|
|
13
|
+
type HoursFormatter,
|
|
14
|
+
} from './utils.js';
|
|
10
15
|
|
|
11
16
|
/** Props for TimeSummary component */
|
|
12
17
|
export interface Props {
|
|
@@ -26,6 +31,8 @@ export interface Props {
|
|
|
26
31
|
entryCount?: number;
|
|
27
32
|
/** Currency code for formatting amounts. */
|
|
28
33
|
currency?: Currency;
|
|
34
|
+
/** Formats decimal hours; defaults to the legacy one-decimal display. */
|
|
35
|
+
hoursFormatter?: HoursFormatter;
|
|
29
36
|
/** Shows the pending hours and amount card when there are pending items. */
|
|
30
37
|
showPending?: boolean;
|
|
31
38
|
/** Shows the approved hours and amount card when there are approved items. */
|
|
@@ -43,6 +50,7 @@ let {
|
|
|
43
50
|
approvedAmount = 0,
|
|
44
51
|
entryCount,
|
|
45
52
|
currency = 'CAD',
|
|
53
|
+
hoursFormatter = formatHours,
|
|
46
54
|
showPending = true,
|
|
47
55
|
showApproved = false,
|
|
48
56
|
layout = 'grid',
|
|
@@ -54,7 +62,7 @@ const { t } = useI18n();
|
|
|
54
62
|
<div class="time-summary" class:horizontal={layout === 'horizontal'}>
|
|
55
63
|
<div class="summary-card">
|
|
56
64
|
<span class="label">{t(M['projects.time_summary.total_hours'])}</span>
|
|
57
|
-
<span class="value">{
|
|
65
|
+
<span class="value">{hoursFormatter(totalHours)}</span>
|
|
58
66
|
{#if entryCount !== undefined}
|
|
59
67
|
<span class="count">{entryCount} {entryCount === 1 ? 'entry' : 'entries'}</span>
|
|
60
68
|
{/if}
|
|
@@ -68,7 +76,7 @@ const { t } = useI18n();
|
|
|
68
76
|
{#if showPending && (pendingHours > 0 || pendingAmount > 0)}
|
|
69
77
|
<div class="summary-card highlight">
|
|
70
78
|
<span class="label">{t(M['projects.time_summary.pending_approval'])}</span>
|
|
71
|
-
<span class="value">{
|
|
79
|
+
<span class="value">{hoursFormatter(pendingHours)}</span>
|
|
72
80
|
<span class="sub-value">{formatCurrency(pendingAmount, currency)}</span>
|
|
73
81
|
</div>
|
|
74
82
|
{/if}
|
|
@@ -76,7 +84,7 @@ const { t } = useI18n();
|
|
|
76
84
|
{#if showApproved && (approvedHours > 0 || approvedAmount > 0)}
|
|
77
85
|
<div class="summary-card success">
|
|
78
86
|
<span class="label">Approved</span>
|
|
79
|
-
<span class="value">{
|
|
87
|
+
<span class="value">{hoursFormatter(approvedHours)}</span>
|
|
80
88
|
<span class="sub-value">{formatCurrency(approvedAmount, currency)}</span>
|
|
81
89
|
</div>
|
|
82
90
|
{/if}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Currency } from './utils.js';
|
|
1
|
+
import { type Currency, type HoursFormatter } from './utils.js';
|
|
2
2
|
/** Props for TimeSummary component */
|
|
3
3
|
export interface Props {
|
|
4
4
|
/** Total hours across all time entries. */
|
|
@@ -17,6 +17,8 @@ export interface Props {
|
|
|
17
17
|
entryCount?: number;
|
|
18
18
|
/** Currency code for formatting amounts. */
|
|
19
19
|
currency?: Currency;
|
|
20
|
+
/** Formats decimal hours; defaults to the legacy one-decimal display. */
|
|
21
|
+
hoursFormatter?: HoursFormatter;
|
|
20
22
|
/** Shows the pending hours and amount card when there are pending items. */
|
|
21
23
|
showPending?: boolean;
|
|
22
24
|
/** Shows the approved hours and amount card when there are approved items. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeSummary.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeSummary.svelte.ts"],"names":[],"mappings":"AASA,OAAO,
|
|
1
|
+
{"version":3,"file":"TimeSummary.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeSummary.svelte.ts"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,QAAQ,EAGb,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAGpB,sCAAsC;AACtC,MAAM,WAAW,KAAK;IACpB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;CAChC;AA8DD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared utilities for time tracking components
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
/** ISO 4217 currency code used to display integer minor-unit amounts. */
|
|
5
|
+
export type Currency = string;
|
|
6
|
+
/** Caller-controlled decimal-hours presentation. */
|
|
7
|
+
export type HoursFormatter = (hours: number) => string;
|
|
5
8
|
export type TimeEntryStatus = 'draft' | 'submitted' | 'approved' | 'rejected';
|
|
6
9
|
/** Alias for TimeEntryStatus for approval workflows */
|
|
7
10
|
export type ApprovalStatus = TimeEntryStatus;
|
|
@@ -29,6 +32,8 @@ export declare const statusColors: Record<TimeEntryStatus, string>;
|
|
|
29
32
|
* Format a date for display (e.g., "Jan 5")
|
|
30
33
|
*/
|
|
31
34
|
export declare function formatDate(date: Date | string): string;
|
|
35
|
+
/** Return the ISO 4217 minor-unit exponent used by timesheet amounts. */
|
|
36
|
+
export declare function currencyMinorUnitExponent(currency: Currency): number;
|
|
32
37
|
/**
|
|
33
38
|
* Format a minor-units amount for display.
|
|
34
39
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,yEAAyE;AACzE,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,oDAAoD;AACpD,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9E,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;IACxB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAKxD,CAAC;AAEF;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAGtD;AAmCD,yEAAyE;AACzE,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,QAAgB,GACzB,MAAM,CAUR;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrD"}
|
|
@@ -17,6 +17,42 @@ export function formatDate(date) {
|
|
|
17
17
|
const d = typeof date === 'string' ? new Date(date) : date;
|
|
18
18
|
return d.toLocaleDateString('en-CA', { month: 'short', day: 'numeric' });
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* ISO 4217 exponents, matching the SDK accounting and commerce tables.
|
|
22
|
+
* ICU display precision is not a monetary scale (notably IQD, MGA and ALL).
|
|
23
|
+
*/
|
|
24
|
+
const ISO_EXPONENTS = {
|
|
25
|
+
BIF: 0,
|
|
26
|
+
CLP: 0,
|
|
27
|
+
DJF: 0,
|
|
28
|
+
GNF: 0,
|
|
29
|
+
ISK: 0,
|
|
30
|
+
JPY: 0,
|
|
31
|
+
KMF: 0,
|
|
32
|
+
KRW: 0,
|
|
33
|
+
PYG: 0,
|
|
34
|
+
RWF: 0,
|
|
35
|
+
UGX: 0,
|
|
36
|
+
UYI: 0,
|
|
37
|
+
VND: 0,
|
|
38
|
+
VUV: 0,
|
|
39
|
+
XAF: 0,
|
|
40
|
+
XOF: 0,
|
|
41
|
+
XPF: 0,
|
|
42
|
+
BHD: 3,
|
|
43
|
+
IQD: 3,
|
|
44
|
+
JOD: 3,
|
|
45
|
+
KWD: 3,
|
|
46
|
+
LYD: 3,
|
|
47
|
+
OMR: 3,
|
|
48
|
+
TND: 3,
|
|
49
|
+
CLF: 4,
|
|
50
|
+
UYW: 4,
|
|
51
|
+
};
|
|
52
|
+
/** Return the ISO 4217 minor-unit exponent used by timesheet amounts. */
|
|
53
|
+
export function currencyMinorUnitExponent(currency) {
|
|
54
|
+
return ISO_EXPONENTS[currency.trim().toUpperCase()] ?? 2;
|
|
55
|
+
}
|
|
20
56
|
/**
|
|
21
57
|
* Format a minor-units amount for display.
|
|
22
58
|
*
|
|
@@ -28,18 +64,14 @@ export function formatDate(date) {
|
|
|
28
64
|
* so zero-decimal currencies (JPY, KRW) are not divided by anything.
|
|
29
65
|
*/
|
|
30
66
|
export function formatCurrency(amount, currency = 'CAD') {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// (as this did before minor units) would pin `maximumFractionDigits` to 2 as
|
|
34
|
-
// well, so a zero-decimal currency like JPY would report a scale of 2 — and
|
|
35
|
-
// then render `¥1,999.00` for a currency that has no fractional part at all.
|
|
36
|
-
// For CAD/USD the resolved default is already 2, so their output is
|
|
37
|
-
// unchanged.
|
|
67
|
+
const code = currency.trim().toUpperCase();
|
|
68
|
+
const digits = currencyMinorUnitExponent(code);
|
|
38
69
|
const format = new Intl.NumberFormat('en-CA', {
|
|
39
70
|
style: 'currency',
|
|
40
|
-
currency,
|
|
71
|
+
currency: code,
|
|
72
|
+
minimumFractionDigits: digits,
|
|
73
|
+
maximumFractionDigits: digits,
|
|
41
74
|
});
|
|
42
|
-
const digits = format.resolvedOptions().maximumFractionDigits ?? 2;
|
|
43
75
|
return format.format(amount / 10 ** digits);
|
|
44
76
|
}
|
|
45
77
|
/**
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -20,6 +20,6 @@ export type TimeEntryApprovalQueueProps = ComponentProps<typeof TimeEntryApprova
|
|
|
20
20
|
export type TimeEntryCardProps = ComponentProps<typeof TimeEntryCard>;
|
|
21
21
|
export type TimeEntryListProps = ComponentProps<typeof TimeEntryList>;
|
|
22
22
|
export type TimeSummaryProps = ComponentProps<typeof TimeSummary>;
|
|
23
|
-
export { type ApprovalStatus, type Currency, formatCurrency, formatDate, formatHours, formatHoursHHMM, statusColors, type TimeEntry, type TimeEntryStatus, } from './components/utils.js';
|
|
23
|
+
export { type ApprovalStatus, type Currency, currencyMinorUnitExponent, formatCurrency, formatDate, formatHours, formatHoursHHMM, type HoursFormatter, statusColors, type TimeEntry, type TimeEntryStatus, } from './components/utils.js';
|
|
24
24
|
export { humanizeTimeEntryStatus, type TimeEntryApprovalView, timeEntryStatusBadgeKey, } from './types.js';
|
|
25
25
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAChF,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAE1D,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,WAAW,GACZ,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1E,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,sBAAsB,CAC9B,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAElE,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,cAAc,EACd,UAAU,EACV,WAAW,EACX,eAAe,EACf,YAAY,EACZ,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,uBAAuB,GACxB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAChF,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAE1D,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,WAAW,GACZ,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1E,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,sBAAsB,CAC9B,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAElE,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,yBAAyB,EACzB,cAAc,EACd,UAAU,EACV,WAAW,EACX,eAAe,EACf,KAAK,cAAc,EACnB,YAAY,EACZ,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,uBAAuB,GACxB,MAAM,YAAY,CAAC"}
|
package/dist/svelte/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import TimeEntryCard from './components/TimeEntryCard.svelte';
|
|
|
16
16
|
import TimeEntryList from './components/TimeEntryList.svelte';
|
|
17
17
|
import TimeSummary from './components/TimeSummary.svelte';
|
|
18
18
|
export { DurationDisplay, TimeEntryApprovalQueue, TimeEntryCard, TimeEntryList, TimeSummary, };
|
|
19
|
-
export { formatCurrency, formatDate, formatHours, formatHoursHHMM, statusColors, } from './components/utils.js';
|
|
19
|
+
export { currencyMinorUnitExponent, formatCurrency, formatDate, formatHours, formatHoursHHMM, statusColors, } from './components/utils.js';
|
|
20
20
|
export { humanizeTimeEntryStatus, timeEntryStatusBadgeKey, } from './types.js';
|
|
21
21
|
// Auto-register with ModuleUIRegistry
|
|
22
22
|
ModuleUIRegistry.registerModule(TIMESHEETS_MODULE_META);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-timesheets",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.2",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"description": "Shared time entries for the SMRT framework: who worked how long on what, approval and correction, with immutable charge and compensation snapshots",
|
|
6
6
|
"type": "module",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@happyvertical/smrt-core": "0.55.
|
|
43
|
-
"@happyvertical/smrt-tenancy": "0.55.
|
|
44
|
-
"@happyvertical/smrt-types": "0.55.
|
|
45
|
-
"@happyvertical/smrt-ui": "0.55.
|
|
46
|
-
"@happyvertical/sql": "^0.102.
|
|
42
|
+
"@happyvertical/smrt-core": "0.55.2",
|
|
43
|
+
"@happyvertical/smrt-tenancy": "0.55.2",
|
|
44
|
+
"@happyvertical/smrt-types": "0.55.2",
|
|
45
|
+
"@happyvertical/smrt-ui": "0.55.2",
|
|
46
|
+
"@happyvertical/sql": "^0.102.2"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"svelte": "^5.57.0"
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@happyvertical/smrt-vitest": "0.55.
|
|
57
|
+
"@happyvertical/smrt-vitest": "0.55.2",
|
|
58
58
|
"@sveltejs/package": "^2.5.8",
|
|
59
59
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
60
60
|
"@types/node": "24.13.6",
|