@jmruthers/pace-core 0.5.120 → 0.5.123
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-WTS4IRF2.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.map → chunk-CX5M4ZAG.js.map} +1 -1
- package/dist/{chunk-HFBOFZ3Z.js → chunk-DHMFMXFV.js} +258 -243
- package/dist/chunk-DHMFMXFV.js.map +1 -0
- 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-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-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/{chunk-QPI2CCBA.js → chunk-ZPJMYGEP.js} +149 -96
- package/dist/chunk-ZPJMYGEP.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 +66 -136
- package/src/components/DataTable/components/DataTableModals.tsx +25 -22
- 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/DataTable/utils/flexibleImport.ts +27 -6
- 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/types/database.generated.ts +1515 -424
- package/src/utils/formatDate.test.ts +11 -11
- package/src/utils/formatting.ts +3 -2
- package/dist/chunk-BHWIUEYH.js.map +0 -1
- package/dist/chunk-FKFHZUGF.js.map +0 -1
- package/dist/chunk-GZRXOUBE.js.map +0 -1
- package/dist/chunk-HFBOFZ3Z.js.map +0 -1
- package/dist/chunk-QPI2CCBA.js.map +0 -1
- package/dist/chunk-SMJZMKYN.js.map +0 -1
- package/dist/chunk-TDNI6ZWL.js.map +0 -1
- package/src/styles/semantic.css +0 -24
- /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-WTS4IRF2.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.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.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
|
|
@@ -420,16 +415,15 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
420
415
|
});
|
|
421
416
|
|
|
422
417
|
useEffect(() => {
|
|
423
|
-
if (!hierarchicalValidation.isValid
|
|
424
|
-
|
|
418
|
+
if (!hierarchicalValidation.isValid) {
|
|
419
|
+
logger.error('Hierarchical data validation failed:', hierarchicalValidation.errors);
|
|
425
420
|
}
|
|
426
|
-
}, [hierarchicalValidation]);
|
|
421
|
+
}, [hierarchicalValidation, logger]);
|
|
427
422
|
|
|
428
|
-
//
|
|
429
|
-
// This helps diagnose the "record count shows but no rows" bug
|
|
423
|
+
// Diagnostic logging when finalTableData is empty but input data exists
|
|
430
424
|
useEffect(() => {
|
|
431
|
-
if (
|
|
432
|
-
|
|
425
|
+
if (finalTableData.length === 0 && data.length > 0) {
|
|
426
|
+
logger.warn('Diagnostic: finalTableData empty but input data exists', {
|
|
433
427
|
inputDataLength: data.length,
|
|
434
428
|
finalTableDataLength: finalTableData.length,
|
|
435
429
|
dataCount: finalDataCount,
|
|
@@ -440,7 +434,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
440
434
|
hierarchicalValid: hierarchicalValidation.isValid,
|
|
441
435
|
});
|
|
442
436
|
}
|
|
443
|
-
}, [finalTableData.length, data.length, finalDataCount, finalPaginationMode, serverData, secureFeatures.hierarchical, hierarchical, hierarchicalValidation.isValid]);
|
|
437
|
+
}, [finalTableData.length, data.length, finalDataCount, finalPaginationMode, serverData, secureFeatures.hierarchical, hierarchical, hierarchicalValidation.isValid, logger]);
|
|
444
438
|
|
|
445
439
|
const {
|
|
446
440
|
columnOrder: savedColumnOrder,
|
|
@@ -473,19 +467,17 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
473
467
|
useEffect(() => {
|
|
474
468
|
if (secureFeatures.selection && state.columnOrder.includes('select') && state.columnOrder[0] !== 'select') {
|
|
475
469
|
const normalizedOrder = ['select', ...state.columnOrder.filter(id => id !== 'select')];
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
});
|
|
481
|
-
}
|
|
470
|
+
logger.warn('Correcting column order state - moving select to first position:', {
|
|
471
|
+
before: state.columnOrder,
|
|
472
|
+
after: normalizedOrder
|
|
473
|
+
});
|
|
482
474
|
stateActions.setColumnOrder(normalizedOrder);
|
|
483
475
|
// Also update persisted order if persistence is enabled
|
|
484
476
|
if (secureFeatures.columnReordering) {
|
|
485
477
|
updateColumnOrder(normalizedOrder);
|
|
486
478
|
}
|
|
487
479
|
}
|
|
488
|
-
}, [secureFeatures.selection, secureFeatures.columnReordering, state.columnOrder, stateActions, updateColumnOrder]);
|
|
480
|
+
}, [secureFeatures.selection, secureFeatures.columnReordering, state.columnOrder, stateActions, updateColumnOrder, logger]);
|
|
489
481
|
|
|
490
482
|
// ============================================================================
|
|
491
483
|
// CONFIGURATION RESOLUTION - ALWAYS call these hooks
|
|
@@ -507,14 +499,12 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
507
499
|
Math.abs(curr - initialPageSize) < Math.abs(prev - initialPageSize) ? curr : prev
|
|
508
500
|
);
|
|
509
501
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
);
|
|
514
|
-
}
|
|
502
|
+
logger.warn(
|
|
503
|
+
`initialPageSize ${initialPageSize} is not available in page size options [${finalPageSizeOptions.join(', ')}]. Using closest option: ${closestOption}`
|
|
504
|
+
);
|
|
515
505
|
|
|
516
506
|
return closestOption;
|
|
517
|
-
}, [initialPageSize, finalPageSizeOptions, secureFeatures.pagination]);
|
|
507
|
+
}, [initialPageSize, finalPageSizeOptions, secureFeatures.pagination, logger]);
|
|
518
508
|
|
|
519
509
|
// Determine the effective pageSize to use (validated or current state)
|
|
520
510
|
// CRITICAL: This ensures we always pass a valid pageSize to TanStack Table configuration.
|
|
@@ -664,24 +654,22 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
664
654
|
// Create a new array to avoid mutating the original
|
|
665
655
|
const result = [...actions];
|
|
666
656
|
|
|
667
|
-
//
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
});
|
|
684
|
-
}
|
|
657
|
+
// Log action configuration in development
|
|
658
|
+
logger.debug('Action Configuration Debug:', {
|
|
659
|
+
originalActions: actions.length,
|
|
660
|
+
secureFeatures: {
|
|
661
|
+
editing: secureFeatures.editing,
|
|
662
|
+
deletion: secureFeatures.deletion
|
|
663
|
+
},
|
|
664
|
+
secureHandlers: {
|
|
665
|
+
onEditRow: !!secureHandlers.onEditRow,
|
|
666
|
+
onDeleteRow: !!secureHandlers.onDeleteRow
|
|
667
|
+
},
|
|
668
|
+
permissions: {
|
|
669
|
+
canUpdate: permissions.canUpdate.can,
|
|
670
|
+
canDelete: permissions.canDelete.can
|
|
671
|
+
}
|
|
672
|
+
});
|
|
685
673
|
|
|
686
674
|
// Add Edit action with RBAC check
|
|
687
675
|
if (secureFeatures.editing && secureHandlers.onEditRow && !result.some(a => a.label === 'Edit')) {
|
|
@@ -730,7 +718,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
730
718
|
variant: "default"
|
|
731
719
|
});
|
|
732
720
|
} catch (error) {
|
|
733
|
-
|
|
721
|
+
logger.error('Delete error:', error);
|
|
734
722
|
toast({
|
|
735
723
|
title: "Delete Failed",
|
|
736
724
|
description: error instanceof Error ? error.message : 'Failed to delete row',
|
|
@@ -745,14 +733,12 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
745
733
|
});
|
|
746
734
|
}
|
|
747
735
|
|
|
748
|
-
//
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
});
|
|
755
|
-
}
|
|
736
|
+
// Log final actions in development
|
|
737
|
+
logger.debug('Final Actions:', {
|
|
738
|
+
totalActions: result.length,
|
|
739
|
+
actionLabels: result.map(a => a.label),
|
|
740
|
+
hiddenActions: result.filter(a => a.hidden).map(a => a.label)
|
|
741
|
+
});
|
|
756
742
|
|
|
757
743
|
return result;
|
|
758
744
|
}, [actions, secureFeatures, permissions, secureHandlers, resolvedGetRowId, stateActions, data]);
|
|
@@ -785,16 +771,14 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
785
771
|
? ['select', ...state.columnOrder.filter(id => id !== 'select')]
|
|
786
772
|
: state.columnOrder;
|
|
787
773
|
|
|
788
|
-
//
|
|
789
|
-
if (
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
});
|
|
797
|
-
}
|
|
774
|
+
// Log column order normalization in development
|
|
775
|
+
if (secureFeatures.selection && state.columnOrder[0] !== 'select') {
|
|
776
|
+
logger.warn('Column order normalized:', {
|
|
777
|
+
original: state.columnOrder,
|
|
778
|
+
normalized: normalizedColumnOrder,
|
|
779
|
+
firstColumnOriginal: state.columnOrder[0],
|
|
780
|
+
firstColumnNormalized: normalizedColumnOrder[0]
|
|
781
|
+
});
|
|
798
782
|
}
|
|
799
783
|
|
|
800
784
|
return {
|
|
@@ -848,59 +832,21 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
848
832
|
hasServerSideConfig: !!serverSide,
|
|
849
833
|
});
|
|
850
834
|
|
|
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
835
|
|
|
866
836
|
const table = useReactTable(tableConfig);
|
|
867
837
|
|
|
868
|
-
//
|
|
838
|
+
// Diagnostic logging when table is created but rows are empty
|
|
869
839
|
useEffect(() => {
|
|
870
840
|
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
841
|
|
|
902
842
|
if (rows.length === 0 && finalTableData.length > 0) {
|
|
903
|
-
|
|
843
|
+
const coreRows = table.getCoreRowModel().rows;
|
|
844
|
+
const prePaginationRows = table.getPrePaginationRowModel?.()?.rows || [];
|
|
845
|
+
const filteredRows = table.getFilteredRowModel?.()?.rows || [];
|
|
846
|
+
const sortedRows = table.getSortedRowModel?.()?.rows || [];
|
|
847
|
+
const tableState = table.getState();
|
|
848
|
+
|
|
849
|
+
logger.warn('Table created but rows are empty!', {
|
|
904
850
|
finalTableDataLength: finalTableData.length,
|
|
905
851
|
tableConfigDataLength: tableConfig.data?.length || 0,
|
|
906
852
|
coreRowsLength: coreRows.length,
|
|
@@ -925,7 +871,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
925
871
|
effectivePageSize,
|
|
926
872
|
});
|
|
927
873
|
}
|
|
928
|
-
}, [table, finalTableData.length, tableConfig, resolvedGetRowId, finalPaginationMode, serverSide, effectivePageSize]);
|
|
874
|
+
}, [table, finalTableData.length, tableConfig, resolvedGetRowId, finalPaginationMode, serverSide, effectivePageSize, logger]);
|
|
929
875
|
|
|
930
876
|
// ============================================================================
|
|
931
877
|
// RBAC VALIDATION AND EARLY RETURNS - AFTER ALL HOOKS
|
|
@@ -938,36 +884,26 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
938
884
|
|
|
939
885
|
// Wait for permission check to complete before making access decisions
|
|
940
886
|
if (permissions.canRead.isLoading) {
|
|
941
|
-
console.log('[DataTable] ⏳ Permission check in progress - showing loading state');
|
|
942
887
|
return <LoadingComponent />;
|
|
943
888
|
}
|
|
944
889
|
|
|
945
890
|
// MANDATORY: No data access without read permission (only check after loading completes)
|
|
946
891
|
if (!permissions.canRead.can) {
|
|
947
|
-
|
|
892
|
+
logger.warn('Access denied - no read permission:', {
|
|
948
893
|
canRead: permissions.canRead,
|
|
949
894
|
pageId: effectivePageId,
|
|
950
895
|
isLoading: permissions.canRead.isLoading,
|
|
951
896
|
});
|
|
952
897
|
return <AccessDeniedPage resource={effectivePageId || 'unknown-page'} operation="read" />;
|
|
953
898
|
}
|
|
954
|
-
|
|
955
|
-
console.log('[DataTable] ✅ Permission check passed - rendering table');
|
|
956
899
|
|
|
957
900
|
// ============================================================================
|
|
958
901
|
// RENDER
|
|
959
902
|
// ============================================================================
|
|
960
903
|
|
|
961
904
|
if (isLoading) {
|
|
962
|
-
console.log('[DataTable] ⏳ Loading state - showing loading component');
|
|
963
905
|
return <LoadingComponent />;
|
|
964
906
|
}
|
|
965
|
-
|
|
966
|
-
console.log('[DataTable] 📊 About to render table with data:', {
|
|
967
|
-
finalTableDataLength: finalTableData.length,
|
|
968
|
-
tableExists: !!table,
|
|
969
|
-
canGetRows: !!table?.getRowModel,
|
|
970
|
-
});
|
|
971
907
|
|
|
972
908
|
const PaginationComponent = enhancedPagination || finalPaginationMode !== 'client'
|
|
973
909
|
? EnhancedPaginationControls
|
|
@@ -1104,11 +1040,9 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1104
1040
|
variant: "default"
|
|
1105
1041
|
});
|
|
1106
1042
|
|
|
1107
|
-
|
|
1108
|
-
console.log('DataTable: Export completed successfully');
|
|
1109
|
-
}
|
|
1043
|
+
logger.debug('Export completed successfully');
|
|
1110
1044
|
} catch (error) {
|
|
1111
|
-
|
|
1045
|
+
logger.error('Failed to export data:', error);
|
|
1112
1046
|
|
|
1113
1047
|
// Show error toast notification to user
|
|
1114
1048
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
@@ -1117,10 +1051,6 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1117
1051
|
description: `Failed to export data: ${errorMessage}`,
|
|
1118
1052
|
variant: "destructive"
|
|
1119
1053
|
});
|
|
1120
|
-
|
|
1121
|
-
if (import.meta.env.MODE === 'development') {
|
|
1122
|
-
console.error('DataTable: Export error details:', error);
|
|
1123
|
-
}
|
|
1124
1054
|
}
|
|
1125
1055
|
})}
|
|
1126
1056
|
rowSelection={rowSelection}
|
|
@@ -1146,7 +1076,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1146
1076
|
variant: "default"
|
|
1147
1077
|
});
|
|
1148
1078
|
} catch (error) {
|
|
1149
|
-
|
|
1079
|
+
logger.error('Bulk delete error:', error);
|
|
1150
1080
|
toast({
|
|
1151
1081
|
title: "Delete Failed",
|
|
1152
1082
|
description: error instanceof Error ? error.message : 'Failed to delete selected rows',
|
|
@@ -1380,12 +1310,12 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1380
1310
|
onImport={async (data: TData[]) => {
|
|
1381
1311
|
if (onImport) {
|
|
1382
1312
|
try {
|
|
1383
|
-
|
|
1313
|
+
logger.debug('onImport called with', data.length, 'rows');
|
|
1384
1314
|
const result = onImport(data);
|
|
1385
1315
|
if (result && typeof result.then === 'function') {
|
|
1386
1316
|
await result;
|
|
1387
1317
|
}
|
|
1388
|
-
|
|
1318
|
+
logger.debug('onImport completed successfully');
|
|
1389
1319
|
|
|
1390
1320
|
// Show success toast
|
|
1391
1321
|
// NOTE: Toast notifications use default timeout (5 seconds) - do not set duration property
|
|
@@ -1395,7 +1325,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1395
1325
|
variant: "default"
|
|
1396
1326
|
});
|
|
1397
1327
|
} catch (error) {
|
|
1398
|
-
|
|
1328
|
+
logger.error('Import error:', error);
|
|
1399
1329
|
toast({
|
|
1400
1330
|
title: "Import Failed",
|
|
1401
1331
|
description: error instanceof Error ? error.message : 'Failed to import data',
|
|
@@ -1405,7 +1335,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
1405
1335
|
return;
|
|
1406
1336
|
}
|
|
1407
1337
|
} else {
|
|
1408
|
-
|
|
1338
|
+
logger.error('onImport handler not provided');
|
|
1409
1339
|
toast({
|
|
1410
1340
|
title: "Import Not Configured",
|
|
1411
1341
|
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
|
}}
|