@open-mercato/ui 0.6.8-develop.6910.1.560e304de2 → 0.6.8-develop.6912.1.36161dc9ac

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/utils/customFieldColumns.ts"],
4
- "sourcesContent": ["import type { ColumnDef } from '@tanstack/react-table'\nimport type { FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { CustomFieldDefDto, CustomFieldVisibility } from './customFieldDefs'\nimport { isDefVisible } from './customFieldDefs'\n\ntype RawCustomFieldOption = string | number | { value?: unknown; label?: unknown }\n\nexport function supportsCustomFieldColumn(def: CustomFieldDefDto): boolean {\n return def.kind !== 'attachment'\n}\n\nexport function mapCustomFieldKindToFilterType(kind: string): FilterFieldType {\n switch (kind) {\n case 'boolean':\n return 'boolean'\n case 'integer':\n case 'float':\n return 'number'\n case 'date':\n case 'datetime':\n return 'date'\n case 'select':\n case 'dictionary':\n case 'currency':\n case 'relation':\n return 'select'\n default:\n return 'text'\n }\n}\n\nexport function normalizeCustomFieldFilterOptions(options?: RawCustomFieldOption[]): FilterOption[] {\n if (!Array.isArray(options)) return []\n return options.map((option) => {\n if (option && typeof option === 'object' && 'value' in option) {\n const rawValue = option.value\n const rawLabel = option.label ?? rawValue\n return {\n value: String(rawValue),\n label: typeof rawLabel === 'string' ? rawLabel : String(rawLabel),\n }\n }\n const value = String(option)\n return {\n value,\n label: value.charAt(0).toUpperCase() + value.slice(1),\n }\n })\n}\n\n// Filters and annotates columns with custom-field definitions:\n// - Drops cf_* columns when no definition exists or listVisible === false\n// - Uses definition label as header when header is missing\nexport function applyCustomFieldVisibility<T>(columns: ColumnDef<T, any>[], defs: CustomFieldDefDto[], mode: CustomFieldVisibility = 'list'): ColumnDef<T, any>[] {\n const byKey = new Map(defs.map((d) => [d.key, d]))\n // First, filter and annotate headers\n const filtered = columns.filter((c) => {\n const key = String((c as any).accessorKey || '')\n if (!key.startsWith('cf_')) return true\n const cfKey = key.slice(3)\n const def = byKey.get(cfKey)\n if (!def) return false\n if (!supportsCustomFieldColumn(def)) return false\n if (!isDefVisible(def, mode)) return false\n const currentHeader = (c as any).header\n const fallbackHeader = typeof currentHeader === 'string' && currentHeader.trim().length ? currentHeader : key\n const label = def.label && def.label.trim().length ? def.label : fallbackHeader\n if (currentHeader == null || typeof currentHeader === 'string') {\n (c as any).header = label\n }\n const existingMeta = ((c as any).meta || {}) as Record<string, unknown>\n const nextMeta = Object.assign({}, existingMeta, { label })\n ;(c as any).meta = nextMeta\n return true\n })\n\n // Then, reorder only the cf_* columns by definition priority while preserving\n // the positions of non-cf columns and the count of cf slots.\n const cfEntries: Array<{ col: ColumnDef<T, any>; key: string; prio: number }> = []\n filtered.forEach((c) => {\n const key = String((c as any).accessorKey || '')\n if (key.startsWith('cf_')) {\n const cfKey = key.slice(3)\n const def = byKey.get(cfKey)\n cfEntries.push({ col: c, key: cfKey, prio: def?.priority ?? 0 })\n }\n })\n cfEntries.sort((a, b) => a.prio - b.prio)\n let cfIdx = 0\n const result = filtered.map((c) => {\n const key = String((c as any).accessorKey || '')\n if (!key.startsWith('cf_')) return c\n const next = cfEntries[cfIdx++]?.col ?? c\n return next\n })\n\n // Append any missing cf columns (defs visible but not present in incoming columns)\n const existingCfKeys = new Set<string>(result\n .map((c) => String((c as any).accessorKey || ''))\n .filter((k) => k.startsWith('cf_'))\n .map((k) => k.slice(3)))\n\n const visibleSorted = defs\n .filter((d) => supportsCustomFieldColumn(d) && isDefVisible(d, mode))\n .sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))\n\n const missing = visibleSorted.filter((d) => !existingCfKeys.has(d.key))\n for (const d of missing) {\n const col: ColumnDef<T, any> = {\n accessorKey: `cf_${d.key}` as any,\n header: d.label || `cf_${d.key}`,\n // Respect responsive priority when provided; default leaves it visible\n meta: { priority: (d as any).priority, label: d.label || `cf_${d.key}` } as any,\n }\n result.push(col)\n }\n return result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { RowData } from '@tanstack/react-table'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { CustomFieldDefDto, CustomFieldVisibility } from './customFieldDefs'\nimport { isDefVisible } from './customFieldDefs'\n\ntype RawCustomFieldOption = string | number | { value?: unknown; label?: unknown }\n\nexport function supportsCustomFieldColumn(def: CustomFieldDefDto): boolean {\n return def.kind !== 'attachment'\n}\n\nexport function mapCustomFieldKindToFilterType(kind: string): FilterFieldType {\n switch (kind) {\n case 'boolean':\n return 'boolean'\n case 'integer':\n case 'float':\n return 'number'\n case 'date':\n case 'datetime':\n return 'date'\n case 'select':\n case 'dictionary':\n case 'currency':\n case 'relation':\n return 'select'\n default:\n return 'text'\n }\n}\n\nexport function normalizeCustomFieldFilterOptions(options?: RawCustomFieldOption[]): FilterOption[] {\n if (!Array.isArray(options)) return []\n return options.map((option) => {\n if (option && typeof option === 'object' && 'value' in option) {\n const rawValue = option.value\n const rawLabel = option.label ?? rawValue\n return {\n value: String(rawValue),\n label: typeof rawLabel === 'string' ? rawLabel : String(rawLabel),\n }\n }\n const value = String(option)\n return {\n value,\n label: value.charAt(0).toUpperCase() + value.slice(1),\n }\n })\n}\n\n// Filters and annotates columns with custom-field definitions:\n// - Drops cf_* columns when no definition exists or listVisible === false\n// - Uses definition label as header when header is missing\nexport function applyCustomFieldVisibility<T extends RowData = RowData>(columns: ColumnDef<T, any>[], defs: CustomFieldDefDto[], mode: CustomFieldVisibility = 'list'): ColumnDef<T, any>[] {\n const byKey = new Map(defs.map((d) => [d.key, d]))\n // First, filter and annotate headers\n const filtered = columns.filter((c) => {\n const key = String((c as any).accessorKey || '')\n if (!key.startsWith('cf_')) return true\n const cfKey = key.slice(3)\n const def = byKey.get(cfKey)\n if (!def) return false\n if (!supportsCustomFieldColumn(def)) return false\n if (!isDefVisible(def, mode)) return false\n const currentHeader = (c as any).header\n const fallbackHeader = typeof currentHeader === 'string' && currentHeader.trim().length ? currentHeader : key\n const label = def.label && def.label.trim().length ? def.label : fallbackHeader\n if (currentHeader == null || typeof currentHeader === 'string') {\n (c as any).header = label\n }\n const existingMeta = ((c as any).meta || {}) as Record<string, unknown>\n const nextMeta = Object.assign({}, existingMeta, { label })\n ;(c as any).meta = nextMeta\n return true\n })\n\n // Then, reorder only the cf_* columns by definition priority while preserving\n // the positions of non-cf columns and the count of cf slots.\n const cfEntries: Array<{ col: ColumnDef<T, any>; key: string; prio: number }> = []\n filtered.forEach((c) => {\n const key = String((c as any).accessorKey || '')\n if (key.startsWith('cf_')) {\n const cfKey = key.slice(3)\n const def = byKey.get(cfKey)\n cfEntries.push({ col: c, key: cfKey, prio: def?.priority ?? 0 })\n }\n })\n cfEntries.sort((a, b) => a.prio - b.prio)\n let cfIdx = 0\n const result = filtered.map((c) => {\n const key = String((c as any).accessorKey || '')\n if (!key.startsWith('cf_')) return c\n const next = cfEntries[cfIdx++]?.col ?? c\n return next\n })\n\n // Append any missing cf columns (defs visible but not present in incoming columns)\n const existingCfKeys = new Set<string>(result\n .map((c) => String((c as any).accessorKey || ''))\n .filter((k) => k.startsWith('cf_'))\n .map((k) => k.slice(3)))\n\n const visibleSorted = defs\n .filter((d) => supportsCustomFieldColumn(d) && isDefVisible(d, mode))\n .sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))\n\n const missing = visibleSorted.filter((d) => !existingCfKeys.has(d.key))\n for (const d of missing) {\n const col: ColumnDef<T, any> = {\n accessorKey: `cf_${d.key}` as any,\n header: d.label || `cf_${d.key}`,\n // Respect responsive priority when provided; default leaves it visible\n meta: { priority: (d as any).priority, label: d.label || `cf_${d.key}` } as any,\n }\n result.push(col)\n }\n return result\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/utils/useAutoDiscoveredFields.ts"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { FilterFieldDef as AdvancedFilterFieldDef, FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { ColumnChooserField } from '../columns/ColumnChooserPanel'\nimport type { CustomFieldDefDto } from './customFieldDefs'\nimport {\n mapCustomFieldKindToFilterType,\n normalizeCustomFieldFilterOptions,\n supportsCustomFieldColumn,\n} from './customFieldColumns'\n\ntype ColumnMeta = {\n filterKey?: string\n filterType?: FilterFieldType\n filterOptions?: FilterOption[]\n filterLoadOptions?: (query?: string) => Promise<FilterOption[]>\n filterable?: boolean\n filterGroup?: string\n filterIconName?: string\n columnChooserGroup?: string\n alwaysVisible?: boolean\n hidden?: boolean\n truncate?: boolean\n maxWidth?: string\n priority?: number\n tooltipContent?: (row: unknown) => string | undefined\n}\n\nfunction resolveHeaderLabel(column: ColumnDef<any, any>): string {\n const header = (column as any).header\n if (typeof header === 'string') return header\n const accessorKey = (column as any).accessorKey as string | undefined\n if (!accessorKey) return ''\n return accessorKey\n .replace(/^cf_/, '')\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (c: string) => c.toUpperCase())\n}\n\nfunction inferFilterType(accessorKey: string, meta?: ColumnMeta): FilterFieldType {\n if (meta?.filterType) return meta.filterType\n if (accessorKey.endsWith('_at') || accessorKey === 'createdAt' || accessorKey === 'updatedAt' || accessorKey === 'expectedCloseAt' || accessorKey.endsWith('At')) return 'date'\n if (accessorKey === 'probability' || accessorKey === 'valueAmount' || accessorKey === 'value_amount') return 'number'\n if (meta?.filterOptions) return 'select'\n return 'text'\n}\n\nexport type UseAutoDiscoveredFieldsInput = {\n columns: ColumnDef<any, any>[]\n customFieldDefs: CustomFieldDefDto[]\n}\n\nexport type UseAutoDiscoveredFieldsResult = {\n advancedFilterFields: AdvancedFilterFieldDef[]\n columnChooserFields: ColumnChooserField[]\n}\n\nexport function useAutoDiscoveredFields({\n columns,\n customFieldDefs,\n}: UseAutoDiscoveredFieldsInput): UseAutoDiscoveredFieldsResult {\n return React.useMemo(() => {\n const filterFields: AdvancedFilterFieldDef[] = []\n const chooserFields: ColumnChooserField[] = []\n const seenFilterKeys = new Set<string>()\n const seenChooserKeys = new Set<string>()\n\n for (let i = 0; i < columns.length; i++) {\n const col = columns[i]\n const accessorKey = (col as any).accessorKey as string | undefined\n if (!accessorKey) continue\n\n const meta = (col as any).meta as ColumnMeta | undefined\n const label = resolveHeaderLabel(col)\n if (!label) continue\n const filterKey = meta?.filterKey ?? accessorKey\n\n // Advanced filter field\n if (meta?.filterable !== false && filterKey && !seenFilterKeys.has(filterKey)) {\n seenFilterKeys.add(filterKey)\n const type = inferFilterType(filterKey, meta)\n const field: AdvancedFilterFieldDef = {\n key: filterKey,\n label,\n type,\n group: meta?.filterGroup ?? meta?.columnChooserGroup,\n }\n if (meta?.filterIconName) field.iconName = meta.filterIconName\n if (meta?.filterOptions) field.options = meta.filterOptions\n if (meta?.filterLoadOptions) field.loadOptions = meta.filterLoadOptions\n filterFields.push(field)\n }\n\n // Column chooser field\n if (!seenChooserKeys.has(accessorKey)) {\n seenChooserKeys.add(accessorKey)\n chooserFields.push({\n key: accessorKey,\n label,\n group: meta?.columnChooserGroup ?? 'Columns',\n alwaysVisible: meta?.alwaysVisible ?? i === 0,\n defaultVisible: true,\n })\n }\n }\n\n // Append ALL custom field definitions (not just filterable/listVisible)\n // so that every entity field appears in both the column chooser and advanced filter\n for (const def of customFieldDefs) {\n if (!supportsCustomFieldColumn(def)) continue\n const filterKey = `cf_${def.key}`\n if (!seenFilterKeys.has(filterKey)) {\n seenFilterKeys.add(filterKey)\n const type = mapCustomFieldKindToFilterType(def.kind)\n const field: AdvancedFilterFieldDef = {\n key: filterKey,\n label: def.label || def.key,\n type,\n group: 'Custom Fields',\n }\n if (type === 'select' && Array.isArray(def.options) && def.options.length) {\n field.options = normalizeCustomFieldFilterOptions(def.options)\n }\n filterFields.push(field)\n }\n if (!seenChooserKeys.has(filterKey)) {\n seenChooserKeys.add(filterKey)\n chooserFields.push({\n key: filterKey,\n label: def.label || def.key,\n group: def.group?.title ?? 'Custom Fields',\n defaultVisible: false,\n })\n }\n }\n\n return { advancedFilterFields: filterFields, columnChooserFields: chooserFields }\n }, [columns, customFieldDefs])\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport type { RowData } from '@tanstack/react-table'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { FilterFieldDef as AdvancedFilterFieldDef, FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport type { ColumnChooserField } from '../columns/ColumnChooserPanel'\nimport type { CustomFieldDefDto } from './customFieldDefs'\nimport {\n mapCustomFieldKindToFilterType,\n normalizeCustomFieldFilterOptions,\n supportsCustomFieldColumn,\n} from './customFieldColumns'\n\ntype ColumnMeta = {\n filterKey?: string\n filterType?: FilterFieldType\n filterOptions?: FilterOption[]\n filterLoadOptions?: (query?: string) => Promise<FilterOption[]>\n filterable?: boolean\n filterGroup?: string\n filterIconName?: string\n columnChooserGroup?: string\n alwaysVisible?: boolean\n hidden?: boolean\n truncate?: boolean\n maxWidth?: string\n priority?: number\n tooltipContent?: (row: unknown) => string | undefined\n}\n\nfunction resolveHeaderLabel<T extends RowData>(column: ColumnDef<T, unknown>): string {\n const header = (column as any).header\n if (typeof header === 'string') return header\n const accessorKey = (column as any).accessorKey as string | undefined\n if (!accessorKey) return ''\n return accessorKey\n .replace(/^cf_/, '')\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (c: string) => c.toUpperCase())\n}\n\nfunction inferFilterType(accessorKey: string, meta?: ColumnMeta): FilterFieldType {\n if (meta?.filterType) return meta.filterType\n if (accessorKey.endsWith('_at') || accessorKey === 'createdAt' || accessorKey === 'updatedAt' || accessorKey === 'expectedCloseAt' || accessorKey.endsWith('At')) return 'date'\n if (accessorKey === 'probability' || accessorKey === 'valueAmount' || accessorKey === 'value_amount') return 'number'\n if (meta?.filterOptions) return 'select'\n return 'text'\n}\n\nexport type UseAutoDiscoveredFieldsInput<T extends RowData = RowData> = {\n columns: ColumnDef<T, unknown>[]\n customFieldDefs: CustomFieldDefDto[]\n}\n\nexport type UseAutoDiscoveredFieldsResult = {\n advancedFilterFields: AdvancedFilterFieldDef[]\n columnChooserFields: ColumnChooserField[]\n}\n\nexport function useAutoDiscoveredFields<T extends RowData = RowData>({\n columns,\n customFieldDefs,\n}: UseAutoDiscoveredFieldsInput<T>): UseAutoDiscoveredFieldsResult {\n return React.useMemo(() => {\n const filterFields: AdvancedFilterFieldDef[] = []\n const chooserFields: ColumnChooserField[] = []\n const seenFilterKeys = new Set<string>()\n const seenChooserKeys = new Set<string>()\n\n for (let i = 0; i < columns.length; i++) {\n const col = columns[i]\n const accessorKey = (col as any).accessorKey as string | undefined\n if (!accessorKey) continue\n\n const meta = (col as any).meta as ColumnMeta | undefined\n const label = resolveHeaderLabel(col)\n if (!label) continue\n const filterKey = meta?.filterKey ?? accessorKey\n\n // Advanced filter field\n if (meta?.filterable !== false && filterKey && !seenFilterKeys.has(filterKey)) {\n seenFilterKeys.add(filterKey)\n const type = inferFilterType(filterKey, meta)\n const field: AdvancedFilterFieldDef = {\n key: filterKey,\n label,\n type,\n group: meta?.filterGroup ?? meta?.columnChooserGroup,\n }\n if (meta?.filterIconName) field.iconName = meta.filterIconName\n if (meta?.filterOptions) field.options = meta.filterOptions\n if (meta?.filterLoadOptions) field.loadOptions = meta.filterLoadOptions\n filterFields.push(field)\n }\n\n // Column chooser field\n if (!seenChooserKeys.has(accessorKey)) {\n seenChooserKeys.add(accessorKey)\n chooserFields.push({\n key: accessorKey,\n label,\n group: meta?.columnChooserGroup ?? 'Columns',\n alwaysVisible: meta?.alwaysVisible ?? i === 0,\n defaultVisible: true,\n })\n }\n }\n\n // Append ALL custom field definitions (not just filterable/listVisible)\n // so that every entity field appears in both the column chooser and advanced filter\n for (const def of customFieldDefs) {\n if (!supportsCustomFieldColumn(def)) continue\n const filterKey = `cf_${def.key}`\n if (!seenFilterKeys.has(filterKey)) {\n seenFilterKeys.add(filterKey)\n const type = mapCustomFieldKindToFilterType(def.kind)\n const field: AdvancedFilterFieldDef = {\n key: filterKey,\n label: def.label || def.key,\n type,\n group: 'Custom Fields',\n }\n if (type === 'select' && Array.isArray(def.options) && def.options.length) {\n field.options = normalizeCustomFieldFilterOptions(def.options)\n }\n filterFields.push(field)\n }\n if (!seenChooserKeys.has(filterKey)) {\n seenChooserKeys.add(filterKey)\n chooserFields.push({\n key: filterKey,\n label: def.label || def.key,\n group: def.group?.title ?? 'Custom Fields',\n defaultVisible: false,\n })\n }\n }\n\n return { advancedFilterFields: filterFields, columnChooserFields: chooserFields }\n }, [columns, customFieldDefs])\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/jest.config.cjs CHANGED
@@ -39,7 +39,7 @@ module.exports = {
39
39
  },
40
40
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
41
41
  transformIgnorePatterns: [
42
- 'node_modules/(?!(@mikro-orm|kysely|ai|@ai-sdk|ai-sdk-ollama|@workflow|@standard-schema)/)',
42
+ 'node_modules/(?!(@mikro-orm|kysely|ai|@ai-sdk|ai-sdk-ollama|@workflow|@standard-schema|@tanstack/react-table|@tanstack/table-core|@tanstack/react-store|@tanstack/store)/)',
43
43
  ],
44
44
  testMatch: [
45
45
  '<rootDir>/src/**/__tests__/**/*.test.(ts|tsx)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.6.8-develop.6910.1.560e304de2",
3
+ "version": "0.6.8-develop.6912.1.36161dc9ac",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -155,14 +155,14 @@
155
155
  "remark-gfm": "^4.0.1"
156
156
  },
157
157
  "peerDependencies": {
158
- "@open-mercato/shared": "0.6.8-develop.6910.1.560e304de2",
158
+ "@open-mercato/shared": "0.6.8-develop.6912.1.36161dc9ac",
159
159
  "react": ">=18.0.0",
160
160
  "react-dom": ">=18.0.0",
161
161
  "react-is": ">=18.0.0"
162
162
  },
163
163
  "devDependencies": {
164
164
  "@figma/code-connect": "^1.3.4",
165
- "@open-mercato/shared": "0.6.8-develop.6910.1.560e304de2",
165
+ "@open-mercato/shared": "0.6.8-develop.6912.1.36161dc9ac",
166
166
  "@testing-library/dom": "^10.4.1",
167
167
  "@testing-library/jest-dom": "^7.0.0",
168
168
  "@testing-library/react": "^16.3.1",
@@ -1,7 +1,8 @@
1
1
  "use client"
2
2
  import * as React from 'react'
3
3
  import { useRouter } from 'next/navigation'
4
- import { useReactTable, getCoreRowModel, getSortedRowModel, flexRender, type ColumnDef, type SortingState, type Column as TableColumn, type VisibilityState, type RowSelectionState } from '@tanstack/react-table'
4
+ import { flexRender, type RowData, type SortingState, type ColumnVisibilityState as VisibilityState, type RowSelectionState } from '@tanstack/react-table'
5
+ import { useLegacyTable, getCoreRowModel, getSortedRowModel, type LegacyColumnDef as ColumnDef, type LegacyColumn as TableColumn } from '@tanstack/react-table/legacy'
5
6
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
6
7
  import { RefreshCw, Loader2, SlidersHorizontal, MoreHorizontal, Circle, Filter, Columns3, ChevronUp, ChevronDown, ChevronsUpDown, Check, Inbox } from 'lucide-react'
7
8
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../primitives/table'
@@ -223,7 +224,7 @@ export type BulkAction<T = Record<string, unknown>> = {
223
224
  onExecute: (selectedRows: T[]) => Promise<void | boolean | BulkActionExecuteResult> | void | boolean | BulkActionExecuteResult
224
225
  }
225
226
 
226
- export type DataTableProps<T> = {
227
+ export type DataTableProps<T extends RowData> = {
227
228
  columns: ColumnDef<T, any>[]
228
229
  data: T[]
229
230
  toolbar?: React.ReactNode
@@ -1112,7 +1113,7 @@ function ViewSwitcherDropdown({
1112
1113
  )
1113
1114
  }
1114
1115
 
1115
- export function DataTable<T>({
1116
+ export function DataTable<T extends RowData>({
1116
1117
  columns,
1117
1118
  data,
1118
1119
  toolbar,
@@ -1628,7 +1629,7 @@ export function DataTable<T>({
1628
1629
  const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({})
1629
1630
  const selectionScopeKeyRef = React.useRef<string | undefined>(selectionScopeKey)
1630
1631
  const enableClientSorting = sortable && !manualSorting
1631
- const table = useReactTable<T>({
1632
+ const table = useLegacyTable<T>({
1632
1633
  data: clientFilteredData,
1633
1634
  columns: mergedColumns,
1634
1635
  getCoreRowModel: getCoreRowModel(),
@@ -4,7 +4,7 @@
4
4
  import * as React from 'react'
5
5
  import { renderToString } from 'react-dom/server'
6
6
  import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
7
- import type { ColumnDef } from '@tanstack/react-table'
7
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
8
8
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
9
9
  import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
10
10
  import { DataTable } from '../DataTable'
@@ -0,0 +1,81 @@
1
+ /** @jest-environment jsdom */
2
+ import * as React from 'react'
3
+ import { DataTable } from '../DataTable'
4
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
5
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
6
+ import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
7
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react'
8
+
9
+ jest.mock('next/navigation', () => ({
10
+ useRouter: () => ({ push: jest.fn(), replace: jest.fn(), prefetch: jest.fn() }),
11
+ }))
12
+
13
+ jest.mock('../injection/useInjectionDataWidgets', () => ({
14
+ useInjectionDataWidgets: () => ({ widgets: [], isLoading: false }),
15
+ }))
16
+
17
+ type Row = { id: string; name: string }
18
+
19
+ const columns: ColumnDef<Row>[] = [{ accessorKey: 'name', header: 'Name' }]
20
+
21
+ const rows: Row[] = [
22
+ { id: '2', name: 'Zed' },
23
+ { id: '1', name: 'Ada' },
24
+ { id: '3', name: 'Mia' },
25
+ ]
26
+
27
+ function renderTable(props: Record<string, unknown>) {
28
+ const queryClient = new QueryClient({ defaultOptions: { queries: { gcTime: 0 } } })
29
+ const result = render(
30
+ <QueryClientProvider client={queryClient}>
31
+ <I18nProvider locale="en" dict={{}}>
32
+ <DataTable columns={columns} data={rows} {...(props as any)} />
33
+ </I18nProvider>
34
+ </QueryClientProvider>,
35
+ )
36
+ return { ...result, queryClient }
37
+ }
38
+
39
+ /**
40
+ * TanStack Table v9 moved the v8 hook and row-model factories behind the
41
+ * `@tanstack/react-table/legacy` entry point. DataTable consumes that shim, so
42
+ * these cover the two v8 behaviours whose wiring the shim owns — the sorted row
43
+ * model and the row-selection feature — rather than re-testing the library.
44
+ */
45
+ describe('DataTable on the TanStack Table v9 legacy shim', () => {
46
+ it('sorts rows client-side through the legacy sorted row model', () => {
47
+ const { container, queryClient } = renderTable({ sortable: true })
48
+ try {
49
+ const initial = container.textContent ?? ''
50
+ expect(initial.indexOf('Zed')).toBeLessThan(initial.indexOf('Ada'))
51
+
52
+ fireEvent.click(screen.getByRole('button', { name: /name/i }))
53
+
54
+ const sorted = container.textContent ?? ''
55
+ expect(sorted.indexOf('Ada')).toBeLessThan(sorted.indexOf('Mia'))
56
+ expect(sorted.indexOf('Mia')).toBeLessThan(sorted.indexOf('Zed'))
57
+ } finally {
58
+ queryClient.clear()
59
+ }
60
+ })
61
+
62
+ it('passes the selected originals to a bulk action through the legacy row-selection feature', async () => {
63
+ const onExecute = jest.fn().mockResolvedValue(undefined)
64
+ const { queryClient } = renderTable({
65
+ bulkActions: [{ id: 'archive', label: 'Archive', onExecute }],
66
+ })
67
+ try {
68
+ const checkboxes = screen.getAllByRole('checkbox')
69
+ // The first checkbox is the header select-all; the rest are per row.
70
+ expect(checkboxes.length).toBe(rows.length + 1)
71
+
72
+ fireEvent.click(checkboxes[1])
73
+ fireEvent.click(await screen.findByRole('button', { name: /archive/i }))
74
+
75
+ await waitFor(() => expect(onExecute).toHaveBeenCalledTimes(1))
76
+ expect(onExecute.mock.calls[0][0]).toEqual([rows[0]])
77
+ } finally {
78
+ queryClient.clear()
79
+ }
80
+ })
81
+ })
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import * as React from 'react'
5
5
  import { render, screen, waitFor } from '@testing-library/react'
6
- import type { ColumnDef } from '@tanstack/react-table'
6
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
7
7
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
8
8
  import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
9
9
  import { DataTable } from '../DataTable'
@@ -1,7 +1,7 @@
1
1
  /** @jest-environment jsdom */
2
2
  import * as React from 'react'
3
3
  import { DataTable } from '../DataTable'
4
- import type { ColumnDef } from '@tanstack/react-table'
4
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
5
5
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
6
6
  import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
7
7
  import { render, fireEvent, waitFor, screen } from '@testing-library/react'
@@ -2,7 +2,7 @@
2
2
  import * as React from 'react'
3
3
  import { renderToString } from 'react-dom/server'
4
4
  import { DataTable } from '../DataTable'
5
- import type { ColumnDef } from '@tanstack/react-table'
5
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
6
6
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
7
7
  import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
8
8
  import { fireEvent, render, screen } from '@testing-library/react'
@@ -1,7 +1,7 @@
1
1
  /** @jest-environment jsdom */
2
2
  import * as React from 'react'
3
3
  import { DataTable } from '../DataTable'
4
- import type { ColumnDef } from '@tanstack/react-table'
4
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
5
5
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
6
6
  import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
7
7
  import { render } from '@testing-library/react'
@@ -10,7 +10,7 @@
10
10
  import * as React from 'react'
11
11
  import { render } from '@testing-library/react'
12
12
  import { DataTable } from '../DataTable'
13
- import type { ColumnDef } from '@tanstack/react-table'
13
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
14
14
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
15
15
  import { I18nProvider } from '@open-mercato/shared/lib/i18n/context'
16
16
 
@@ -1,4 +1,5 @@
1
- import type { ColumnDef } from '@tanstack/react-table'
1
+ import type { RowData } from '@tanstack/react-table'
2
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
2
3
  import type { FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'
3
4
  import type { CustomFieldDefDto, CustomFieldVisibility } from './customFieldDefs'
4
5
  import { isDefVisible } from './customFieldDefs'
@@ -51,7 +52,7 @@ export function normalizeCustomFieldFilterOptions(options?: RawCustomFieldOption
51
52
  // Filters and annotates columns with custom-field definitions:
52
53
  // - Drops cf_* columns when no definition exists or listVisible === false
53
54
  // - Uses definition label as header when header is missing
54
- export function applyCustomFieldVisibility<T>(columns: ColumnDef<T, any>[], defs: CustomFieldDefDto[], mode: CustomFieldVisibility = 'list'): ColumnDef<T, any>[] {
55
+ export function applyCustomFieldVisibility<T extends RowData = RowData>(columns: ColumnDef<T, any>[], defs: CustomFieldDefDto[], mode: CustomFieldVisibility = 'list'): ColumnDef<T, any>[] {
55
56
  const byKey = new Map(defs.map((d) => [d.key, d]))
56
57
  // First, filter and annotate headers
57
58
  const filtered = columns.filter((c) => {
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
  import * as React from 'react'
3
- import type { ColumnDef } from '@tanstack/react-table'
3
+ import type { RowData } from '@tanstack/react-table'
4
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
4
5
  import type { FilterFieldDef as AdvancedFilterFieldDef, FilterFieldType, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'
5
6
  import type { ColumnChooserField } from '../columns/ColumnChooserPanel'
6
7
  import type { CustomFieldDefDto } from './customFieldDefs'
@@ -27,7 +28,7 @@ type ColumnMeta = {
27
28
  tooltipContent?: (row: unknown) => string | undefined
28
29
  }
29
30
 
30
- function resolveHeaderLabel(column: ColumnDef<any, any>): string {
31
+ function resolveHeaderLabel<T extends RowData>(column: ColumnDef<T, unknown>): string {
31
32
  const header = (column as any).header
32
33
  if (typeof header === 'string') return header
33
34
  const accessorKey = (column as any).accessorKey as string | undefined
@@ -46,8 +47,8 @@ function inferFilterType(accessorKey: string, meta?: ColumnMeta): FilterFieldTyp
46
47
  return 'text'
47
48
  }
48
49
 
49
- export type UseAutoDiscoveredFieldsInput = {
50
- columns: ColumnDef<any, any>[]
50
+ export type UseAutoDiscoveredFieldsInput<T extends RowData = RowData> = {
51
+ columns: ColumnDef<T, unknown>[]
51
52
  customFieldDefs: CustomFieldDefDto[]
52
53
  }
53
54
 
@@ -56,10 +57,10 @@ export type UseAutoDiscoveredFieldsResult = {
56
57
  columnChooserFields: ColumnChooserField[]
57
58
  }
58
59
 
59
- export function useAutoDiscoveredFields({
60
+ export function useAutoDiscoveredFields<T extends RowData = RowData>({
60
61
  columns,
61
62
  customFieldDefs,
62
- }: UseAutoDiscoveredFieldsInput): UseAutoDiscoveredFieldsResult {
63
+ }: UseAutoDiscoveredFieldsInput<T>): UseAutoDiscoveredFieldsResult {
63
64
  return React.useMemo(() => {
64
65
  const filterFields: AdvancedFilterFieldDef[] = []
65
66
  const chooserFields: ColumnChooserField[] = []