mgv-backoffice 1.46.0 → 1.47.0

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.
@@ -53,6 +53,21 @@ export declare function fmtUsd(v: number): string;
53
53
  * strings so BigDecimal-as-string payloads pass through.
54
54
  */
55
55
  export declare function fmtMoney(n: number | string | null | undefined, digits?: number, currency?: string): string;
56
+ /**
57
+ * Display symbol for an ISO / ticker currency code: "€" for EUR, "£" for
58
+ * GBP, "$" for USD and the USD stablecoins — and for a missing code, so
59
+ * rows with no currency keep reading as dollars. Any other code (BTC,
60
+ * ETH, …) has no symbol and returns null; callers suffix the code instead.
61
+ */
62
+ export declare function currencySymbol(code: string | null | undefined): string | null;
63
+ /**
64
+ * Format an amount in its own currency: fiat with a symbol goes through
65
+ * fmtMoney ("€20.00", "-$1.50"), a USD stablecoin keeps its ticker after
66
+ * the dollar figure ("$25.00 USDT") so it isn't mistaken for cash, and any
67
+ * other asset prints as a plain quantity with its code ("0.5 BTC", up to 8
68
+ * decimals). Em-dash for missing / non-finite amounts, like fmtMoney.
69
+ */
70
+ export declare function fmtCurrencyAmount(n: number | string | null | undefined, code: string | null | undefined, digits?: number): string;
56
71
  /**
57
72
  * Format an ISO date string / epoch as a "Mon D, YYYY" calendar label
58
73
  * (en-US, e.g. "Jan 5, 2026"). Falls back to the raw value on parse
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mgv-backoffice",
3
- "version": "1.46.0",
3
+ "version": "1.47.0",
4
4
  "description": "Shared Vue 3 UI component library",
5
5
  "type": "module",
6
6
  "main": "./dist/ui-lib.umd.cjs",
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import BaseIdChip from './BaseIdChip.vue'
4
+
5
+ describe('BaseIdChip', () => {
6
+ it('shows the label and the full id in the tooltip', () => {
7
+ const w = mount(BaseIdChip, { props: { label: 'ord', value: 'abc-123-def' } })
8
+ expect(w.text()).toContain('ord')
9
+ expect(w.get('code').text()).toBe('abc-123-def')
10
+ expect(w.get('code').attributes('title')).toBe('abc-123-def')
11
+ })
12
+
13
+ it('labels the copy button from label + value, or copyLabel when given', () => {
14
+ const plain = mount(BaseIdChip, { props: { label: 'id', value: 'x1' } })
15
+ expect(plain.get('button[aria-label="Copy id x1"]').exists()).toBe(true)
16
+ const custom = mount(BaseIdChip, { props: { label: 'id', value: 'x1', copyLabel: 'tx id' } })
17
+ expect(custom.get('button[aria-label="Copy tx id"]').exists()).toBe(true)
18
+ })
19
+
20
+ it('renders the action button only with actionLabel, emitting the value', async () => {
21
+ expect(mount(BaseIdChip, { props: { value: 'x1' } }).findAll('button')).toHaveLength(1)
22
+ const w = mount(BaseIdChip, { props: { value: 'x1', actionLabel: 'Search this id' } })
23
+ await w.get('button[aria-label="Search this id"]').trigger('click')
24
+ expect(w.emitted('action')).toEqual([['x1']])
25
+ })
26
+ })
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <!--
3
+ Compact id cell: a small uppercase label, the id truncated in a <code>
4
+ pill (full value in the tooltip), a copy button, and — when
5
+ `actionLabel` is set — a magnifier button that emits `action` (e.g.
6
+ "search the log for this id"). Built for dense rows that carry several
7
+ ids side by side (transaction / buy-order / sell-order).
8
+ -->
9
+ <div class="inline-flex items-center gap-1 font-mono text-[11px]">
10
+ <span v-if="label" class="text-slate-500 dark:text-slate-400 uppercase tracking-wide">{{ label }}</span>
11
+ <code
12
+ class="truncate rounded bg-slate-100 px-1 text-slate-600 dark:bg-slate-800 dark:text-slate-300"
13
+ :class="maxWidthClass"
14
+ :title="value"
15
+ >{{ value }}</code>
16
+ <BaseCopyButton :text="value" :label="copyLabel ?? `${label} ${value}`.trim()" icon-class="w-3.5 h-3.5" />
17
+ <button
18
+ v-if="actionLabel"
19
+ type="button"
20
+ :title="actionLabel"
21
+ :aria-label="actionLabel"
22
+ class="rounded px-0.5 text-slate-400 hover:bg-slate-100 hover:text-emerald-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500 dark:hover:bg-slate-800 dark:hover:text-emerald-400"
23
+ @click="emit('action', value)"
24
+ >
25
+ <MagnifyingGlassIcon class="h-3.5 w-3.5" aria-hidden="true" />
26
+ </button>
27
+ </div>
28
+ </template>
29
+
30
+ <script setup lang="ts">
31
+ import { MagnifyingGlassIcon } from '@heroicons/vue/24/outline'
32
+ import BaseCopyButton from './BaseCopyButton.vue'
33
+
34
+ withDefaults(
35
+ defineProps<{
36
+ /** The id itself — shown truncated, copied in full. */
37
+ value: string
38
+ /** Short uppercase prefix ("id", "ord", "sell"). Omit for no prefix. */
39
+ label?: string
40
+ /** Copy button's tooltip / aria noun; defaults to "<label> <value>". */
41
+ copyLabel?: string
42
+ /** Tooltip / aria-label of the action button; the button only renders when set. */
43
+ actionLabel?: string
44
+ /** Width cap of the truncated id. */
45
+ maxWidthClass?: string
46
+ }>(),
47
+ { label: '', copyLabel: undefined, actionLabel: undefined, maxWidthClass: 'max-w-[90px]' },
48
+ )
49
+
50
+ const emit = defineEmits<{ action: [value: string] }>()
51
+ </script>
package/src/index.ts CHANGED
@@ -52,6 +52,7 @@ export { default as BaseCredentialsForm } from './components/BaseCredentialsForm
52
52
  export { default as BasePillPickerModal } from './components/BasePillPickerModal.vue'
53
53
  export { default as BaseBarDistribution } from './components/BaseBarDistribution.vue'
54
54
  export { default as BaseGroupedChecklist } from './components/BaseGroupedChecklist.vue'
55
+ export { default as BaseIdChip } from './components/BaseIdChip.vue'
55
56
  export type { CredentialsView, CredentialsUpdate } from './components/BaseCredentialsForm.vue'
56
57
 
57
58
  // Components — third-party wrappers
@@ -144,6 +145,8 @@ export {
144
145
  fmtPct,
145
146
  fmtUsd,
146
147
  fmtMoney,
148
+ currencySymbol,
149
+ fmtCurrencyAmount,
147
150
  fmtDuration,
148
151
  formatJson,
149
152
  stringifyValue,