@jmruthers/pace-core 0.5.97 → 0.5.99

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.
Files changed (118) hide show
  1. package/dist/{DataTable-FB22ES46.js → DataTable-VSJWLCVT.js} +4 -2
  2. package/dist/{chunk-UBPRDNPY.js → chunk-2PWJ6NFH.js} +2 -2
  3. package/dist/{chunk-QX5I62KP.js → chunk-D7CZVI3K.js} +167 -17
  4. package/dist/{chunk-QX5I62KP.js.map → chunk-D7CZVI3K.js.map} +1 -1
  5. package/dist/components.js +2 -2
  6. package/dist/index.js +2 -2
  7. package/dist/utils.js +1 -1
  8. package/docs/api/classes/ColumnFactory.md +1 -1
  9. package/docs/api/classes/ErrorBoundary.md +1 -1
  10. package/docs/api/classes/InvalidScopeError.md +1 -1
  11. package/docs/api/classes/MissingUserContextError.md +1 -1
  12. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  13. package/docs/api/classes/PermissionDeniedError.md +1 -1
  14. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  15. package/docs/api/classes/RBACAuditManager.md +1 -1
  16. package/docs/api/classes/RBACCache.md +1 -1
  17. package/docs/api/classes/RBACEngine.md +1 -1
  18. package/docs/api/classes/RBACError.md +1 -1
  19. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  20. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  21. package/docs/api/classes/StorageUtils.md +1 -1
  22. package/docs/api/enums/FileCategory.md +1 -1
  23. package/docs/api/interfaces/AggregateConfig.md +1 -1
  24. package/docs/api/interfaces/ButtonProps.md +1 -1
  25. package/docs/api/interfaces/CardProps.md +1 -1
  26. package/docs/api/interfaces/ColorPalette.md +1 -1
  27. package/docs/api/interfaces/ColorShade.md +1 -1
  28. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  29. package/docs/api/interfaces/DataRecord.md +1 -1
  30. package/docs/api/interfaces/DataTableAction.md +1 -1
  31. package/docs/api/interfaces/DataTableColumn.md +1 -1
  32. package/docs/api/interfaces/DataTableProps.md +1 -1
  33. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  34. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  35. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  36. package/docs/api/interfaces/EventLogoProps.md +1 -1
  37. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  38. package/docs/api/interfaces/FileMetadata.md +1 -1
  39. package/docs/api/interfaces/FileReference.md +1 -1
  40. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  41. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  42. package/docs/api/interfaces/FileUploadProps.md +1 -1
  43. package/docs/api/interfaces/FooterProps.md +1 -1
  44. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  45. package/docs/api/interfaces/InputProps.md +1 -1
  46. package/docs/api/interfaces/LabelProps.md +1 -1
  47. package/docs/api/interfaces/LoginFormProps.md +1 -1
  48. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  49. package/docs/api/interfaces/NavigationContextType.md +1 -1
  50. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  51. package/docs/api/interfaces/NavigationItem.md +1 -1
  52. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  53. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  54. package/docs/api/interfaces/Organisation.md +1 -1
  55. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  56. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  57. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  58. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  59. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  60. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  61. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  62. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  63. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  64. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  65. package/docs/api/interfaces/PaletteData.md +1 -1
  66. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  67. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  68. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  69. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  70. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  71. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  72. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  73. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  74. package/docs/api/interfaces/RBACConfig.md +1 -1
  75. package/docs/api/interfaces/RBACLogger.md +1 -1
  76. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  77. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  78. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  79. package/docs/api/interfaces/RouteConfig.md +1 -1
  80. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  81. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  82. package/docs/api/interfaces/StorageConfig.md +1 -1
  83. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  84. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  85. package/docs/api/interfaces/StorageListOptions.md +1 -1
  86. package/docs/api/interfaces/StorageListResult.md +1 -1
  87. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  88. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  89. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  90. package/docs/api/interfaces/StyleImport.md +1 -1
  91. package/docs/api/interfaces/SwitchProps.md +1 -1
  92. package/docs/api/interfaces/ToastActionElement.md +1 -1
  93. package/docs/api/interfaces/ToastProps.md +1 -1
  94. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  95. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  96. package/docs/api/interfaces/UseEventLogoOptions.md +1 -1
  97. package/docs/api/interfaces/UseEventLogoReturn.md +1 -1
  98. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  99. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  100. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  101. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  102. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  103. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  104. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  105. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  106. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  107. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  108. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  109. package/docs/api/interfaces/UserEventAccess.md +1 -1
  110. package/docs/api/interfaces/UserMenuProps.md +1 -1
  111. package/docs/api/interfaces/UserProfile.md +1 -1
  112. package/docs/api/modules.md +2 -2
  113. package/package.json +1 -1
  114. package/src/components/DataTable/components/DataTableCore.tsx +60 -16
  115. package/src/components/DataTable/components/DataTableModals.tsx +67 -8
  116. package/src/components/DataTable/utils/exportUtils.ts +115 -0
  117. /package/dist/{DataTable-FB22ES46.js.map → DataTable-VSJWLCVT.js.map} +0 -0
  118. /package/dist/{chunk-UBPRDNPY.js.map → chunk-2PWJ6NFH.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / StorageFileMetadata
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / StorageFileMetadata
2
2
 
3
3
  # Interface: StorageFileMetadata
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / StorageListOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / StorageListOptions
2
2
 
3
3
  # Interface: StorageListOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / StorageListResult
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / StorageListResult
2
2
 
3
3
  # Interface: StorageListResult
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / StorageUploadOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / StorageUploadOptions
2
2
 
3
3
  # Interface: StorageUploadOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / StorageUploadResult
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / StorageUploadResult
2
2
 
3
3
  # Interface: StorageUploadResult
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / StorageUrlOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / StorageUrlOptions
2
2
 
3
3
  # Interface: StorageUrlOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / StyleImport
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / StyleImport
2
2
 
3
3
  # Interface: StyleImport
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / SwitchProps
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / SwitchProps
2
2
 
3
3
  # Interface: SwitchProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / ToastActionElement
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / ToastActionElement
2
2
 
3
3
  # Interface: ToastActionElement
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / ToastProps
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / ToastProps
2
2
 
3
3
  # Interface: ToastProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UnifiedAuthContextType
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UnifiedAuthContextType
2
2
 
3
3
  # Interface: UnifiedAuthContextType
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UnifiedAuthProviderProps
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UnifiedAuthProviderProps
2
2
 
3
3
  # Interface: UnifiedAuthProviderProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UseEventLogoOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UseEventLogoOptions
2
2
 
3
3
  # Interface: UseEventLogoOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UseEventLogoReturn
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UseEventLogoReturn
2
2
 
3
3
  # Interface: UseEventLogoReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UseInactivityTrackerOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UseInactivityTrackerOptions
2
2
 
3
3
  # Interface: UseInactivityTrackerOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UseInactivityTrackerReturn
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UseInactivityTrackerReturn
2
2
 
3
3
  # Interface: UseInactivityTrackerReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UsePublicEventLogoOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UsePublicEventLogoOptions
2
2
 
3
3
  # Interface: UsePublicEventLogoOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UsePublicEventLogoReturn
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UsePublicEventLogoReturn
2
2
 
3
3
  # Interface: UsePublicEventLogoReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UsePublicEventOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UsePublicEventOptions
2
2
 
3
3
  # Interface: UsePublicEventOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UsePublicEventReturn
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UsePublicEventReturn
2
2
 
3
3
  # Interface: UsePublicEventReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayOptions
2
2
 
3
3
  # Interface: UsePublicFileDisplayOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayReturn
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayReturn
2
2
 
3
3
  # Interface: UsePublicFileDisplayReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UsePublicRouteParamsReturn
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UsePublicRouteParamsReturn
2
2
 
3
3
  # Interface: UsePublicRouteParamsReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UseResolvedScopeOptions
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UseResolvedScopeOptions
2
2
 
3
3
  # Interface: UseResolvedScopeOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UseResolvedScopeReturn
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UseResolvedScopeReturn
2
2
 
3
3
  # Interface: UseResolvedScopeReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UserEventAccess
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UserEventAccess
2
2
 
3
3
  # Interface: UserEventAccess
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UserMenuProps
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UserMenuProps
2
2
 
3
3
  # Interface: UserMenuProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.97](../README.md) / [Exports](../modules.md) / UserProfile
1
+ [@jmruthers/pace-core - v0.5.99](../README.md) / [Exports](../modules.md) / UserProfile
2
2
 
3
3
  # Interface: UserProfile
4
4
 
@@ -1,6 +1,6 @@
1
- [@jmruthers/pace-core - v0.5.97](README.md) / Exports
1
+ [@jmruthers/pace-core - v0.5.99](README.md) / Exports
2
2
 
3
- # @jmruthers/pace-core - v0.5.97
3
+ # @jmruthers/pace-core - v0.5.99
4
4
 
5
5
  **`File`**
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.97",
3
+ "version": "0.5.99",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -43,7 +43,7 @@ import { ColumnFactory } from '../core/ColumnFactory';
43
43
  import { AccessDeniedPage } from './AccessDeniedPage';
44
44
  import { useCan, useResolvedScope } from '../../../rbac/hooks';
45
45
  import { toast } from '../../../hooks/useToast';
46
- import { exportToCSV } from '../utils/exportUtils';
46
+ import { exportToCSV, exportToCSVWithTableRows } from '../utils/exportUtils';
47
47
  import { useUnifiedAuth } from '../../../providers/UnifiedAuthProvider';
48
48
  import { Scope } from '../../../rbac/types';
49
49
  import { useDataTablePermissions } from '../hooks/useDataTablePermissions';
@@ -969,8 +969,8 @@ function DataTableInternal<TData extends DataRecord>({
969
969
  onExport={secureHandlers.onExport || (async () => {
970
970
  try {
971
971
  // Automatic export: exports exactly what's shown in the table
972
- // Get the current filtered/paginated data (what's actually visible)
973
- const currentData = table.getFilteredRowModel().rows.map(row => row.original);
972
+ // Get the table rows (which have getValue() that properly evaluates accessorFn)
973
+ const tableRows = table.getFilteredRowModel().rows;
974
974
 
975
975
  // Get only visible columns by checking the actual table columns
976
976
  // This approach is more reliable because it uses the table's actual column registry
@@ -981,8 +981,8 @@ function DataTableInternal<TData extends DataRecord>({
981
981
  return !isSystemColumn && col.getIsVisible();
982
982
  });
983
983
 
984
- // Map table columns back to original column definitions for export
985
- // Also add ID columns for reference fields (columns with accessorFn + editAccessorKey)
984
+ // Map table columns to export columns
985
+ // Use TanStack Table's getValue() which properly handles accessorFn
986
986
  const visibleColumns: Array<{
987
987
  header?: string;
988
988
  id?: string;
@@ -992,6 +992,9 @@ function DataTableInternal<TData extends DataRecord>({
992
992
  isIdColumn?: boolean;
993
993
  }> = [];
994
994
 
995
+ // Store mapping of column IDs to table column instances for getValue() calls
996
+ const columnIdToTableColumn = new Map<string, typeof visibleTableColumns[0]>();
997
+
995
998
  visibleTableColumns.forEach(tableCol => {
996
999
  // Find the original column definition that matches this table column
997
1000
  const originalCol = columns.find(col => {
@@ -1001,27 +1004,38 @@ function DataTableInternal<TData extends DataRecord>({
1001
1004
 
1002
1005
  if (!originalCol) return;
1003
1006
 
1007
+ // Store the table column for getValue() calls
1008
+ columnIdToTableColumn.set(tableCol.id, tableCol);
1009
+
1004
1010
  const hasAccessorFn = 'accessorFn' in originalCol && (originalCol as any).accessorFn;
1005
1011
  const editAccessorKey = originalCol.editAccessorKey;
1006
1012
 
1007
1013
  // Add the display column (what's shown in the table)
1014
+ // For columns with accessorFn, we'll use the table's getValue() method instead of calling accessorFn directly
1015
+ const displayHeader = typeof originalCol.header === 'string'
1016
+ ? originalCol.header
1017
+ : originalCol.accessorKey || tableCol.id || 'Column';
1018
+
1008
1019
  visibleColumns.push({
1009
1020
  ...originalCol,
1010
- header: typeof originalCol.header === 'string'
1011
- ? originalCol.header
1012
- : originalCol.accessorKey || tableCol.id || 'Column',
1013
- // Preserve accessorFn if present (for computed/reference fields)
1014
- // accessorFn is part of ColumnDef from TanStack Table
1021
+ header: displayHeader,
1022
+ // Store table column ID for getValue() lookup
1023
+ id: tableCol.id,
1024
+ // Preserve accessorFn if present (will use getValue() if available)
1015
1025
  accessorFn: hasAccessorFn ? (originalCol as any).accessorFn : undefined,
1016
1026
  });
1017
1027
 
1018
1028
  // For reference fields (accessorFn + editAccessorKey), also add ID column
1019
1029
  // This allows round-trip import/export to work seamlessly
1020
1030
  if (hasAccessorFn && editAccessorKey) {
1031
+ // Use the same string fallback logic as the display column to ensure
1032
+ // JSX headers are converted to strings (e.g., accessorKey or column ID)
1033
+ // This prevents "[object Object] (ID)" in the CSV header
1034
+ const idHeader = `${displayHeader} (ID)`;
1021
1035
  visibleColumns.push({
1022
1036
  id: editAccessorKey,
1023
1037
  accessorKey: editAccessorKey,
1024
- header: `${originalCol.header || 'ID'} (ID)`,
1038
+ header: idHeader,
1025
1039
  isIdColumn: true,
1026
1040
  editAccessorKey: editAccessorKey,
1027
1041
  });
@@ -1032,8 +1046,9 @@ function DataTableInternal<TData extends DataRecord>({
1032
1046
  const timestamp = new Date().toISOString().split('T')[0];
1033
1047
  const filename = title ? `${title.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_${timestamp}.csv` : `data_export_${timestamp}.csv`;
1034
1048
 
1035
- // Export to CSV
1036
- await exportToCSV(currentData, visibleColumns, filename);
1049
+ // Export using table rows with getValue() for proper accessorFn evaluation
1050
+ // This ensures we get the same values that are displayed in the table
1051
+ await exportToCSVWithTableRows(tableRows, visibleColumns, columnIdToTableColumn, filename);
1037
1052
 
1038
1053
  // Show success toast notification
1039
1054
  toast({
@@ -1278,10 +1293,39 @@ function DataTableInternal<TData extends DataRecord>({
1278
1293
  onCloseImportModal={() => stateActions.setImportModal(false)}
1279
1294
  onImport={async (data: TData[]) => {
1280
1295
  if (onImport) {
1281
- const result = onImport(data);
1282
- if (result && typeof result.then === 'function') {
1283
- await result;
1296
+ try {
1297
+ console.log('[DataTableCore] onImport called with', data.length, 'rows');
1298
+ const result = onImport(data);
1299
+ if (result && typeof result.then === 'function') {
1300
+ await result;
1301
+ }
1302
+ console.log('[DataTableCore] onImport completed successfully');
1303
+
1304
+ // Show success toast
1305
+ toast({
1306
+ title: "Import Successful",
1307
+ description: `Successfully imported ${data.length} ${data.length === 1 ? 'row' : 'rows'}`,
1308
+ variant: "default"
1309
+ });
1310
+ } catch (error) {
1311
+ console.error('[DataTableCore] Import error:', error);
1312
+ toast({
1313
+ title: "Import Failed",
1314
+ description: error instanceof Error ? error.message : 'Failed to import data',
1315
+ variant: "destructive"
1316
+ });
1317
+ // Don't close modal on error so user can see the error
1318
+ return;
1284
1319
  }
1320
+ } else {
1321
+ console.error('[DataTableCore] onImport handler not provided');
1322
+ toast({
1323
+ title: "Import Not Configured",
1324
+ description: "Import functionality requires an onImport handler to be provided.",
1325
+ variant: "destructive"
1326
+ });
1327
+ // Don't close modal so user can see the error
1328
+ return;
1285
1329
  }
1286
1330
  stateActions.setImportModal(false);
1287
1331
  }}
@@ -41,6 +41,8 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
41
41
  // Priority: editAccessorKey > accessorKey > id
42
42
  const columnMap = new Map<string, string>();
43
43
 
44
+ console.log('[mapCSVToTableColumns] Building column map from', columns.length, 'column definitions');
45
+
44
46
  columns.forEach(col => {
45
47
  const fieldName = col.editAccessorKey || col.accessorKey || col.id;
46
48
  const header = typeof col.header === 'string' ? col.header : '';
@@ -48,10 +50,13 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
48
50
  if (fieldName && header) {
49
51
  // Map header to field name (case-insensitive)
50
52
  columnMap.set(header.toLowerCase(), fieldName);
53
+ console.log(`[mapCSVToTableColumns] Mapped "${header}" -> "${fieldName}"`);
54
+
51
55
  // Also map id/accessorKey if different from header
52
56
  const colId = col.id || col.accessorKey;
53
57
  if (colId && colId !== header && colId !== fieldName) {
54
58
  columnMap.set(colId.toLowerCase(), fieldName);
59
+ console.log(`[mapCSVToTableColumns] Also mapped "${colId}" -> "${fieldName}"`);
55
60
  }
56
61
 
57
62
  // For reference fields with editAccessorKey, also map the ID column header
@@ -59,25 +64,57 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
59
64
  if (col.editAccessorKey && header) {
60
65
  const idColumnHeader = `${header} (ID)`;
61
66
  columnMap.set(idColumnHeader.toLowerCase(), col.editAccessorKey);
67
+ console.log(`[mapCSVToTableColumns] Mapped ID column "${idColumnHeader}" -> "${col.editAccessorKey}"`);
62
68
  // Also map the editAccessorKey directly (in case CSV uses the field name)
63
69
  columnMap.set(col.editAccessorKey.toLowerCase(), col.editAccessorKey);
70
+ console.log(`[mapCSVToTableColumns] Also mapped "${col.editAccessorKey}" -> "${col.editAccessorKey}"`);
64
71
  }
72
+ } else {
73
+ console.warn('[mapCSVToTableColumns] Skipping column with missing fieldName or header:', col);
65
74
  }
66
75
  });
67
76
 
77
+ if (csvData.length === 0) {
78
+ console.warn('[mapCSVToTableColumns] No CSV data to map');
79
+ return [];
80
+ }
81
+
82
+ const csvHeaders = Object.keys(csvData[0]);
83
+ console.log('[mapCSVToTableColumns] CSV headers found:', csvHeaders);
84
+ console.log('[mapCSVToTableColumns] Column map size:', columnMap.size);
85
+
68
86
  // Transform CSV data using the mapping
69
- return csvData.map(row => {
87
+ const mappedData = csvData.map((row, index) => {
70
88
  const mappedRow: Record<string, unknown> = {};
71
89
 
72
90
  // For each CSV column, find the corresponding table field
73
91
  Object.keys(row).forEach(csvHeader => {
74
92
  const csvHeaderLower = csvHeader.toLowerCase();
75
- const fieldName = columnMap.get(csvHeaderLower) || csvHeaderLower;
76
- mappedRow[fieldName] = row[csvHeader];
93
+ const fieldName = columnMap.get(csvHeaderLower);
94
+
95
+ if (fieldName) {
96
+ mappedRow[fieldName] = row[csvHeader];
97
+ if (index === 0) {
98
+ console.log(`[mapCSVToTableColumns] Row 0: "${csvHeader}" -> "${fieldName}"`);
99
+ }
100
+ } else {
101
+ // If no mapping found, use the CSV header as-is (lowercase)
102
+ mappedRow[csvHeaderLower] = row[csvHeader];
103
+ if (index === 0) {
104
+ console.warn(`[mapCSVToTableColumns] No mapping found for "${csvHeader}", using as-is`);
105
+ }
106
+ }
77
107
  });
78
108
 
79
109
  return mappedRow as TData;
80
110
  });
111
+
112
+ console.log('[mapCSVToTableColumns] Mapped', mappedData.length, 'rows');
113
+ if (mappedData.length > 0) {
114
+ console.log('[mapCSVToTableColumns] First mapped row keys:', Object.keys(mappedData[0]));
115
+ }
116
+
117
+ return mappedData;
81
118
  }
82
119
 
83
120
  /**
@@ -164,11 +201,33 @@ export function DataTableModals<TData extends Record<string, unknown> = Record<s
164
201
  isOpen={showImportModal}
165
202
  onClose={onCloseImportModal}
166
203
  onImport={async (rawData: Array<Record<string, unknown>>) => {
167
- // Automatically map CSV columns to table columns based on column definitions
168
- const mappedData = columns ? mapCSVToTableColumns<TData>(rawData, columns) : (rawData as TData[]);
169
- const result = onImport(mappedData);
170
- if (result && typeof result.then === 'function') {
171
- await result;
204
+ try {
205
+ // Automatically map CSV columns to table columns based on column definitions
206
+ let mappedData: TData[];
207
+ if (columns && columns.length > 0) {
208
+ console.log('[DataTableModals] Mapping CSV data with', columns.length, 'column definitions');
209
+ console.log('[DataTableModals] Raw CSV headers:', rawData.length > 0 ? Object.keys(rawData[0]) : []);
210
+ mappedData = mapCSVToTableColumns<TData>(rawData, columns);
211
+ console.log('[DataTableModals] Mapped data sample:', mappedData.length > 0 ? mappedData[0] : null);
212
+ } else {
213
+ console.warn('[DataTableModals] No columns provided for mapping, using raw data');
214
+ mappedData = rawData as TData[];
215
+ }
216
+
217
+ if (!onImport) {
218
+ console.error('[DataTableModals] onImport callback is not provided');
219
+ throw new Error('Import handler is not configured. Please provide an onImport callback.');
220
+ }
221
+
222
+ console.log('[DataTableModals] Calling onImport with', mappedData.length, 'rows');
223
+ const result = onImport(mappedData);
224
+ if (result && typeof result.then === 'function') {
225
+ await result;
226
+ }
227
+ console.log('[DataTableModals] Import completed successfully');
228
+ } catch (error) {
229
+ console.error('[DataTableModals] Import error:', error);
230
+ throw error; // Re-throw to let ImportModal handle it
172
231
  }
173
232
  }}
174
233
  config={importModalConfig}
@@ -180,6 +180,121 @@ export function generateCSVContent<TData extends DataRecord>(
180
180
  return csvContent;
181
181
  }
182
182
 
183
+ /**
184
+ * Exports DataTable data using TanStack Table rows with getValue() for proper accessorFn evaluation
185
+ * This ensures computed columns (with accessorFn) export correctly with their displayed values
186
+ */
187
+ export async function exportToCSVWithTableRows(
188
+ tableRows: Array<{
189
+ original: any;
190
+ getValue: (columnId: string) => any;
191
+ id: string;
192
+ }>,
193
+ columns: ExportColumn[],
194
+ columnIdToTableColumn: Map<string, any>,
195
+ filename: string = "download.csv",
196
+ options: {
197
+ locale?: string;
198
+ sanitizeForSecurity?: boolean;
199
+ } = {}
200
+ ): Promise<void> {
201
+ const logger = createLogger('ExportUtils');
202
+ return new Promise((resolve, reject) => {
203
+ try {
204
+ if (typeof window === 'undefined') {
205
+ throw new Error('CSV export is only available in browser environments');
206
+ }
207
+
208
+ if (!tableRows || tableRows.length === 0) {
209
+ throw new Error('No data to export');
210
+ }
211
+
212
+ if (!columns || columns.length === 0) {
213
+ throw new Error('No columns defined for export');
214
+ }
215
+
216
+ // Create CSV header row
217
+ const headers = columns.map(col => {
218
+ const headerValue = col.header || col.id || "Column";
219
+ return escapeCSVValue(headerValue, options.sanitizeForSecurity !== false);
220
+ });
221
+
222
+ // Format data into CSV rows using table row getValue() for proper accessorFn evaluation
223
+ const csvData = tableRows.map(tableRow => {
224
+ return columns.map(col => {
225
+ let value: any;
226
+
227
+ // For ID columns (reference field IDs), get directly from original data
228
+ if (col.isIdColumn && col.accessorKey) {
229
+ value = tableRow.original[col.accessorKey];
230
+ }
231
+ // For columns with accessorFn, use TanStack Table's getValue() which properly evaluates it
232
+ else if (col.id && columnIdToTableColumn.has(col.id)) {
233
+ try {
234
+ // Use getValue() which properly evaluates accessorFn with the table's context
235
+ value = tableRow.getValue(col.id);
236
+ } catch (error) {
237
+ // Fallback: try accessorFn directly if getValue() fails
238
+ if (col.accessorFn) {
239
+ try {
240
+ value = col.accessorFn(tableRow.original);
241
+ } catch (accessorError) {
242
+ console.warn('Error evaluating accessorFn for column:', col.id || col.header, accessorError);
243
+ value = undefined;
244
+ }
245
+ } else {
246
+ // Direct property access
247
+ const key = col.accessorKey || col.id;
248
+ value = key ? tableRow.original[key] : undefined;
249
+ }
250
+ }
251
+ }
252
+ // Fallback to direct property access
253
+ else {
254
+ const key = col.accessorKey || col.id;
255
+ value = key ? tableRow.original[key] : undefined;
256
+ }
257
+
258
+ // Format according to locale if provided
259
+ if (options.locale && (typeof value === 'number' || value instanceof Date || typeof value === 'boolean')) {
260
+ value = formatLocaleValue(value, options.locale);
261
+ }
262
+
263
+ // Escape the value for CSV
264
+ return escapeCSVValue(value, options.sanitizeForSecurity !== false);
265
+ }).join(",");
266
+ });
267
+
268
+ // Combine header and data
269
+ const csvContent = [headers.join(","), ...csvData].join("\n");
270
+
271
+ // Create and trigger download
272
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
273
+ const link = document.createElement("a");
274
+ const url = URL.createObjectURL(blob);
275
+
276
+ link.setAttribute("href", url);
277
+ link.setAttribute("download", filename);
278
+ link.style.display = "none";
279
+
280
+ // Handle click event
281
+ link.onclick = () => {
282
+ setTimeout(() => {
283
+ URL.revokeObjectURL(url);
284
+ resolve();
285
+ }, 100);
286
+ };
287
+
288
+ document.body.appendChild(link);
289
+ link.click();
290
+ document.body.removeChild(link);
291
+ } catch (error) {
292
+ logger.error('Failed to export data to CSV:', error);
293
+ reject(error);
294
+ }
295
+ });
296
+ }
297
+
183
298
  /**
184
299
  * Exports DataTable data to CSV format
185
300
  *