@asteby/metacore-runtime-react 17.0.1 → 17.0.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 17.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - b5c8f5f: Pro datetime columns: `datetime`/`timestamp`/`timestamptz` columns now use the
8
+ date cell renderer instead of falling through to the raw-ISO fallback. Datetime
9
+ variants show day + time with a full-precision tooltip on hover (the 7Leguas
10
+ pattern); plain `date` columns stay day-only. Null and the Go zero-time render
11
+ an em-dash. Date-typed columns (including the timestamp variants) now infer the
12
+ `date_range` filter. Adds a pure `formatDateCell` helper (+ tests).
13
+ - Updated dependencies [b5c8f5f]
14
+ - @asteby/metacore-ui@2.4.2
15
+
16
+ ## 17.0.2
17
+
18
+ ### Patch Changes
19
+
20
+ - 3e45754: Humanize unmatched enum/status/option tokens as a scalable fallback. When a
21
+ column value has no matching declared `option`, dynamic cells (table, record
22
+ detail, relation rows) now render a humanized label (`in_progress` → "In
23
+ Progress", `pos` → "POS") instead of the raw token. A matched `option.label`
24
+ (the addon-localized source of truth) still wins; this only affects the
25
+ previously-raw fallback.
26
+
3
27
  ## 17.0.1
4
28
 
5
29
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAyF1C,MAAM,WAAW,wBAAwB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IAClF;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACpG;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB;AAwDD,wBAAgB,mBAAmB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,MAAM,GACT,EAAE,wBAAwB,+BAsP1B"}
1
+ {"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA0F1C,MAAM,WAAW,wBAAwB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IAClF;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACpG;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB;AA0DD,wBAAgB,mBAAmB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,MAAM,GACT,EAAE,wBAAwB,+BAsP1B"}
@@ -15,6 +15,7 @@ import { useApi } from '../api-context';
15
15
  import { DynamicSelectField } from '../dynamic-select-field';
16
16
  import { getFieldRef } from '../dynamic-form-schema';
17
17
  import { normalizeNilUuid } from '../nil-uuid';
18
+ import { humanizeToken } from '../dynamic-columns-helpers';
18
19
  function resolvePath(obj, path) {
19
20
  return path.split('.').reduce((acc, part) => acc?.[part], obj);
20
21
  }
@@ -37,7 +38,9 @@ function formatDisplayValue(rawValue, field) {
37
38
  }
38
39
  if (field.type === 'select' && field.options?.length) {
39
40
  const match = field.options.find(o => o.value === String(value));
40
- return match?.label ?? String(value);
41
+ // Matched option label wins (localized); humanize the raw token only
42
+ // when no declared option matches the value.
43
+ return match?.label ?? humanizeToken(value);
41
44
  }
42
45
  return String(value);
43
46
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Scalable safety net for enum/status/option values that have **no matching
3
+ * declared option**. Turns a raw machine token into a readable label so a cell
4
+ * never leaks `in_progress` / `out-of-stock` verbatim:
5
+ *
6
+ * - `in_progress` → `In Progress` (snake_case)
7
+ * - `out-of-stock` → `Out Of Stock` (kebab-case)
8
+ * - `payment.failed` → `Payment Failed` (dotted)
9
+ * - `sale` → `Sale` (single word)
10
+ * - `pos` → `POS`, `sku_count` → `SKU Count` (acronyms uppercased)
11
+ *
12
+ * This is a FALLBACK only. The localized source of truth is the `options` an
13
+ * addon declares; callers must prefer a matched `option.label` and only reach
14
+ * for `humanizeToken` when nothing matched.
15
+ *
16
+ * Values that don't look like an enum token are returned unchanged: anything
17
+ * already containing whitespace (free text), overly long strings, UUIDs, and
18
+ * non-strings. This keeps it safe to call on arbitrary cell values.
19
+ */
20
+ export declare function humanizeToken(value: unknown): string;
21
+ //# sourceMappingURL=dynamic-columns-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-columns-helpers.d.ts","sourceRoot":"","sources":["../src/dynamic-columns-helpers.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiBpD"}
@@ -0,0 +1,59 @@
1
+ // Pure, DOM-free helpers for the dynamic-column / record / relation renderers.
2
+ // Kept in their own module so they unit-test in node (no React, no
3
+ // metacore-ui primitives) and the same logic is shared across every cell
4
+ // renderer.
5
+ // Short, case-insensitive list of tokens that read better fully capitalized
6
+ // than Title-Cased. Intentionally tiny: this is a last-resort fallback, not a
7
+ // dictionary. Addons localize real labels via the column `options`.
8
+ const ACRONYMS = {
9
+ pos: 'POS',
10
+ sku: 'SKU',
11
+ id: 'ID',
12
+ url: 'URL',
13
+ api: 'API',
14
+ iva: 'IVA',
15
+ rfc: 'RFC',
16
+ };
17
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18
+ /**
19
+ * Scalable safety net for enum/status/option values that have **no matching
20
+ * declared option**. Turns a raw machine token into a readable label so a cell
21
+ * never leaks `in_progress` / `out-of-stock` verbatim:
22
+ *
23
+ * - `in_progress` → `In Progress` (snake_case)
24
+ * - `out-of-stock` → `Out Of Stock` (kebab-case)
25
+ * - `payment.failed` → `Payment Failed` (dotted)
26
+ * - `sale` → `Sale` (single word)
27
+ * - `pos` → `POS`, `sku_count` → `SKU Count` (acronyms uppercased)
28
+ *
29
+ * This is a FALLBACK only. The localized source of truth is the `options` an
30
+ * addon declares; callers must prefer a matched `option.label` and only reach
31
+ * for `humanizeToken` when nothing matched.
32
+ *
33
+ * Values that don't look like an enum token are returned unchanged: anything
34
+ * already containing whitespace (free text), overly long strings, UUIDs, and
35
+ * non-strings. This keeps it safe to call on arbitrary cell values.
36
+ */
37
+ export function humanizeToken(value) {
38
+ if (typeof value !== 'string')
39
+ return value == null ? '' : String(value);
40
+ const raw = value;
41
+ const trimmed = raw.trim();
42
+ // Already human (has spaces), empty, or free-form long text → leave as-is.
43
+ if (trimmed === '' || /\s/.test(trimmed) || trimmed.length > 40)
44
+ return raw;
45
+ // UUIDs (unresolved FKs) contain dashes but are not enum tokens.
46
+ if (UUID_RE.test(trimmed))
47
+ return raw;
48
+ const parts = trimmed.split(/[_.\-]+/).filter(Boolean);
49
+ if (parts.length === 0)
50
+ return raw;
51
+ return parts
52
+ .map((part) => {
53
+ const lower = part.toLowerCase();
54
+ if (ACRONYMS[lower])
55
+ return ACRONYMS[lower];
56
+ return lower.charAt(0).toUpperCase() + lower.slice(1);
57
+ })
58
+ .join(' ');
59
+ }
@@ -1,3 +1,4 @@
1
+ import { type Locale } from 'date-fns';
1
2
  import type { ColumnDefinition } from './types';
2
3
  import type { GetDynamicColumns } from './dynamic-columns-shim';
3
4
  /** Host-supplied helpers consumed by avatar/image cell renderers. */
@@ -37,6 +38,20 @@ export declare const isActionAllowedForRowState: (action: any, row: any) => bool
37
38
  * cell can read `row[relationKeyFor(col)]`.
38
39
  */
39
40
  export declare const relationKeyFor: (col: Pick<ColumnDefinition, "key">) => string;
41
+ /** Cell renderers (`cellStyle`/`type`) that resolve to the date renderer. */
42
+ export declare const DATE_CELL_TYPES: readonly ["date", "datetime", "timestamp", "timestamptz"];
43
+ /**
44
+ * Pure formatter behind the date/datetime cell. Returns the display string and
45
+ * an optional full-precision `title` (tooltip), or `null` when the value is
46
+ * empty/invalid/the Go zero-time so the cell renders an em-dash.
47
+ * - `date`: day only (`PPP`), no tooltip.
48
+ * - `datetime`/`timestamp(tz)`: day + time (`Pp`) with a full-precision
49
+ * tooltip (`PPpp`) — the 7Leguas pattern.
50
+ */
51
+ export declare function formatDateCell(value: unknown, renderAs: string | undefined, locale: Locale): {
52
+ display: string;
53
+ title?: string;
54
+ } | null;
40
55
  /**
41
56
  * Reads the resolved relation/option label a backend serves for an FK or
42
57
  * option column, falling back to the raw value. Pure so the cell renderers and
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAgGD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AAwHD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAiED;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CAmmBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
1
+ {"version":3,"file":"dynamic-columns.d.ts","sourceRoot":"","sources":["../src/dynamic-columns.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAgC9C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAER,iBAAiB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAgGD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,GAAG,EAAE,KAAK,GAAG,KAAG,OAMlE,CAAA;AA0HD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAG,MAGnE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2DAA4D,CAAA;AAExF;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GACf;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAY5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,gBAAgB,EAAE,KAAK,GAAG,KAAG,MAWtE,CAAA;AAiED;;;;GAIG;AACH,wBAAgB,4BAA4B,CACxC,OAAO,GAAE,qBAA0B,GACpC,iBAAiB,CAsmBnB;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBACL,CAAA"}
@@ -21,6 +21,7 @@ import { Avatar, AvatarFallback, AvatarImage, Badge, Button, Checkbox, DropdownM
21
21
  import { DataTableColumnHeader, FilterableColumnHeader, } from '@asteby/metacore-ui/data-table';
22
22
  import { generateBadgeStyles, getInitials, optionColor, relationChipStyles, } from '@asteby/metacore-ui/lib';
23
23
  import { Progress } from './dialogs/_primitives';
24
+ import { humanizeToken } from './dynamic-columns-helpers';
24
25
  import { OptionsContext } from './options-context';
25
26
  import { DynamicIcon } from './dynamic-icon';
26
27
  import { isNilUuid, normalizeNilUuid } from './nil-uuid';
@@ -184,7 +185,9 @@ const BadgeWithEndpointOptions = ({ endpoint, value }) => {
184
185
  const option = options.find((opt) => opt.value === value);
185
186
  if (option)
186
187
  return _jsx(OptionBadge, { option: option, fallback: String(value) });
187
- return _jsx(Badge, { variant: "outline", children: String(value) });
188
+ // No declared option matched humanize the raw token as a safety net so a
189
+ // cell never shows `in_progress` verbatim (option.label still wins above).
190
+ return _jsx(Badge, { variant: "outline", children: humanizeToken(value) });
188
191
  };
189
192
  /**
190
193
  * Resolves the relation sibling object a backend serves alongside an FK column.
@@ -198,6 +201,31 @@ export const relationKeyFor = (col) => {
198
201
  const k = col.key;
199
202
  return k.endsWith('_id') ? k.slice(0, -3) : k;
200
203
  };
204
+ /** Cell renderers (`cellStyle`/`type`) that resolve to the date renderer. */
205
+ export const DATE_CELL_TYPES = ['date', 'datetime', 'timestamp', 'timestamptz'];
206
+ /**
207
+ * Pure formatter behind the date/datetime cell. Returns the display string and
208
+ * an optional full-precision `title` (tooltip), or `null` when the value is
209
+ * empty/invalid/the Go zero-time so the cell renders an em-dash.
210
+ * - `date`: day only (`PPP`), no tooltip.
211
+ * - `datetime`/`timestamp(tz)`: day + time (`Pp`) with a full-precision
212
+ * tooltip (`PPpp`) — the 7Leguas pattern.
213
+ */
214
+ export function formatDateCell(value, renderAs, locale) {
215
+ if (value === null || value === undefined || value === '')
216
+ return null;
217
+ const date = new Date(value);
218
+ if (isNaN(date.getTime()) || date.getFullYear() <= 1)
219
+ return null;
220
+ const withTime = renderAs !== 'date';
221
+ if (withTime) {
222
+ return {
223
+ display: format(date, 'Pp', { locale }),
224
+ title: format(date, 'PPpp', { locale }),
225
+ };
226
+ }
227
+ return { display: format(date, 'PPP', { locale }) };
228
+ }
201
229
  /**
202
230
  * Reads the resolved relation/option label a backend serves for an FK or
203
231
  * option column, falling back to the raw value. Pure so the cell renderers and
@@ -315,16 +343,17 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
315
343
  const option = col.options.find((o) => o.value === String(value));
316
344
  if (option)
317
345
  return _jsx(OptionBadge, { option: option, fallback: String(value) });
318
- return _jsx(Badge, { variant: "outline", children: String(value) });
346
+ return _jsx(Badge, { variant: "outline", children: humanizeToken(value) });
319
347
  }
320
348
  if (renderAs === 'relation-badge-list') {
321
349
  return renderRelationBadges(value, col);
322
350
  }
323
- // Generic badge (no options/endpoint) — still pill it.
351
+ // Generic badge (no options/endpoint) — still pill it, and
352
+ // humanize raw enum tokens (no option exists to localize it).
324
353
  if (renderAs === 'badge') {
325
354
  if (!value && value !== 0)
326
355
  return _jsx(EmptyCell, {});
327
- return _jsx(Badge, { variant: "outline", children: String(value) });
356
+ return _jsx(Badge, { variant: "outline", children: humanizeToken(value) });
328
357
  }
329
358
  // Status — semantic color by value, options color wins.
330
359
  if (renderAs === 'status') {
@@ -337,7 +366,9 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
337
366
  const isDark = typeof document !== 'undefined' &&
338
367
  document.documentElement.classList.contains('dark');
339
368
  const styles = generateBadgeStyles(statusColorFor(sv), { isDark });
340
- return (_jsx(Badge, { variant: "outline", className: "border-0 capitalize", style: styles, children: sv }));
369
+ // No declared option humanize the status token so
370
+ // `in_progress` reads as "In Progress" instead of raw.
371
+ return (_jsx(Badge, { variant: "outline", className: "border-0", style: styles, children: humanizeToken(sv) }));
341
372
  }
342
373
  // Resolved FK relation chip. Triggers on an explicit
343
374
  // `cellStyle: 'relation'` or on any column carrying a `ref`
@@ -361,22 +392,17 @@ export function makeDefaultGetDynamicColumns(helpers = {}) {
361
392
  const option = col.options.find((o) => o.value === String(value));
362
393
  if (option)
363
394
  return _jsx(OptionBadge, { option: option, fallback: String(value) });
364
- return _jsx(Badge, { variant: "outline", children: String(value) });
395
+ return _jsx(Badge, { variant: "outline", children: humanizeToken(value) });
365
396
  }
366
397
  switch (renderAs) {
367
- case 'date': {
368
- if (!value)
398
+ case 'date':
399
+ case 'datetime':
400
+ case 'timestamp':
401
+ case 'timestamptz': {
402
+ const formatted = formatDateCell(value, renderAs, dateLocale);
403
+ if (!formatted)
369
404
  return _jsx("span", { className: "text-muted-foreground", children: "-" });
370
- try {
371
- const date = new Date(value);
372
- if (isNaN(date.getTime()) || date.getFullYear() <= 1) {
373
- return _jsx("span", { className: "text-muted-foreground", children: "-" });
374
- }
375
- return (_jsxs("div", { className: "flex items-center gap-1.5 text-muted-foreground", children: [_jsx(icons.Calendar, { className: "h-3.5 w-3.5 opacity-70" }), _jsx("span", { className: "text-sm font-medium", children: format(date, 'PPP', { locale: dateLocale }) })] }));
376
- }
377
- catch {
378
- return _jsx("span", { children: String(value) });
379
- }
405
+ return (_jsxs("div", { className: "flex items-center gap-1.5 text-muted-foreground", title: formatted.title, children: [_jsx(icons.Calendar, { className: "h-3.5 w-3.5 opacity-70" }), _jsx("span", { className: "text-sm font-medium", children: formatted.display })] }));
380
406
  }
381
407
  case 'search':
382
408
  case 'avatar':
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-relation-helpers.d.ts","sourceRoot":"","sources":["../src/dynamic-relation-helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-relation-helpers.d.ts","sourceRoot":"","sources":["../src/dynamic-relation-helpers.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,15 @@
1
1
  import { isNilUuid } from './nil-uuid';
2
+ import { humanizeToken } from './dynamic-columns-helpers';
3
+ // An enum-like column renders a single token value (status/select/option/badge)
4
+ // rather than free text, so an unmatched value should be humanized, not leaked.
5
+ function isEnumLikeColumn(col) {
6
+ const renderAs = col.cellStyle ?? col.type;
7
+ return (renderAs === 'select' ||
8
+ renderAs === 'option' ||
9
+ renderAs === 'status' ||
10
+ renderAs === 'badge' ||
11
+ !!col.options?.length);
12
+ }
2
13
  // Pulls a human label off a resolved relation/user object a backend serves:
3
14
  // `{ value, label }` (FK sibling), `{ name, … }` (user object such as
4
15
  // created_by) or `{ title }`. Returns undefined for plain/empty objects so the
@@ -43,7 +54,18 @@ export function formatRelationCell(row, col) {
43
54
  if (typeof value === 'object')
44
55
  return '—';
45
56
  const text = String(value);
46
- return text === '' ? '—' : text;
57
+ if (text === '')
58
+ return '—';
59
+ // Enum/status/option columns: prefer the declared option label (localized
60
+ // source of truth), then humanize the raw token as a fallback so a status
61
+ // never reads as `in_progress`. Plain text columns are left untouched.
62
+ if (isEnumLikeColumn(col)) {
63
+ const match = col.options?.find((o) => String(o.value) === text);
64
+ if (match)
65
+ return match.label;
66
+ return humanizeToken(text);
67
+ }
68
+ return text;
47
69
  }
48
70
  /**
49
71
  * Builds the query params used by `<DynamicRelation kind="one_to_many">` to
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAUnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,+BA+xBnB"}
1
+ {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAUnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,+BAgyBnB"}
@@ -24,7 +24,7 @@ import { toast } from 'sonner';
24
24
  import { Progress } from './dialogs/_primitives';
25
25
  import { useMetadataCache } from './metadata-cache';
26
26
  import { useApi, useCurrentBranch } from './api-context';
27
- import { defaultGetDynamicColumns } from './dynamic-columns';
27
+ import { defaultGetDynamicColumns, DATE_CELL_TYPES } from './dynamic-columns';
28
28
  import { OptionsContext } from './options-context';
29
29
  import { ActionModalDispatcher } from './action-modal-dispatcher';
30
30
  import { getSearchableColumnKeys } from './column-visibility';
@@ -520,7 +520,7 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
520
520
  filterType = 'boolean';
521
521
  else if (c.type === 'number')
522
522
  filterType = 'number_range';
523
- else if (c.type === 'date')
523
+ else if (DATE_CELL_TYPES.includes(c.type))
524
524
  filterType = 'date_range';
525
525
  else
526
526
  filterType = 'text';
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederation
17
17
  export * from './dynamic-icon';
18
18
  export type { ColumnFilterConfig, FilterOption as DynamicColumnFilterOption, GetDynamicColumns, DynamicIconComponent, } from './dynamic-columns-shim';
19
19
  export { defaultGetDynamicColumns, makeDefaultGetDynamicColumns, relationKeyFor, resolveRelationLabel, type DynamicColumnsHelpers, } from './dynamic-columns';
20
+ export { humanizeToken } from './dynamic-columns-helpers';
20
21
  export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid';
21
22
  export { DynamicRecordDialog } from './dialogs/dynamic-record';
22
23
  export { CreateRecordDialog } from './dialogs/create-record-dialog';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ export { ADDON_MANIFEST_CHANGED_TYPE, wireHotSwapInvalidation, useManifestHotSwa
21
21
  export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederationContainer, shortenHash, } from './hotswap-reload-policy';
22
22
  export * from './dynamic-icon';
23
23
  export { defaultGetDynamicColumns, makeDefaultGetDynamicColumns, relationKeyFor, resolveRelationLabel, } from './dynamic-columns';
24
+ export { humanizeToken } from './dynamic-columns-helpers';
24
25
  export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid';
25
26
  export { DynamicRecordDialog } from './dialogs/dynamic-record';
26
27
  export { CreateRecordDialog } from './dialogs/create-record-dialog';
package/dist/types.d.ts CHANGED
@@ -81,7 +81,7 @@ export type ColumnVisibility = 'all' | 'table' | 'modal' | 'list' | (string & {}
81
81
  export interface ColumnDefinition {
82
82
  key: string;
83
83
  label: string;
84
- type: 'text' | 'number' | 'date' | 'select' | 'search' | 'relation-badge-list' | 'avatar' | 'boolean' | 'phone' | 'media-gallery' | 'image' | 'url' | 'link' | 'email' | 'currency' | 'percent' | 'progress' | 'badge' | 'status' | 'tags' | 'color' | 'code' | 'truncate-text' | 'creator' | 'user' | 'relation';
84
+ type: 'text' | 'number' | 'date' | 'datetime' | 'timestamp' | 'timestamptz' | 'select' | 'search' | 'relation-badge-list' | 'avatar' | 'boolean' | 'phone' | 'media-gallery' | 'image' | 'url' | 'link' | 'email' | 'currency' | 'percent' | 'progress' | 'badge' | 'status' | 'tags' | 'color' | 'code' | 'truncate-text' | 'creator' | 'user' | 'relation';
85
85
  sortable: boolean;
86
86
  filterable: boolean;
87
87
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "17.0.1",
3
+ "version": "17.0.3",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,8 +33,8 @@
33
33
  "lucide-react": ">=0.460",
34
34
  "date-fns": ">=3",
35
35
  "react-day-picker": ">=8",
36
- "@asteby/metacore-ui": "^2.4.1",
37
- "@asteby/metacore-sdk": "^3.2.0"
36
+ "@asteby/metacore-sdk": "^3.2.0",
37
+ "@asteby/metacore-ui": "^2.4.2"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@tanstack/react-router": {
@@ -62,7 +62,7 @@
62
62
  "vitest": "^4.0.0",
63
63
  "zustand": "^5.0.0",
64
64
  "@asteby/metacore-sdk": "3.2.0",
65
- "@asteby/metacore-ui": "2.4.1"
65
+ "@asteby/metacore-ui": "2.4.2"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsc -p tsconfig.json",
@@ -0,0 +1,52 @@
1
+ // Pure-logic coverage for the date/datetime cell formatter behind
2
+ // `defaultGetDynamicColumns`. Locks the contract the JSX cell relies on:
3
+ // `date` → day only (no tooltip), `datetime`/`timestamp(tz)` → day + time with
4
+ // a full-precision tooltip, and null/invalid/Go-zero-time → `null` (em-dash).
5
+ import { describe, it, expect } from 'vitest'
6
+ import { enUS } from 'date-fns/locale'
7
+ import { formatDateCell } from '../dynamic-columns'
8
+
9
+ describe('formatDateCell', () => {
10
+ const iso = '2026-06-06T17:47:33.201Z'
11
+
12
+ it('renders day only with no tooltip for `date`', () => {
13
+ const out = formatDateCell(iso, 'date', enUS)
14
+ expect(out).not.toBeNull()
15
+ expect(out!.title).toBeUndefined()
16
+ // `PPP` is the long day form — no time component.
17
+ expect(out!.display).not.toMatch(/\d{1,2}:\d{2}/)
18
+ expect(out!.display).toMatch(/2026/)
19
+ })
20
+
21
+ it('renders day + time and a full-precision tooltip for `datetime`', () => {
22
+ const out = formatDateCell(iso, 'datetime', enUS)
23
+ expect(out).not.toBeNull()
24
+ expect(out!.display).toMatch(/\d{1,2}:\d{2}/)
25
+ expect(out!.title).toBeDefined()
26
+ expect(out!.title).toMatch(/\d{1,2}:\d{2}/)
27
+ })
28
+
29
+ it('treats timestamp/timestamptz like datetime (day + time + tooltip)', () => {
30
+ for (const t of ['timestamp', 'timestamptz']) {
31
+ const out = formatDateCell(iso, t, enUS)
32
+ expect(out).not.toBeNull()
33
+ expect(out!.display).toMatch(/\d{1,2}:\d{2}/)
34
+ expect(out!.title).toBeDefined()
35
+ }
36
+ })
37
+
38
+ it('returns null for null/undefined/empty (empty cell)', () => {
39
+ expect(formatDateCell(null, 'datetime', enUS)).toBeNull()
40
+ expect(formatDateCell(undefined, 'datetime', enUS)).toBeNull()
41
+ expect(formatDateCell('', 'datetime', enUS)).toBeNull()
42
+ })
43
+
44
+ it('returns null for the Go zero-time (0001-01-01T00:00:00Z)', () => {
45
+ expect(formatDateCell('0001-01-01T00:00:00Z', 'datetime', enUS)).toBeNull()
46
+ expect(formatDateCell('0001-01-01T00:00:00Z', 'date', enUS)).toBeNull()
47
+ })
48
+
49
+ it('returns null for an unparseable value', () => {
50
+ expect(formatDateCell('not-a-date', 'datetime', enUS)).toBeNull()
51
+ })
52
+ })
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { humanizeToken } from '../dynamic-columns-helpers'
3
+
4
+ describe('humanizeToken', () => {
5
+ it('title-cases snake_case tokens', () => {
6
+ expect(humanizeToken('in_progress')).toBe('In Progress')
7
+ expect(humanizeToken('out_of_stock')).toBe('Out Of Stock')
8
+ })
9
+
10
+ it('title-cases kebab-case tokens', () => {
11
+ expect(humanizeToken('out-of-stock')).toBe('Out Of Stock')
12
+ expect(humanizeToken('draft-pending')).toBe('Draft Pending')
13
+ })
14
+
15
+ it('handles dotted tokens', () => {
16
+ expect(humanizeToken('payment.failed')).toBe('Payment Failed')
17
+ })
18
+
19
+ it('title-cases a single word', () => {
20
+ expect(humanizeToken('sale')).toBe('Sale')
21
+ expect(humanizeToken('completed')).toBe('Completed')
22
+ })
23
+
24
+ it('uppercases known acronyms (case-insensitive)', () => {
25
+ expect(humanizeToken('pos')).toBe('POS')
26
+ expect(humanizeToken('SKU')).toBe('SKU')
27
+ expect(humanizeToken('sku_count')).toBe('SKU Count')
28
+ expect(humanizeToken('api_url')).toBe('API URL')
29
+ expect(humanizeToken('rfc')).toBe('RFC')
30
+ expect(humanizeToken('id')).toBe('ID')
31
+ })
32
+
33
+ it('leaves already-humanized text untouched', () => {
34
+ expect(humanizeToken('In Progress')).toBe('In Progress')
35
+ expect(humanizeToken('Some free form note')).toBe('Some free form note')
36
+ })
37
+
38
+ it('leaves long free text untouched', () => {
39
+ const long = 'this is a fairly long sentence describing the order status today'
40
+ expect(humanizeToken(long)).toBe(long)
41
+ })
42
+
43
+ it('leaves UUIDs untouched', () => {
44
+ const uuid = '550e8400-e29b-41d4-a716-446655440000'
45
+ expect(humanizeToken(uuid)).toBe(uuid)
46
+ })
47
+
48
+ it('coerces non-string values without transforming them', () => {
49
+ expect(humanizeToken(42 as unknown as string)).toBe('42')
50
+ expect(humanizeToken(null)).toBe('')
51
+ expect(humanizeToken(undefined)).toBe('')
52
+ })
53
+
54
+ it('returns empty/whitespace input unchanged', () => {
55
+ expect(humanizeToken('')).toBe('')
56
+ expect(humanizeToken(' ')).toBe(' ')
57
+ })
58
+ })
@@ -43,6 +43,7 @@ import { useApi } from '../api-context'
43
43
  import { DynamicSelectField } from '../dynamic-select-field'
44
44
  import { getFieldRef } from '../dynamic-form-schema'
45
45
  import { normalizeNilUuid } from '../nil-uuid'
46
+ import { humanizeToken } from '../dynamic-columns-helpers'
46
47
  import type { ActionFieldDef } from '../types'
47
48
 
48
49
  interface FieldOption {
@@ -156,7 +157,9 @@ function formatDisplayValue(rawValue: any, field: FieldDef): string {
156
157
 
157
158
  if (field.type === 'select' && field.options?.length) {
158
159
  const match = field.options.find(o => o.value === String(value))
159
- return match?.label ?? String(value)
160
+ // Matched option label wins (localized); humanize the raw token only
161
+ // when no declared option matches the value.
162
+ return match?.label ?? humanizeToken(value)
160
163
  }
161
164
 
162
165
  return String(value)
@@ -0,0 +1,58 @@
1
+ // Pure, DOM-free helpers for the dynamic-column / record / relation renderers.
2
+ // Kept in their own module so they unit-test in node (no React, no
3
+ // metacore-ui primitives) and the same logic is shared across every cell
4
+ // renderer.
5
+
6
+ // Short, case-insensitive list of tokens that read better fully capitalized
7
+ // than Title-Cased. Intentionally tiny: this is a last-resort fallback, not a
8
+ // dictionary. Addons localize real labels via the column `options`.
9
+ const ACRONYMS: Record<string, string> = {
10
+ pos: 'POS',
11
+ sku: 'SKU',
12
+ id: 'ID',
13
+ url: 'URL',
14
+ api: 'API',
15
+ iva: 'IVA',
16
+ rfc: 'RFC',
17
+ }
18
+
19
+ const UUID_RE =
20
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
21
+
22
+ /**
23
+ * Scalable safety net for enum/status/option values that have **no matching
24
+ * declared option**. Turns a raw machine token into a readable label so a cell
25
+ * never leaks `in_progress` / `out-of-stock` verbatim:
26
+ *
27
+ * - `in_progress` → `In Progress` (snake_case)
28
+ * - `out-of-stock` → `Out Of Stock` (kebab-case)
29
+ * - `payment.failed` → `Payment Failed` (dotted)
30
+ * - `sale` → `Sale` (single word)
31
+ * - `pos` → `POS`, `sku_count` → `SKU Count` (acronyms uppercased)
32
+ *
33
+ * This is a FALLBACK only. The localized source of truth is the `options` an
34
+ * addon declares; callers must prefer a matched `option.label` and only reach
35
+ * for `humanizeToken` when nothing matched.
36
+ *
37
+ * Values that don't look like an enum token are returned unchanged: anything
38
+ * already containing whitespace (free text), overly long strings, UUIDs, and
39
+ * non-strings. This keeps it safe to call on arbitrary cell values.
40
+ */
41
+ export function humanizeToken(value: unknown): string {
42
+ if (typeof value !== 'string') return value == null ? '' : String(value)
43
+ const raw = value
44
+ const trimmed = raw.trim()
45
+ // Already human (has spaces), empty, or free-form long text → leave as-is.
46
+ if (trimmed === '' || /\s/.test(trimmed) || trimmed.length > 40) return raw
47
+ // UUIDs (unresolved FKs) contain dashes but are not enum tokens.
48
+ if (UUID_RE.test(trimmed)) return raw
49
+ const parts = trimmed.split(/[_.\-]+/).filter(Boolean)
50
+ if (parts.length === 0) return raw
51
+ return parts
52
+ .map((part) => {
53
+ const lower = part.toLowerCase()
54
+ if (ACRONYMS[lower]) return ACRONYMS[lower]
55
+ return lower.charAt(0).toUpperCase() + lower.slice(1)
56
+ })
57
+ .join(' ')
58
+ }
@@ -14,7 +14,7 @@
14
14
 
15
15
  import * as React from 'react'
16
16
  import { ColumnDef } from '@tanstack/react-table'
17
- import { format } from 'date-fns'
17
+ import { format, type Locale } from 'date-fns'
18
18
  import { es, enUS } from 'date-fns/locale'
19
19
  import * as icons from 'lucide-react'
20
20
  import { MoreHorizontal } from 'lucide-react'
@@ -42,6 +42,7 @@ import {
42
42
  relationChipStyles,
43
43
  } from '@asteby/metacore-ui/lib'
44
44
  import { Progress } from './dialogs/_primitives'
45
+ import { humanizeToken } from './dynamic-columns-helpers'
45
46
  import { OptionsContext } from './options-context'
46
47
  import { DynamicIcon } from './dynamic-icon'
47
48
  import { isNilUuid, normalizeNilUuid } from './nil-uuid'
@@ -297,7 +298,9 @@ const BadgeWithEndpointOptions: React.FC<{ endpoint: string; value: any }> = ({
297
298
  const options = optionsMap.get(endpoint) || []
298
299
  const option = options.find((opt: any) => opt.value === value)
299
300
  if (option) return <OptionBadge option={option} fallback={String(value)} />
300
- return <Badge variant="outline">{String(value)}</Badge>
301
+ // No declared option matched → humanize the raw token as a safety net so a
302
+ // cell never shows `in_progress` verbatim (option.label still wins above).
303
+ return <Badge variant="outline">{humanizeToken(value)}</Badge>
301
304
  }
302
305
 
303
306
  /**
@@ -313,6 +316,35 @@ export const relationKeyFor = (col: Pick<ColumnDefinition, 'key'>): string => {
313
316
  return k.endsWith('_id') ? k.slice(0, -3) : k
314
317
  }
315
318
 
319
+ /** Cell renderers (`cellStyle`/`type`) that resolve to the date renderer. */
320
+ export const DATE_CELL_TYPES = ['date', 'datetime', 'timestamp', 'timestamptz'] as const
321
+
322
+ /**
323
+ * Pure formatter behind the date/datetime cell. Returns the display string and
324
+ * an optional full-precision `title` (tooltip), or `null` when the value is
325
+ * empty/invalid/the Go zero-time so the cell renders an em-dash.
326
+ * - `date`: day only (`PPP`), no tooltip.
327
+ * - `datetime`/`timestamp(tz)`: day + time (`Pp`) with a full-precision
328
+ * tooltip (`PPpp`) — the 7Leguas pattern.
329
+ */
330
+ export function formatDateCell(
331
+ value: unknown,
332
+ renderAs: string | undefined,
333
+ locale: Locale,
334
+ ): { display: string; title?: string } | null {
335
+ if (value === null || value === undefined || value === '') return null
336
+ const date = new Date(value as any)
337
+ if (isNaN(date.getTime()) || date.getFullYear() <= 1) return null
338
+ const withTime = renderAs !== 'date'
339
+ if (withTime) {
340
+ return {
341
+ display: format(date, 'Pp', { locale }),
342
+ title: format(date, 'PPpp', { locale }),
343
+ }
344
+ }
345
+ return { display: format(date, 'PPP', { locale }) }
346
+ }
347
+
316
348
  /**
317
349
  * Reads the resolved relation/option label a backend serves for an FK or
318
350
  * option column, falling back to the raw value. Pure so the cell renderers and
@@ -499,17 +531,18 @@ export function makeDefaultGetDynamicColumns(
499
531
  if (!value && value !== 0) return <span className="text-muted-foreground">-</span>
500
532
  const option = col.options.find((o) => o.value === String(value))
501
533
  if (option) return <OptionBadge option={option} fallback={String(value)} />
502
- return <Badge variant="outline">{String(value)}</Badge>
534
+ return <Badge variant="outline">{humanizeToken(value)}</Badge>
503
535
  }
504
536
 
505
537
  if (renderAs === 'relation-badge-list') {
506
538
  return renderRelationBadges(value, col)
507
539
  }
508
540
 
509
- // Generic badge (no options/endpoint) — still pill it.
541
+ // Generic badge (no options/endpoint) — still pill it, and
542
+ // humanize raw enum tokens (no option exists to localize it).
510
543
  if (renderAs === 'badge') {
511
544
  if (!value && value !== 0) return <EmptyCell />
512
- return <Badge variant="outline">{String(value)}</Badge>
545
+ return <Badge variant="outline">{humanizeToken(value)}</Badge>
513
546
  }
514
547
 
515
548
  // Status — semantic color by value, options color wins.
@@ -522,9 +555,11 @@ export function makeDefaultGetDynamicColumns(
522
555
  typeof document !== 'undefined' &&
523
556
  document.documentElement.classList.contains('dark')
524
557
  const styles = generateBadgeStyles(statusColorFor(sv), { isDark })
558
+ // No declared option → humanize the status token so
559
+ // `in_progress` reads as "In Progress" instead of raw.
525
560
  return (
526
- <Badge variant="outline" className="border-0 capitalize" style={styles}>
527
- {sv}
561
+ <Badge variant="outline" className="border-0" style={styles}>
562
+ {humanizeToken(sv)}
528
563
  </Badge>
529
564
  )
530
565
  }
@@ -554,28 +589,28 @@ export function makeDefaultGetDynamicColumns(
554
589
  if (!value && value !== 0) return <EmptyCell />
555
590
  const option = col.options.find((o) => o.value === String(value))
556
591
  if (option) return <OptionBadge option={option} fallback={String(value)} />
557
- return <Badge variant="outline">{String(value)}</Badge>
592
+ return <Badge variant="outline">{humanizeToken(value)}</Badge>
558
593
  }
559
594
 
560
595
  switch (renderAs) {
561
- case 'date': {
562
- if (!value) return <span className="text-muted-foreground">-</span>
563
- try {
564
- const date = new Date(value)
565
- if (isNaN(date.getTime()) || date.getFullYear() <= 1) {
566
- return <span className="text-muted-foreground">-</span>
567
- }
568
- return (
569
- <div className="flex items-center gap-1.5 text-muted-foreground">
570
- <icons.Calendar className="h-3.5 w-3.5 opacity-70" />
571
- <span className="text-sm font-medium">
572
- {format(date, 'PPP', { locale: dateLocale })}
573
- </span>
574
- </div>
575
- )
576
- } catch {
577
- return <span>{String(value)}</span>
578
- }
596
+ case 'date':
597
+ case 'datetime':
598
+ case 'timestamp':
599
+ case 'timestamptz': {
600
+ const formatted = formatDateCell(value, renderAs, dateLocale)
601
+ if (!formatted)
602
+ return <span className="text-muted-foreground">-</span>
603
+ return (
604
+ <div
605
+ className="flex items-center gap-1.5 text-muted-foreground"
606
+ title={formatted.title}
607
+ >
608
+ <icons.Calendar className="h-3.5 w-3.5 opacity-70" />
609
+ <span className="text-sm font-medium">
610
+ {formatted.display}
611
+ </span>
612
+ </div>
613
+ )
579
614
  }
580
615
 
581
616
  case 'search':
@@ -3,6 +3,20 @@
3
3
  // URL/payload conventions outside the component.
4
4
  import type { ActionFieldDef, ColumnDefinition, TableMetadata } from './types'
5
5
  import { isNilUuid } from './nil-uuid'
6
+ import { humanizeToken } from './dynamic-columns-helpers'
7
+
8
+ // An enum-like column renders a single token value (status/select/option/badge)
9
+ // rather than free text, so an unmatched value should be humanized, not leaked.
10
+ function isEnumLikeColumn(col: ColumnDefinition): boolean {
11
+ const renderAs = col.cellStyle ?? col.type
12
+ return (
13
+ renderAs === 'select' ||
14
+ renderAs === 'option' ||
15
+ renderAs === 'status' ||
16
+ renderAs === 'badge' ||
17
+ !!col.options?.length
18
+ )
19
+ }
6
20
 
7
21
  export type DynamicRelationKind = 'one_to_many' | 'many_to_many'
8
22
 
@@ -49,7 +63,16 @@ export function formatRelationCell(row: Record<string, unknown>, col: ColumnDefi
49
63
  if (typeof value === 'object') return '—'
50
64
 
51
65
  const text = String(value)
52
- return text === '' ? '—' : text
66
+ if (text === '') return '—'
67
+ // Enum/status/option columns: prefer the declared option label (localized
68
+ // source of truth), then humanize the raw token as a fallback so a status
69
+ // never reads as `in_progress`. Plain text columns are left untouched.
70
+ if (isEnumLikeColumn(col)) {
71
+ const match = col.options?.find((o) => String(o.value) === text)
72
+ if (match) return match.label
73
+ return humanizeToken(text)
74
+ }
75
+ return text
53
76
  }
54
77
 
55
78
  export interface PivotRowLike {
@@ -65,7 +65,7 @@ import { Progress } from './dialogs/_primitives'
65
65
  import { useMetadataCache } from './metadata-cache'
66
66
  import { useApi, useCurrentBranch } from './api-context'
67
67
  import type { ColumnFilterConfig, GetDynamicColumns } from './dynamic-columns-shim'
68
- import { defaultGetDynamicColumns } from './dynamic-columns'
68
+ import { defaultGetDynamicColumns, DATE_CELL_TYPES } from './dynamic-columns'
69
69
  import { OptionsContext } from './options-context'
70
70
  import { ActionModalDispatcher } from './action-modal-dispatcher'
71
71
  import type { TableMetadata, ApiResponse, ActionMetadata } from './types'
@@ -555,7 +555,8 @@ export function DynamicTable({
555
555
  else if (hasStaticOptions || hasEndpoint) filterType = 'select'
556
556
  else if (c.type === 'boolean') filterType = 'boolean'
557
557
  else if (c.type === 'number') filterType = 'number_range'
558
- else if (c.type === 'date') filterType = 'date_range'
558
+ else if ((DATE_CELL_TYPES as readonly string[]).includes(c.type))
559
+ filterType = 'date_range'
559
560
  else filterType = 'text'
560
561
 
561
562
  const options = hasStaticOptions
package/src/index.ts CHANGED
@@ -66,6 +66,7 @@ export {
66
66
  resolveRelationLabel,
67
67
  type DynamicColumnsHelpers,
68
68
  } from './dynamic-columns'
69
+ export { humanizeToken } from './dynamic-columns-helpers'
69
70
  export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid'
70
71
  export { DynamicRecordDialog } from './dialogs/dynamic-record'
71
72
  export { CreateRecordDialog } from './dialogs/create-record-dialog'
package/src/types.ts CHANGED
@@ -87,6 +87,11 @@ export interface ColumnDefinition {
87
87
  | 'text'
88
88
  | 'number'
89
89
  | 'date'
90
+ // Timestamp variants. They share the `date` cell renderer but append
91
+ // the time + a full-precision tooltip (see formatDateCell).
92
+ | 'datetime'
93
+ | 'timestamp'
94
+ | 'timestamptz'
90
95
  | 'select'
91
96
  | 'search'
92
97
  | 'relation-badge-list'