@jmruthers/pace-core 0.5.121 → 0.5.124
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/dist/{AuthService-D4646R4b.d.ts → AuthService-DYuQPJj6.d.ts} +0 -9
- package/dist/{DataTable-DGZDJUYM.js → DataTable-OKDYRW2S.js} +7 -8
- package/dist/{PublicLoadingSpinner-DgDWTFqn.d.ts → PublicLoadingSpinner-CaoRbHvJ.d.ts} +30 -4
- package/dist/{UnifiedAuthProvider-UACKFATV.js → UnifiedAuthProvider-6C47WIML.js} +3 -4
- package/dist/{chunk-D6BOFXYR.js → chunk-35ZDPMBM.js} +3 -3
- package/dist/{chunk-CGURJ27Z.js → chunk-4MXVZVNS.js} +2 -2
- package/dist/{chunk-ZYJ6O5CA.js → chunk-C43QIDN3.js} +2 -2
- package/dist/{chunk-VKOCWWVY.js → chunk-CX5M4ZAG.js} +1 -6
- package/dist/{chunk-VKOCWWVY.js 3.map → chunk-CX5M4ZAG.js.map} +1 -1
- package/dist/{chunk-RIEJGKD3.js → chunk-ESJTIADP.js} +15 -6
- package/dist/{chunk-RIEJGKD3.js.map → chunk-ESJTIADP.js.map} +1 -1
- package/dist/{chunk-HFBOFZ3Z.js → chunk-GBGYYMC6.js} +317 -251
- package/dist/chunk-GBGYYMC6.js.map +1 -0
- package/dist/{chunk-SMJZMKYN.js → chunk-GEVIB2UB.js} +43 -10
- package/dist/chunk-GEVIB2UB.js.map +1 -0
- package/dist/{chunk-TDNI6ZWL.js → chunk-IJOZZOGT.js} +7 -7
- package/dist/chunk-IJOZZOGT.js.map +1 -0
- package/dist/{chunk-GZRXOUBE.js → chunk-M6DDYFUD.js} +2 -2
- package/dist/chunk-M6DDYFUD.js.map +1 -0
- package/dist/{chunk-B4GZ2BXO.js → chunk-NZGLXZGP.js} +3 -3
- package/dist/{chunk-NZ32EONV.js → chunk-QWNJCQXZ.js} +2 -2
- package/dist/{chunk-QPI2CCBA.js → chunk-VPUCTHTY.js} +149 -96
- package/dist/chunk-VPUCTHTY.js.map +1 -0
- package/dist/{chunk-FKFHZUGF.js → chunk-XN6GWKMV.js} +43 -56
- package/dist/chunk-XN6GWKMV.js.map +1 -0
- package/dist/{chunk-BHWIUEYH.js → chunk-ZBLK676C.js} +1 -61
- package/dist/chunk-ZBLK676C.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +11 -11
- package/dist/{formatting-B1jSqgl-.d.ts → formatting-DFcCxUEk.d.ts} +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +9 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +19 -17
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +2 -3
- package/dist/rbac/index.js +7 -8
- package/dist/styles/index.d.ts +1 -1
- package/dist/styles/index.js +5 -3
- package/dist/theming/runtime.d.ts +73 -1
- package/dist/theming/runtime.js +5 -5
- package/dist/{usePublicRouteParams-BdF8bZgs.d.ts → usePublicRouteParams-Dyt1tzI9.d.ts} +60 -8
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +5 -5
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +6 -6
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +6 -6
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +7 -7
- package/docs/api/interfaces/PublicErrorBoundaryState.md +5 -5
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +7 -7
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +51 -12
- package/docs/api/interfaces/PublicPageLayoutProps.md +72 -12
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +140 -30
- package/docs/best-practices/README.md +1 -1
- package/docs/implementation-guides/datatable-filtering.md +313 -0
- package/docs/implementation-guides/datatable-rbac-usage.md +317 -0
- package/docs/implementation-guides/hierarchical-datatable.md +850 -0
- package/docs/implementation-guides/large-datasets.md +281 -0
- package/docs/implementation-guides/performance.md +403 -0
- package/docs/implementation-guides/public-pages.md +4 -4
- package/docs/migration/quick-migration-guide.md +320 -0
- package/docs/rbac/quick-start.md +16 -16
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +1 -1
- package/docs/troubleshooting/debugging.md +1117 -0
- package/docs/troubleshooting/migration.md +918 -0
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +30 -30
- package/examples/public-pages/PublicEventPage.tsx +41 -41
- package/examples/public-pages/PublicPageApp.tsx +33 -33
- package/examples/public-pages/PublicPageUsageExample.tsx +30 -30
- package/package.json +4 -4
- package/src/__tests__/hooks/usePermissions.test.ts +265 -0
- package/src/components/DataTable/DataTable.test.tsx +9 -38
- package/src/components/DataTable/DataTable.tsx +0 -7
- package/src/components/DataTable/components/DataTableCore.tsx +125 -144
- package/src/components/DataTable/components/DataTableModals.tsx +25 -22
- package/src/components/DataTable/components/DataTableToolbar.tsx +14 -1
- package/src/components/DataTable/components/EditableRow.tsx +118 -42
- package/src/components/DataTable/components/UnifiedTableBody.tsx +129 -76
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +33 -14
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +17 -5
- package/src/components/DataTable/utils/exportUtils.ts +3 -2
- package/src/components/Dialog/Dialog.tsx +1 -1
- package/src/components/Dialog/README.md +24 -24
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +2 -2
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +6 -6
- package/src/components/Dialog/examples/HtmlDialogExample.tsx +2 -2
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +3 -3
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +4 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +12 -1
- package/src/components/PublicLayout/EventLogo.tsx +175 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +22 -18
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +22 -14
- package/src/components/PublicLayout/PublicPageHeader.tsx +133 -40
- package/src/components/PublicLayout/PublicPageLayout.tsx +75 -72
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +1 -1
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +8 -8
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +23 -16
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +86 -14
- package/src/examples/CorrectPublicPageImplementation.tsx +30 -30
- package/src/examples/PublicEventPage.tsx +41 -41
- package/src/examples/PublicPageApp.tsx +33 -33
- package/src/examples/PublicPageUsageExample.tsx +30 -30
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +10 -3
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +285 -0
- package/src/hooks/public/usePublicRouteParams.ts +21 -4
- package/src/hooks/useEventTheme.test.ts +119 -43
- package/src/hooks/useEventTheme.ts +84 -55
- package/src/index.ts +3 -1
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +630 -0
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +496 -0
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
- package/src/rbac/secureClient.ts +4 -2
- package/src/services/EventService.ts +0 -66
- package/src/services/__tests__/EventService.eventColours.test.ts +44 -40
- package/src/styles/index.ts +1 -1
- package/src/theming/__tests__/parseEventColours.test.ts +209 -0
- package/src/theming/parseEventColours.ts +123 -0
- package/src/theming/runtime.ts +3 -0
- package/src/types/__tests__/file-reference.test.ts +447 -0
- package/src/utils/formatDate.test.ts +11 -11
- package/src/utils/formatting.ts +3 -2
- package/dist/chunk-BDZUMRBD.js 3.map +0 -1
- package/dist/chunk-BHWIUEYH.js.map +0 -1
- package/dist/chunk-CGURJ27Z.js.map +0 -1
- package/dist/chunk-FKFHZUGF.js.map +0 -1
- package/dist/chunk-GKHF54DI 2.js +0 -619
- package/dist/chunk-GKHF54DI.js 2.map +0 -1
- package/dist/chunk-GZRXOUBE.js.map +0 -1
- package/dist/chunk-HFBOFZ3Z.js.map +0 -1
- package/dist/chunk-NZ32EONV.js.map +0 -1
- package/dist/chunk-O3NWNXDY 2.js +0 -76
- package/dist/chunk-QPI2CCBA.js.map +0 -1
- package/dist/chunk-SMJZMKYN.js.map +0 -1
- package/dist/chunk-TDNI6ZWL.js 2.map +0 -1
- package/dist/chunk-TDNI6ZWL.js.map +0 -1
- package/dist/chunk-VKOCWWVY.js.map +0 -1
- package/dist/chunk-WP5I5GLN 2.js +0 -1564
- package/dist/index 3.js +0 -856
- package/dist/providers 3.js +0 -38
- package/dist/providers.js 3.map +0 -1
- package/dist/types 3.js +0 -128
- package/dist/types.js 3.map +0 -1
- package/dist/useInactivityTracker-MRUU55XI.js 3.map +0 -1
- package/dist/utils.js 3.map +0 -1
- package/dist/validation 3.js +0 -479
- package/src/styles/semantic.css +0 -24
- /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-OKDYRW2S.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-UACKFATV.js.map → UnifiedAuthProvider-6C47WIML.js.map} +0 -0
- /package/dist/{chunk-D6BOFXYR.js.map → chunk-35ZDPMBM.js.map} +0 -0
- /package/dist/{chunk-CGURJ27Z.js 2.map → chunk-4MXVZVNS.js.map} +0 -0
- /package/dist/{chunk-ZYJ6O5CA.js.map → chunk-C43QIDN3.js.map} +0 -0
- /package/dist/{chunk-B4GZ2BXO.js.map → chunk-NZGLXZGP.js.map} +0 -0
- /package/dist/{chunk-NZ32EONV.js 2.map → chunk-QWNJCQXZ.js.map} +0 -0
|
@@ -53,6 +53,7 @@ import { useTableColumns } from '../hooks/useTableColumns';
|
|
|
53
53
|
import { initializeLiveRegion, announceSortChange } from '../utils/a11yUtils';
|
|
54
54
|
import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
|
|
55
55
|
import { getRowIdSafe } from '../utils/rowUtils';
|
|
56
|
+
import { createLogger } from '../../../utils/logger';
|
|
56
57
|
|
|
57
58
|
import { normalizeDataTableFeatures } from '../types';
|
|
58
59
|
import type {
|
|
@@ -216,13 +217,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
216
217
|
storageKey,
|
|
217
218
|
onLayoutChange,
|
|
218
219
|
}: DataTableCoreProps<TData>) {
|
|
219
|
-
|
|
220
|
-
console.log('[DataTable] 🚀 DataTableInternal RENDERED:', {
|
|
221
|
-
dataLength: data.length,
|
|
222
|
-
columnsCount: columns.length,
|
|
223
|
-
isLoading: externalIsLoading,
|
|
224
|
-
pageName: rbac?.pageName || rbac?.pageId,
|
|
225
|
-
});
|
|
220
|
+
const logger = React.useMemo(() => createLogger('DataTableCore'), []);
|
|
226
221
|
|
|
227
222
|
// ============================================================================
|
|
228
223
|
// ALL HOOKS MUST BE CALLED IN THE SAME ORDER EVERY RENDER
|
|
@@ -239,6 +234,17 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
239
234
|
// MANDATORY: Get permissions and secure features
|
|
240
235
|
const { permissions, secureFeatures, effectivePageId } = useDataTablePermissions(rbac, requestedFeatures);
|
|
241
236
|
|
|
237
|
+
// Debug logging for creation feature
|
|
238
|
+
if (import.meta.env.MODE === 'development') {
|
|
239
|
+
logger.debug('[DataTableCore] Creation feature check:', {
|
|
240
|
+
'incomingFeatures.creation': incomingFeatures.creation,
|
|
241
|
+
'requestedFeatures.creation': requestedFeatures.creation,
|
|
242
|
+
'secureFeatures.creation': secureFeatures.creation,
|
|
243
|
+
'permissions.canCreate.can': permissions.canCreate.can,
|
|
244
|
+
'onCreateRow prop provided': !!onCreateRow
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
242
248
|
// ============================================================================
|
|
243
249
|
// UNIFIED STATE MANAGEMENT - Use ONLY useDataTableState for all state
|
|
244
250
|
// ============================================================================
|
|
@@ -420,16 +426,15 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
420
426
|
});
|
|
421
427
|
|
|
422
428
|
useEffect(() => {
|
|
423
|
-
if (!hierarchicalValidation.isValid
|
|
424
|
-
|
|
429
|
+
if (!hierarchicalValidation.isValid) {
|
|
430
|
+
logger.error('Hierarchical data validation failed:', hierarchicalValidation.errors);
|
|
425
431
|
}
|
|
426
|
-
}, [hierarchicalValidation]);
|
|
432
|
+
}, [hierarchicalValidation, logger]);
|
|
427
433
|
|
|
428
|
-
//
|
|
429
|
-
// This helps diagnose the "record count shows but no rows" bug
|
|
434
|
+
// Diagnostic logging when finalTableData is empty but input data exists
|
|
430
435
|
useEffect(() => {
|
|
431
|
-
if (
|
|
432
|
-
|
|
436
|
+
if (finalTableData.length === 0 && data.length > 0) {
|
|
437
|
+
logger.warn('Diagnostic: finalTableData empty but input data exists', {
|
|
433
438
|
inputDataLength: data.length,
|
|
434
439
|
finalTableDataLength: finalTableData.length,
|
|
435
440
|
dataCount: finalDataCount,
|
|
@@ -440,7 +445,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
440
445
|
hierarchicalValid: hierarchicalValidation.isValid,
|
|
441
446
|
});
|
|
442
447
|
}
|
|
443
|
-
}, [finalTableData.length, data.length, finalDataCount, finalPaginationMode, serverData, secureFeatures.hierarchical, hierarchical, hierarchicalValidation.isValid]);
|
|
448
|
+
}, [finalTableData.length, data.length, finalDataCount, finalPaginationMode, serverData, secureFeatures.hierarchical, hierarchical, hierarchicalValidation.isValid, logger]);
|
|
444
449
|
|
|
445
450
|
const {
|
|
446
451
|
columnOrder: savedColumnOrder,
|
|
@@ -473,19 +478,17 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
473
478
|
useEffect(() => {
|
|
474
479
|
if (secureFeatures.selection && state.columnOrder.includes('select') && state.columnOrder[0] !== 'select') {
|
|
475
480
|
const normalizedOrder = ['select', ...state.columnOrder.filter(id => id !== 'select')];
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
});
|
|
481
|
-
}
|
|
481
|
+
logger.warn('Correcting column order state - moving select to first position:', {
|
|
482
|
+
before: state.columnOrder,
|
|
483
|
+
after: normalizedOrder
|
|
484
|
+
});
|
|
482
485
|
stateActions.setColumnOrder(normalizedOrder);
|
|
483
486
|
// Also update persisted order if persistence is enabled
|
|
484
487
|
if (secureFeatures.columnReordering) {
|
|
485
488
|
updateColumnOrder(normalizedOrder);
|
|
486
489
|
}
|
|
487
490
|
}
|
|
488
|
-
}, [secureFeatures.selection, secureFeatures.columnReordering, state.columnOrder, stateActions, updateColumnOrder]);
|
|
491
|
+
}, [secureFeatures.selection, secureFeatures.columnReordering, state.columnOrder, stateActions, updateColumnOrder, logger]);
|
|
489
492
|
|
|
490
493
|
// ============================================================================
|
|
491
494
|
// CONFIGURATION RESOLUTION - ALWAYS call these hooks
|
|
@@ -507,14 +510,12 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
507
510
|
Math.abs(curr - initialPageSize) < Math.abs(prev - initialPageSize) ? curr : prev
|
|
508
511
|
);
|
|
509
512
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
);
|
|
514
|
-
}
|
|
513
|
+
logger.warn(
|
|
514
|
+
`initialPageSize ${initialPageSize} is not available in page size options [${finalPageSizeOptions.join(', ')}]. Using closest option: ${closestOption}`
|
|
515
|
+
);
|
|
515
516
|
|
|
516
517
|
return closestOption;
|
|
517
|
-
}, [initialPageSize, finalPageSizeOptions, secureFeatures.pagination]);
|
|
518
|
+
}, [initialPageSize, finalPageSizeOptions, secureFeatures.pagination, logger]);
|
|
518
519
|
|
|
519
520
|
// Determine the effective pageSize to use (validated or current state)
|
|
520
521
|
// CRITICAL: This ensures we always pass a valid pageSize to TanStack Table configuration.
|
|
@@ -650,38 +651,76 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
650
651
|
// ============================================================================
|
|
651
652
|
|
|
652
653
|
// MANDATORY: Handlers are automatically secured
|
|
653
|
-
const secureHandlers = useMemo(() =>
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
654
|
+
const secureHandlers = useMemo(() => {
|
|
655
|
+
const handlers = {
|
|
656
|
+
onEditRow: permissions.canUpdate.can ? onEditRow : undefined,
|
|
657
|
+
onDeleteRow: permissions.canDelete.can ? onDeleteRow : undefined,
|
|
658
|
+
onCreateRow: permissions.canCreate.can ? onCreateRow : undefined,
|
|
659
|
+
onImport: permissions.canImport.can ? onImport : undefined,
|
|
660
|
+
onExport: permissions.canExport.can ? onExport : undefined,
|
|
661
|
+
onDeleteSelected: permissions.canDelete.can ? onDeleteSelected : undefined,
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
// Debug logging for creation handler
|
|
665
|
+
if (import.meta.env.MODE === 'development') {
|
|
666
|
+
logger.debug('[DataTableCore] Creation handler check:', {
|
|
667
|
+
'permissions.canCreate.can': permissions.canCreate.can,
|
|
668
|
+
'onCreateRow prop provided': !!onCreateRow,
|
|
669
|
+
'secureHandlers.onCreateRow': !!handlers.onCreateRow,
|
|
670
|
+
'secureFeatures.creation': secureFeatures.creation,
|
|
671
|
+
'will pass onCreateRow to toolbar': secureFeatures.creation && !!handlers.onCreateRow
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return handlers;
|
|
676
|
+
}, [permissions.canUpdate.can, permissions.canDelete.can, permissions.canCreate.can, permissions.canImport.can, permissions.canExport.can, onEditRow, onDeleteRow, onCreateRow, onImport, onExport, onDeleteSelected, secureFeatures.creation, logger]);
|
|
677
|
+
|
|
678
|
+
// Explicit debug log for creation button visibility (easier to spot)
|
|
679
|
+
if (import.meta.env.MODE === 'development') {
|
|
680
|
+
logger.debug('[DataTableCore] ⚠️ CREATION BUTTON DIAGNOSIS:', {
|
|
681
|
+
'✅ permissions.canCreate.can': permissions.canCreate.can,
|
|
682
|
+
'❓ features.creation enabled': secureFeatures.creation,
|
|
683
|
+
'❓ onCreateRow handler provided': !!onCreateRow,
|
|
684
|
+
'❓ secureHandlers.onCreateRow': !!secureHandlers.onCreateRow,
|
|
685
|
+
'🔍 WILL SHOW BUTTON': secureFeatures.creation && permissions.canCreate.can && !!onCreateRow,
|
|
686
|
+
'💡 SOLUTION': !secureFeatures.creation
|
|
687
|
+
? 'Enable features={{ creation: true }} in DataTable props'
|
|
688
|
+
: !onCreateRow
|
|
689
|
+
? 'Provide onCreateRow handler in DataTable props'
|
|
690
|
+
: 'All conditions met - button should be visible'
|
|
691
|
+
});
|
|
692
|
+
}
|
|
661
693
|
|
|
662
694
|
// MANDATORY: Process actions with RBAC checks
|
|
663
695
|
const effectiveActions = useMemo(() => {
|
|
664
696
|
// Create a new array to avoid mutating the original
|
|
665
697
|
const result = [...actions];
|
|
666
698
|
|
|
667
|
-
//
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
|
|
699
|
+
// Log action configuration in development
|
|
700
|
+
logger.debug('Action Configuration Debug:', {
|
|
701
|
+
originalActions: actions.length,
|
|
702
|
+
secureFeatures: {
|
|
703
|
+
editing: secureFeatures.editing,
|
|
704
|
+
deletion: secureFeatures.deletion,
|
|
705
|
+
creation: secureFeatures.creation,
|
|
706
|
+
import: secureFeatures.import,
|
|
707
|
+
export: secureFeatures.export
|
|
708
|
+
},
|
|
709
|
+
secureHandlers: {
|
|
710
|
+
onEditRow: !!secureHandlers.onEditRow,
|
|
711
|
+
onDeleteRow: !!secureHandlers.onDeleteRow,
|
|
712
|
+
onCreateRow: !!secureHandlers.onCreateRow,
|
|
713
|
+
onImport: !!secureHandlers.onImport,
|
|
714
|
+
onExport: !!secureHandlers.onExport
|
|
715
|
+
},
|
|
716
|
+
permissions: {
|
|
717
|
+
canUpdate: permissions.canUpdate.can,
|
|
718
|
+
canDelete: permissions.canDelete.can,
|
|
719
|
+
canCreate: permissions.canCreate.can,
|
|
720
|
+
canImport: permissions.canImport.can,
|
|
721
|
+
canExport: permissions.canExport.can
|
|
722
|
+
}
|
|
723
|
+
});
|
|
685
724
|
|
|
686
725
|
// Add Edit action with RBAC check
|
|
687
726
|
if (secureFeatures.editing && secureHandlers.onEditRow && !result.some(a => a.label === 'Edit')) {
|
|
@@ -730,7 +769,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
730
769
|
variant: "default"
|
|
731
770
|
});
|
|
732
771
|
} catch (error) {
|
|
733
|
-
|
|
772
|
+
logger.error('Delete error:', error);
|
|
734
773
|
toast({
|
|
735
774
|
title: "Delete Failed",
|
|
736
775
|
description: error instanceof Error ? error.message : 'Failed to delete row',
|
|
@@ -745,14 +784,12 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
745
784
|
});
|
|
746
785
|
}
|
|
747
786
|
|
|
748
|
-
//
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
});
|
|
755
|
-
}
|
|
787
|
+
// Log final actions in development
|
|
788
|
+
logger.debug('Final Actions:', {
|
|
789
|
+
totalActions: result.length,
|
|
790
|
+
actionLabels: result.map(a => a.label),
|
|
791
|
+
hiddenActions: result.filter(a => a.hidden).map(a => a.label)
|
|
792
|
+
});
|
|
756
793
|
|
|
757
794
|
return result;
|
|
758
795
|
}, [actions, secureFeatures, permissions, secureHandlers, resolvedGetRowId, stateActions, data]);
|
|
@@ -785,16 +822,14 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
785
822
|
? ['select', ...state.columnOrder.filter(id => id !== 'select')]
|
|
786
823
|
: state.columnOrder;
|
|
787
824
|
|
|
788
|
-
//
|
|
789
|
-
if (
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
});
|
|
797
|
-
}
|
|
825
|
+
// Log column order normalization in development
|
|
826
|
+
if (secureFeatures.selection && state.columnOrder[0] !== 'select') {
|
|
827
|
+
logger.warn('Column order normalized:', {
|
|
828
|
+
original: state.columnOrder,
|
|
829
|
+
normalized: normalizedColumnOrder,
|
|
830
|
+
firstColumnOriginal: state.columnOrder[0],
|
|
831
|
+
firstColumnNormalized: normalizedColumnOrder[0]
|
|
832
|
+
});
|
|
798
833
|
}
|
|
799
834
|
|
|
800
835
|
return {
|
|
@@ -848,59 +883,21 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
848
883
|
hasServerSideConfig: !!serverSide,
|
|
849
884
|
});
|
|
850
885
|
|
|
851
|
-
// CRITICAL DEBUG: Always log table configuration state
|
|
852
|
-
useEffect(() => {
|
|
853
|
-
console.log('[DataTable] 🔍 Table Config Created:', {
|
|
854
|
-
finalTableDataLength: finalTableData.length,
|
|
855
|
-
configDataLength: tableConfig.data?.length || 0,
|
|
856
|
-
columnsCount: enhancedColumns.length,
|
|
857
|
-
finalPaginationMode,
|
|
858
|
-
hasServerSideConfig: !!serverSide,
|
|
859
|
-
manualPagination: tableConfig.manualPagination,
|
|
860
|
-
hasGetPaginationRowModel: !!tableConfig.getPaginationRowModel,
|
|
861
|
-
effectivePageSize,
|
|
862
|
-
paginationState: tableStateSnapshot.pagination,
|
|
863
|
-
});
|
|
864
|
-
}, [finalTableData.length, tableConfig.data, tableConfig.manualPagination, tableConfig.getPaginationRowModel, finalPaginationMode, serverSide, effectivePageSize, tableStateSnapshot.pagination, enhancedColumns.length]);
|
|
865
886
|
|
|
866
887
|
const table = useReactTable(tableConfig);
|
|
867
888
|
|
|
868
|
-
//
|
|
889
|
+
// Diagnostic logging when table is created but rows are empty
|
|
869
890
|
useEffect(() => {
|
|
870
891
|
const rows = table.getRowModel().rows;
|
|
871
|
-
const coreRows = table.getCoreRowModel().rows;
|
|
872
|
-
const prePaginationRows = table.getPrePaginationRowModel?.()?.rows || [];
|
|
873
|
-
const filteredRows = table.getFilteredRowModel?.()?.rows || [];
|
|
874
|
-
const sortedRows = table.getSortedRowModel?.()?.rows || [];
|
|
875
|
-
const tableState = table.getState();
|
|
876
|
-
|
|
877
|
-
console.log('[DataTable] 🔍 Table Created - Row Counts:', {
|
|
878
|
-
finalTableDataLength: finalTableData.length,
|
|
879
|
-
tableConfigDataLength: tableConfig.data?.length || 0,
|
|
880
|
-
coreRowsLength: coreRows.length,
|
|
881
|
-
prePaginationRowsLength: prePaginationRows.length,
|
|
882
|
-
filteredRowsLength: filteredRows.length,
|
|
883
|
-
sortedRowsLength: sortedRows.length,
|
|
884
|
-
finalRowsLength: rows.length,
|
|
885
|
-
pagination: tableState.pagination,
|
|
886
|
-
finalPaginationMode,
|
|
887
|
-
hasServerSideConfig: !!serverSide,
|
|
888
|
-
tableOptions: {
|
|
889
|
-
getCoreRowModel: !!tableConfig.getCoreRowModel,
|
|
890
|
-
getFilteredRowModel: !!tableConfig.getFilteredRowModel,
|
|
891
|
-
getSortedRowModel: !!tableConfig.getSortedRowModel,
|
|
892
|
-
getPaginationRowModel: !!tableConfig.getPaginationRowModel,
|
|
893
|
-
manualPagination: tableConfig.manualPagination,
|
|
894
|
-
manualFiltering: tableConfig.manualFiltering,
|
|
895
|
-
manualSorting: tableConfig.manualSorting,
|
|
896
|
-
pageCount: tableConfig.pageCount,
|
|
897
|
-
},
|
|
898
|
-
hasRowId: !!resolvedGetRowId,
|
|
899
|
-
effectivePageSize,
|
|
900
|
-
});
|
|
901
892
|
|
|
902
893
|
if (rows.length === 0 && finalTableData.length > 0) {
|
|
903
|
-
|
|
894
|
+
const coreRows = table.getCoreRowModel().rows;
|
|
895
|
+
const prePaginationRows = table.getPrePaginationRowModel?.()?.rows || [];
|
|
896
|
+
const filteredRows = table.getFilteredRowModel?.()?.rows || [];
|
|
897
|
+
const sortedRows = table.getSortedRowModel?.()?.rows || [];
|
|
898
|
+
const tableState = table.getState();
|
|
899
|
+
|
|
900
|
+
logger.warn('Table created but rows are empty!', {
|
|
904
901
|
finalTableDataLength: finalTableData.length,
|
|
905
902
|
tableConfigDataLength: tableConfig.data?.length || 0,
|
|
906
903
|
coreRowsLength: coreRows.length,
|
|
@@ -925,7 +922,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
925
922
|
effectivePageSize,
|
|
926
923
|
});
|
|
927
924
|
}
|
|
928
|
-
}, [table, finalTableData.length, tableConfig, resolvedGetRowId, finalPaginationMode, serverSide, effectivePageSize]);
|
|
925
|
+
}, [table, finalTableData.length, tableConfig, resolvedGetRowId, finalPaginationMode, serverSide, effectivePageSize, logger]);
|
|
929
926
|
|
|
930
927
|
// ============================================================================
|
|
931
928
|
// RBAC VALIDATION AND EARLY RETURNS - AFTER ALL HOOKS
|
|
@@ -938,36 +935,26 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
938
935
|
|
|
939
936
|
// Wait for permission check to complete before making access decisions
|
|
940
937
|
if (permissions.canRead.isLoading) {
|
|
941
|
-
console.log('[DataTable] ⏳ Permission check in progress - showing loading state');
|
|
942
938
|
return <LoadingComponent />;
|
|
943
939
|
}
|
|
944
940
|
|
|
945
941
|
// MANDATORY: No data access without read permission (only check after loading completes)
|
|
946
942
|
if (!permissions.canRead.can) {
|
|
947
|
-
|
|
943
|
+
logger.warn('Access denied - no read permission:', {
|
|
948
944
|
canRead: permissions.canRead,
|
|
949
945
|
pageId: effectivePageId,
|
|
950
946
|
isLoading: permissions.canRead.isLoading,
|
|
951
947
|
});
|
|
952
948
|
return <AccessDeniedPage resource={effectivePageId || 'unknown-page'} operation="read" />;
|
|
953
949
|
}
|
|
954
|
-
|
|
955
|
-
console.log('[DataTable] ✅ Permission check passed - rendering table');
|
|
956
950
|
|
|
957
951
|
// ============================================================================
|
|
958
952
|
// RENDER
|
|
959
953
|
// ============================================================================
|
|
960
954
|
|
|
961
955
|
if (isLoading) {
|
|
962
|
-
console.log('[DataTable] ⏳ Loading state - showing loading component');
|
|
963
956
|
return <LoadingComponent />;
|
|
964
957
|
}
|
|
965
|
-
|
|
966
|
-
console.log('[DataTable] 📊 About to render table with data:', {
|
|
967
|
-
finalTableDataLength: finalTableData.length,
|
|
968
|
-
tableExists: !!table,
|
|
969
|
-
canGetRows: !!table?.getRowModel,
|
|
970
|
-
});
|
|
971
958
|
|
|
972
959
|
const PaginationComponent = enhancedPagination || finalPaginationMode !== 'client'
|
|
973
960
|
? EnhancedPaginationControls
|
|
@@ -1104,11 +1091,9 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1104
1091
|
variant: "default"
|
|
1105
1092
|
});
|
|
1106
1093
|
|
|
1107
|
-
|
|
1108
|
-
console.log('DataTable: Export completed successfully');
|
|
1109
|
-
}
|
|
1094
|
+
logger.debug('Export completed successfully');
|
|
1110
1095
|
} catch (error) {
|
|
1111
|
-
|
|
1096
|
+
logger.error('Failed to export data:', error);
|
|
1112
1097
|
|
|
1113
1098
|
// Show error toast notification to user
|
|
1114
1099
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
@@ -1117,10 +1102,6 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1117
1102
|
description: `Failed to export data: ${errorMessage}`,
|
|
1118
1103
|
variant: "destructive"
|
|
1119
1104
|
});
|
|
1120
|
-
|
|
1121
|
-
if (import.meta.env.MODE === 'development') {
|
|
1122
|
-
console.error('DataTable: Export error details:', error);
|
|
1123
|
-
}
|
|
1124
1105
|
}
|
|
1125
1106
|
})}
|
|
1126
1107
|
rowSelection={rowSelection}
|
|
@@ -1146,7 +1127,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1146
1127
|
variant: "default"
|
|
1147
1128
|
});
|
|
1148
1129
|
} catch (error) {
|
|
1149
|
-
|
|
1130
|
+
logger.error('Bulk delete error:', error);
|
|
1150
1131
|
toast({
|
|
1151
1132
|
title: "Delete Failed",
|
|
1152
1133
|
description: error instanceof Error ? error.message : 'Failed to delete selected rows',
|
|
@@ -1380,12 +1361,12 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1380
1361
|
onImport={async (data: TData[]) => {
|
|
1381
1362
|
if (onImport) {
|
|
1382
1363
|
try {
|
|
1383
|
-
|
|
1364
|
+
logger.debug('onImport called with', data.length, 'rows');
|
|
1384
1365
|
const result = onImport(data);
|
|
1385
1366
|
if (result && typeof result.then === 'function') {
|
|
1386
1367
|
await result;
|
|
1387
1368
|
}
|
|
1388
|
-
|
|
1369
|
+
logger.debug('onImport completed successfully');
|
|
1389
1370
|
|
|
1390
1371
|
// Show success toast
|
|
1391
1372
|
// NOTE: Toast notifications use default timeout (5 seconds) - do not set duration property
|
|
@@ -1395,7 +1376,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1395
1376
|
variant: "default"
|
|
1396
1377
|
});
|
|
1397
1378
|
} catch (error) {
|
|
1398
|
-
|
|
1379
|
+
logger.error('Import error:', error);
|
|
1399
1380
|
toast({
|
|
1400
1381
|
title: "Import Failed",
|
|
1401
1382
|
description: error instanceof Error ? error.message : 'Failed to import data',
|
|
@@ -1405,7 +1386,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1405
1386
|
return;
|
|
1406
1387
|
}
|
|
1407
1388
|
} else {
|
|
1408
|
-
|
|
1389
|
+
logger.error('onImport handler not provided');
|
|
1409
1390
|
toast({
|
|
1410
1391
|
title: "Import Not Configured",
|
|
1411
1392
|
description: "Import functionality requires an onImport handler to be provided.",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import React, { useEffect } from 'react';
|
|
25
25
|
import { ImportModal, type ImportModalConfig } from './ImportModal';
|
|
26
|
+
import { createLogger } from '../../../utils/logger';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Maps CSV column data to table column structure
|
|
@@ -37,11 +38,12 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
|
|
|
37
38
|
editAccessorKey?: string;
|
|
38
39
|
}>
|
|
39
40
|
): TData[] {
|
|
41
|
+
const logger = createLogger('mapCSVToTableColumns');
|
|
40
42
|
// Create a mapping from CSV headers to table field names
|
|
41
43
|
// Priority: editAccessorKey > accessorKey > id
|
|
42
44
|
const columnMap = new Map<string, string>();
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
logger.debug('Building column map from', columns.length, 'column definitions');
|
|
45
47
|
|
|
46
48
|
columns.forEach(col => {
|
|
47
49
|
const fieldName = col.editAccessorKey || col.accessorKey || col.id;
|
|
@@ -51,13 +53,13 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
|
|
|
51
53
|
const headerLower = header.toLowerCase();
|
|
52
54
|
// Map header to field name (case-insensitive)
|
|
53
55
|
columnMap.set(headerLower, fieldName);
|
|
54
|
-
|
|
56
|
+
logger.debug(`Mapped "${header}" -> "${fieldName}"`);
|
|
55
57
|
|
|
56
58
|
// Also map id/accessorKey if different from header
|
|
57
59
|
const colId = col.id || col.accessorKey;
|
|
58
60
|
if (colId && colId !== header && colId !== fieldName) {
|
|
59
61
|
columnMap.set(colId.toLowerCase(), fieldName);
|
|
60
|
-
|
|
62
|
+
logger.debug(`Also mapped "${colId}" -> "${fieldName}"`);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
// For reference fields with editAccessorKey, also map the ID column header
|
|
@@ -66,24 +68,24 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
|
|
|
66
68
|
const editAccessorKey = col.editAccessorKey; // Store in const for proper type narrowing
|
|
67
69
|
const idColumnHeader = `${header} (ID)`;
|
|
68
70
|
columnMap.set(idColumnHeader.toLowerCase(), editAccessorKey);
|
|
69
|
-
|
|
71
|
+
logger.debug(`Mapped ID column "${idColumnHeader}" -> "${editAccessorKey}"`);
|
|
70
72
|
// Also map the editAccessorKey directly (in case CSV uses the field name)
|
|
71
73
|
columnMap.set(editAccessorKey.toLowerCase(), editAccessorKey);
|
|
72
|
-
|
|
74
|
+
logger.debug(`Also mapped "${editAccessorKey}" -> "${editAccessorKey}"`);
|
|
73
75
|
}
|
|
74
76
|
} else {
|
|
75
|
-
|
|
77
|
+
logger.warn('Skipping column with missing fieldName or header:', col);
|
|
76
78
|
}
|
|
77
79
|
});
|
|
78
80
|
|
|
79
81
|
if (csvData.length === 0) {
|
|
80
|
-
|
|
82
|
+
logger.warn('No CSV data to map');
|
|
81
83
|
return [];
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
const csvHeaders = Object.keys(csvData[0]);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
logger.debug('CSV headers found:', csvHeaders);
|
|
88
|
+
logger.debug('Column map size:', columnMap.size);
|
|
87
89
|
|
|
88
90
|
// Transform CSV data using the mapping
|
|
89
91
|
const mappedData = csvData.map((row, index) => {
|
|
@@ -107,7 +109,7 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
|
|
|
107
109
|
if (csvHeaderLower === mapKey || csvHeaderLower.endsWith(keyWithSpace)) {
|
|
108
110
|
fieldName = mapValue;
|
|
109
111
|
if (index === 0) {
|
|
110
|
-
|
|
112
|
+
logger.debug(`Flexible match: "${csvHeader}" -> "${mapValue}" (matched "${mapKey}")`);
|
|
111
113
|
}
|
|
112
114
|
break; // Found a match, stop searching
|
|
113
115
|
}
|
|
@@ -117,13 +119,13 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
|
|
|
117
119
|
if (fieldName) {
|
|
118
120
|
mappedRow[fieldName] = row[csvHeader];
|
|
119
121
|
if (index === 0) {
|
|
120
|
-
|
|
122
|
+
logger.debug(`Row 0: "${csvHeader}" -> "${fieldName}"`);
|
|
121
123
|
}
|
|
122
124
|
} else {
|
|
123
125
|
// If no mapping found, use the CSV header as-is (lowercase)
|
|
124
126
|
mappedRow[csvHeaderLower] = row[csvHeader];
|
|
125
127
|
if (index === 0) {
|
|
126
|
-
|
|
128
|
+
logger.warn(`No mapping found for "${csvHeader}", using as-is`);
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
});
|
|
@@ -131,9 +133,9 @@ function mapCSVToTableColumns<TData extends Record<string, unknown> = Record<str
|
|
|
131
133
|
return mappedRow as TData;
|
|
132
134
|
});
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
logger.debug('Mapped', mappedData.length, 'rows');
|
|
135
137
|
if (mappedData.length > 0) {
|
|
136
|
-
|
|
138
|
+
logger.debug('First mapped row keys:', Object.keys(mappedData[0]));
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
return mappedData;
|
|
@@ -203,6 +205,7 @@ export function DataTableModals<TData extends Record<string, unknown> = Record<s
|
|
|
203
205
|
onStoreFocus,
|
|
204
206
|
onRestoreFocus,
|
|
205
207
|
}: DataTableModalsProps<TData>) {
|
|
208
|
+
const logger = React.useMemo(() => createLogger('DataTableModals'), []);
|
|
206
209
|
// Handle focus management for import modal
|
|
207
210
|
useEffect(() => {
|
|
208
211
|
if (showImportModal) {
|
|
@@ -227,28 +230,28 @@ export function DataTableModals<TData extends Record<string, unknown> = Record<s
|
|
|
227
230
|
// Automatically map CSV columns to table columns based on column definitions
|
|
228
231
|
let mappedData: TData[];
|
|
229
232
|
if (columns && columns.length > 0) {
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
logger.debug('Mapping CSV data with', columns.length, 'column definitions');
|
|
234
|
+
logger.debug('Raw CSV headers:', rawData.length > 0 ? Object.keys(rawData[0]) : []);
|
|
232
235
|
mappedData = mapCSVToTableColumns<TData>(rawData, columns);
|
|
233
|
-
|
|
236
|
+
logger.debug('Mapped data sample:', mappedData.length > 0 ? mappedData[0] : null);
|
|
234
237
|
} else {
|
|
235
|
-
|
|
238
|
+
logger.warn('No columns provided for mapping, using raw data');
|
|
236
239
|
mappedData = rawData as TData[];
|
|
237
240
|
}
|
|
238
241
|
|
|
239
242
|
if (!onImport) {
|
|
240
|
-
|
|
243
|
+
logger.error('onImport callback is not provided');
|
|
241
244
|
throw new Error('Import handler is not configured. Please provide an onImport callback.');
|
|
242
245
|
}
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
logger.debug('Calling onImport with', mappedData.length, 'rows');
|
|
245
248
|
const result = onImport(mappedData);
|
|
246
249
|
if (result && typeof result.then === 'function') {
|
|
247
250
|
await result;
|
|
248
251
|
}
|
|
249
|
-
|
|
252
|
+
logger.debug('Import completed successfully');
|
|
250
253
|
} catch (error) {
|
|
251
|
-
|
|
254
|
+
logger.error('Import error:', error);
|
|
252
255
|
throw error; // Re-throw to let ImportModal handle it
|
|
253
256
|
}
|
|
254
257
|
}}
|
|
@@ -212,7 +212,20 @@ export function DataTableToolbar<TData extends DataRecord>({
|
|
|
212
212
|
)}
|
|
213
213
|
|
|
214
214
|
{/* Row Creation - ONLY if user has create permission */}
|
|
215
|
-
{
|
|
215
|
+
{(() => {
|
|
216
|
+
// Debug logging for creation button visibility
|
|
217
|
+
if (import.meta.env.MODE === 'development') {
|
|
218
|
+
const logger = console;
|
|
219
|
+
logger.debug('[DataTableToolbar] Creation button check:', {
|
|
220
|
+
'features.creation': features.creation,
|
|
221
|
+
'permissions.canCreate.can': permissions.canCreate.can,
|
|
222
|
+
'onCreateRow provided': !!onCreateRow,
|
|
223
|
+
'will show button': features.creation && permissions.canCreate.can && onCreateRow
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return features.creation && permissions.canCreate.can && onCreateRow;
|
|
228
|
+
})() && (
|
|
216
229
|
<Button
|
|
217
230
|
variant="outline"
|
|
218
231
|
onClick={onCreateRow}
|