dev-api-ui 0.1.7 → 0.1.9

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,7 @@
1
1
  {
2
2
  "name": "dev-api-ui",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
+ "private": false,
4
5
  "files": [
5
6
  "ui"
6
7
  ],
@@ -70,6 +70,12 @@ const GROUP_CONFIG: Record<string, GroupConfig> = {
70
70
 
71
71
  type Row = { sym: string; name: string; last: string; delta: string; up: boolean; status: string; cat: string; slug: string };
72
72
 
73
+ // Route slug derived from the symbol so links always follow the house lowercase-hyphen convention
74
+ // (EUR/USD → eur-usd), matching every vertical's /<group>/<base>-<quote> routes. Used by BOTH the
75
+ // leaf "Popular" cards and the list/search RowTable — the hand-maintained `slug` field shipped
76
+ // un-hyphenated demo values (eurusd) that 404'd (BUG-3). Slash-less symbols (PJM-RT) just lowercase.
77
+ const routeSlug = (sym: string) => sym.toLowerCase().replace('/', '-');
78
+
73
79
  const GROUP_ROWS: Record<string, Row[]> = {
74
80
  fx: [
75
81
  { sym: 'EUR/USD', name: 'Euro / US Dollar', last: '1.08642', delta: '+0.31%', up: true, status: 'Live', cat: 'Majors', slug: 'eurusd' },
@@ -145,7 +151,7 @@ function deltaColor(up: boolean): string {
145
151
 
146
152
  // The shared table / list layout used in both list and search modes
147
153
  function RowTable({ rows, group }: { rows: Row[]; group: string }) {
148
- const slugFor = (r: Row) => r.slug;
154
+ const slugFor = (r: Row) => routeSlug(r.sym);
149
155
  return (
150
156
  <div
151
157
  style={{
@@ -374,7 +380,7 @@ export default function HubPage({ group, description, pairs: pairsOverride, eyeb
374
380
  {popular.map((p) => (
375
381
  <Link
376
382
  key={p.sym}
377
- href={`/${group}/${p.slug}`}
383
+ href={`/${group}/${routeSlug(p.sym)}`}
378
384
  style={{
379
385
  display: 'block',
380
386
  background: 'var(--surface)',
package/ui/format.ts ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Currency/number formatting helpers backed by Intl.NumberFormat + CLDR.
3
+ * Zero-decimal currencies (JPY, KRW, VND, …) render without fractional digits
4
+ * automatically — no lookup table required.
5
+ */
6
+
7
+ /**
8
+ * Returns the correct maximumFractionDigits for a currency code, or 2 on error.
9
+ * Uses a throw-away Intl.NumberFormat so we never duplicate CLDR data.
10
+ */
11
+ function fractionDigits(currencyCode: string, locale: string): number {
12
+ try {
13
+ return new Intl.NumberFormat(locale, {
14
+ style: 'currency',
15
+ currency: currencyCode,
16
+ }).resolvedOptions().maximumFractionDigits ?? 2;
17
+ } catch {
18
+ return 2;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Formats `value` as a localised currency string including the symbol/code.
24
+ * Uses Intl.NumberFormat so minor-unit digit count is correct per ISO-4217
25
+ * (JPY → 0 decimals, USD → 2, BHD → 3, etc.).
26
+ * Falls back to a plain number string if `currencyCode` is invalid.
27
+ */
28
+ export function formatCurrencyAmount(
29
+ value: number,
30
+ currencyCode: string,
31
+ locale = 'en-US',
32
+ ): string {
33
+ try {
34
+ return new Intl.NumberFormat(locale, {
35
+ style: 'currency',
36
+ currency: currencyCode,
37
+ }).format(value);
38
+ } catch {
39
+ return new Intl.NumberFormat(locale).format(value);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Formats `value` with the correct decimal precision for `currencyCode` but
45
+ * WITHOUT a currency symbol — for callers that render their own prefix/suffix.
46
+ * Falls back to 2 decimal places if `currencyCode` is invalid.
47
+ */
48
+ export function formatCurrencyNumber(
49
+ value: number,
50
+ currencyCode: string,
51
+ locale = 'en-US',
52
+ ): string {
53
+ const digits = fractionDigits(currencyCode, locale);
54
+ return new Intl.NumberFormat(locale, {
55
+ style: 'decimal',
56
+ minimumFractionDigits: digits,
57
+ maximumFractionDigits: digits,
58
+ }).format(value);
59
+ }
package/ui/index.ts CHANGED
@@ -48,3 +48,6 @@ export { default as StatusMark } from './components/StatusMark';
48
48
  // The illustrative SERIES_DATA blob stays internal (demo-only, not public API).
49
49
  export { getSeriesData } from './data/series';
50
50
  export type { RangeEntry, RelatedEntry, ReadingEntry, SeriesData } from './data/series';
51
+
52
+ // Formatting helpers
53
+ export { formatCurrencyAmount, formatCurrencyNumber } from './format';