@jmruthers/pace-core 0.5.93 → 0.5.95

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 (170) hide show
  1. package/dist/{DataTable-HC5S4RKB.js → DataTable-XENXNMCP.js} +6 -6
  2. package/dist/{PublicLoadingSpinner-n74JgA9h.d.ts → PublicLoadingSpinner-B84QWsvB.d.ts} +31 -3
  3. package/dist/{UnifiedAuthProvider-ZM7VUC45.js → UnifiedAuthProvider-H7RI4KYD.js} +3 -3
  4. package/dist/{chunk-AZ2QJYKU.js → chunk-2KLAOD4M.js} +3 -3
  5. package/dist/{chunk-HW5BGOWB.js → chunk-2ZYHCFUO.js} +4 -4
  6. package/dist/{chunk-AAM57AEU.js → chunk-5RYPBJYL.js} +16 -19
  7. package/dist/chunk-5RYPBJYL.js.map +1 -0
  8. package/dist/{chunk-XIBSVWJW.js → chunk-7TQDRDSM.js} +5 -5
  9. package/dist/{chunk-TZXYSZT3.js → chunk-EPKHU5SS.js} +314 -245
  10. package/dist/{chunk-TZXYSZT3.js.map → chunk-EPKHU5SS.js.map} +1 -1
  11. package/dist/{chunk-GP3HU6WS.js → chunk-G7UUVEAP.js} +3 -3
  12. package/dist/{chunk-M52CQP5W.js → chunk-MKMKUCPF.js} +762 -12
  13. package/dist/chunk-MKMKUCPF.js.map +1 -0
  14. package/dist/{chunk-OXFOS62D.js → chunk-MVNOAHOP.js} +2 -2
  15. package/dist/{chunk-AYC2P377.js → chunk-ORACUZ7H.js} +2 -2
  16. package/dist/{chunk-SVMPR5IV.js → chunk-V5CTX4FR.js} +963 -788
  17. package/dist/chunk-V5CTX4FR.js.map +1 -0
  18. package/dist/{chunk-6WFM22A4.js → chunk-ZGCVJ7WW.js} +2 -2
  19. package/dist/components.d.ts +1 -1
  20. package/dist/components.js +8 -8
  21. package/dist/hooks.d.ts +94 -3
  22. package/dist/hooks.js +20 -8
  23. package/dist/hooks.js.map +1 -1
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +17 -11
  26. package/dist/index.js.map +1 -1
  27. package/dist/providers.js +2 -2
  28. package/dist/rbac/index.js +7 -7
  29. package/dist/{usePublicRouteParams-BlgwXweB.d.ts → usePublicRouteParams-BwMR2uub.d.ts} +93 -1
  30. package/dist/utils.js +1 -1
  31. package/docs/api/classes/ColumnFactory.md +1 -1
  32. package/docs/api/classes/ErrorBoundary.md +1 -1
  33. package/docs/api/classes/InvalidScopeError.md +1 -1
  34. package/docs/api/classes/MissingUserContextError.md +1 -1
  35. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  36. package/docs/api/classes/PermissionDeniedError.md +1 -1
  37. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  38. package/docs/api/classes/RBACAuditManager.md +1 -1
  39. package/docs/api/classes/RBACCache.md +1 -1
  40. package/docs/api/classes/RBACEngine.md +1 -1
  41. package/docs/api/classes/RBACError.md +1 -1
  42. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  43. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  44. package/docs/api/classes/StorageUtils.md +1 -1
  45. package/docs/api/enums/FileCategory.md +1 -1
  46. package/docs/api/interfaces/AggregateConfig.md +1 -1
  47. package/docs/api/interfaces/ButtonProps.md +1 -1
  48. package/docs/api/interfaces/CardProps.md +1 -1
  49. package/docs/api/interfaces/ColorPalette.md +1 -1
  50. package/docs/api/interfaces/ColorShade.md +1 -1
  51. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  52. package/docs/api/interfaces/DataRecord.md +1 -1
  53. package/docs/api/interfaces/DataTableAction.md +1 -1
  54. package/docs/api/interfaces/DataTableColumn.md +1 -1
  55. package/docs/api/interfaces/DataTableProps.md +1 -1
  56. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  57. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  58. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  59. package/docs/api/interfaces/EventLogoProps.md +1 -1
  60. package/docs/api/interfaces/FileDisplayProps.md +52 -11
  61. package/docs/api/interfaces/FileMetadata.md +1 -1
  62. package/docs/api/interfaces/FileReference.md +1 -1
  63. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  64. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  65. package/docs/api/interfaces/FileUploadProps.md +1 -1
  66. package/docs/api/interfaces/FooterProps.md +1 -1
  67. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  68. package/docs/api/interfaces/InputProps.md +1 -1
  69. package/docs/api/interfaces/LabelProps.md +1 -1
  70. package/docs/api/interfaces/LoginFormProps.md +1 -1
  71. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  72. package/docs/api/interfaces/NavigationContextType.md +1 -1
  73. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  74. package/docs/api/interfaces/NavigationItem.md +1 -1
  75. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  76. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  77. package/docs/api/interfaces/Organisation.md +1 -1
  78. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  79. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  80. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  81. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  82. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  83. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  84. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  85. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  86. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  87. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  88. package/docs/api/interfaces/PaletteData.md +1 -1
  89. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  90. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  91. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  92. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  93. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  94. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  95. package/docs/api/interfaces/PublicPageHeaderProps.md +24 -11
  96. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  97. package/docs/api/interfaces/RBACConfig.md +1 -1
  98. package/docs/api/interfaces/RBACLogger.md +1 -1
  99. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  100. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  101. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  102. package/docs/api/interfaces/RouteConfig.md +1 -1
  103. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  104. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  105. package/docs/api/interfaces/StorageConfig.md +1 -1
  106. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  107. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  108. package/docs/api/interfaces/StorageListOptions.md +1 -1
  109. package/docs/api/interfaces/StorageListResult.md +1 -1
  110. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  111. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  112. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  113. package/docs/api/interfaces/StyleImport.md +1 -1
  114. package/docs/api/interfaces/SwitchProps.md +1 -1
  115. package/docs/api/interfaces/ToastActionElement.md +1 -1
  116. package/docs/api/interfaces/ToastProps.md +1 -1
  117. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  118. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  119. package/docs/api/interfaces/UseEventLogoOptions.md +1 -1
  120. package/docs/api/interfaces/UseEventLogoReturn.md +1 -1
  121. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  122. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  123. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  124. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  125. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  126. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  127. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +47 -0
  128. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +120 -0
  129. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  130. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  131. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  132. package/docs/api/interfaces/UserEventAccess.md +1 -1
  133. package/docs/api/interfaces/UserMenuProps.md +1 -1
  134. package/docs/api/interfaces/UserProfile.md +1 -1
  135. package/docs/api/modules.md +102 -16
  136. package/docs/implementation-guides/file-reference-system.md +15 -0
  137. package/docs/implementation-guides/file-upload-storage.md +16 -0
  138. package/package.json +1 -1
  139. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +9 -7
  140. package/src/components/DataTable/components/DataTableCore.tsx +35 -10
  141. package/src/components/DataTable/components/EditableRow.tsx +62 -22
  142. package/src/components/DataTable/components/UnifiedTableBody.tsx +25 -101
  143. package/src/components/FileDisplay/FileDisplay.test.tsx +263 -39
  144. package/src/components/FileDisplay/FileDisplay.tsx +667 -103
  145. package/src/components/PublicLayout/PublicPageHeader.tsx +15 -8
  146. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +71 -28
  147. package/src/components/Select/Select.test.tsx +83 -6
  148. package/src/components/Select/Select.tsx +236 -16
  149. package/src/examples/CorrectPublicPageImplementation.tsx +16 -13
  150. package/src/examples/PublicEventPage.tsx +9 -6
  151. package/src/examples/PublicPageApp.tsx +9 -6
  152. package/src/examples/PublicPageUsageExample.tsx +9 -7
  153. package/src/hooks/index.ts +4 -0
  154. package/src/hooks/public/index.ts +2 -0
  155. package/src/hooks/public/usePublicFileDisplay.ts +355 -0
  156. package/src/hooks/useFileDisplay.ts +370 -0
  157. package/src/hooks/useFileUrl.ts +130 -0
  158. package/src/services/AuthService.ts +19 -22
  159. package/dist/chunk-AAM57AEU.js.map +0 -1
  160. package/dist/chunk-M52CQP5W.js.map +0 -1
  161. package/dist/chunk-SVMPR5IV.js.map +0 -1
  162. /package/dist/{DataTable-HC5S4RKB.js.map → DataTable-XENXNMCP.js.map} +0 -0
  163. /package/dist/{UnifiedAuthProvider-ZM7VUC45.js.map → UnifiedAuthProvider-H7RI4KYD.js.map} +0 -0
  164. /package/dist/{chunk-AZ2QJYKU.js.map → chunk-2KLAOD4M.js.map} +0 -0
  165. /package/dist/{chunk-HW5BGOWB.js.map → chunk-2ZYHCFUO.js.map} +0 -0
  166. /package/dist/{chunk-XIBSVWJW.js.map → chunk-7TQDRDSM.js.map} +0 -0
  167. /package/dist/{chunk-GP3HU6WS.js.map → chunk-G7UUVEAP.js.map} +0 -0
  168. /package/dist/{chunk-OXFOS62D.js.map → chunk-MVNOAHOP.js.map} +0 -0
  169. /package/dist/{chunk-AYC2P377.js.map → chunk-ORACUZ7H.js.map} +0 -0
  170. /package/dist/{chunk-6WFM22A4.js.map → chunk-ZGCVJ7WW.js.map} +0 -0
@@ -571,16 +571,21 @@ function DataTableInternal<TData extends DataRecord>({
571
571
  * from the beginning. This provides a better user experience when searching
572
572
  * large datasets.
573
573
  *
574
+ * CRITICAL: Must sync both state.searchQuery (used by state management) and
575
+ * performance hook's searchQuery (used by table filtering and search index).
576
+ *
574
577
  * @param value - The search query string
575
578
  */
576
579
  const handleSearch = useCallback((value: string) => {
580
+ // Update both search query states to keep them in sync
577
581
  stateActions.setSearchQuery(value);
582
+ setSearchQuery(value);
578
583
 
579
584
  // Reset to first page when searching to show results from the beginning
580
585
  if (secureFeatures.pagination) {
581
586
  stateActions.setPagination({ ...state.pagination, pageIndex: 0 });
582
587
  }
583
- }, [stateActions, secureFeatures.pagination, state.pagination]);
588
+ }, [stateActions, setSearchQuery, secureFeatures.pagination, state.pagination]);
584
589
 
585
590
  // ============================================================================
586
591
  // SERVER-SIDE DATA FETCHING - ALWAYS call these hooks
@@ -967,16 +972,34 @@ function DataTableInternal<TData extends DataRecord>({
967
972
  // Get the current filtered/paginated data (what's actually visible)
968
973
  const currentData = table.getFilteredRowModel().rows.map(row => row.original);
969
974
 
970
- // Get only visible columns
971
- const visibleColumns = columns
972
- .filter(col => {
973
- const column = table.getColumn(col.accessorKey as string);
974
- return column && column.getIsVisible();
975
+ // Get only visible columns by checking the actual table columns
976
+ // This approach is more reliable because it uses the table's actual column registry
977
+ const tableColumns = table.getAllColumns();
978
+ const visibleTableColumns = tableColumns.filter(col => {
979
+ // Exclude system columns (selection, actions) and only include data columns
980
+ const isSystemColumn = col.id === 'select' || col.id === 'actions';
981
+ return !isSystemColumn && col.getIsVisible();
982
+ });
983
+
984
+ // Map table columns back to original column definitions for export
985
+ const visibleColumns = visibleTableColumns
986
+ .map(tableCol => {
987
+ // Find the original column definition that matches this table column
988
+ const originalCol = columns.find(col => {
989
+ const colId = col.id || col.accessorKey;
990
+ return colId && String(colId) === tableCol.id;
991
+ });
992
+
993
+ if (!originalCol) return null;
994
+
995
+ return {
996
+ ...originalCol,
997
+ header: typeof originalCol.header === 'string'
998
+ ? originalCol.header
999
+ : originalCol.accessorKey || tableCol.id || 'Column',
1000
+ };
975
1001
  })
976
- .map(col => ({
977
- ...col,
978
- header: typeof col.header === 'string' ? col.header : col.accessorKey || 'Column',
979
- }));
1002
+ .filter((col): col is NonNullable<typeof col> => col !== null);
980
1003
 
981
1004
  // Generate filename with timestamp
982
1005
  const timestamp = new Date().toISOString().split('T')[0];
@@ -1179,7 +1202,9 @@ function DataTableInternal<TData extends DataRecord>({
1179
1202
  emptyState={React.isValidElement(emptyState) ? undefined : emptyState as any}
1180
1203
  isFiltered={searchQuery !== '' || state.columnFilters.length > 0}
1181
1204
  onClearFilters={() => {
1205
+ // Clear both search query states to keep them in sync
1182
1206
  stateActions.setSearchQuery('');
1207
+ setSearchQuery('');
1183
1208
  stateActions.setColumnFilters([]);
1184
1209
  }}
1185
1210
  enableFiltering={secureFeatures.filtering}
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { flexRender, type Row, type Column } from '@tanstack/react-table';
3
+ import { X, Check } from 'lucide-react';
3
4
 
4
5
  import { Button } from '../../Button/Button';
5
6
  import { Input } from '../../Input/Input';
@@ -29,7 +30,9 @@ const renderEditField = <TData extends DataRecord>(
29
30
  column: Column<TData, unknown>,
30
31
  value: CellValue,
31
32
  onChange: (value: CellValue | Record<string, CellValue>) => void,
32
- editingData: Record<string, CellValue> = {}
33
+ editingData: Record<string, CellValue> = {},
34
+ placeholder?: string,
35
+ inputRef?: React.Ref<HTMLInputElement>
33
36
  ) => {
34
37
  const columnDef = column.columnDef as EditableColumnDef<TData>;
35
38
 
@@ -63,6 +66,7 @@ const renderEditField = <TData extends DataRecord>(
63
66
  if (columnDef.fieldType === 'date') {
64
67
  return (
65
68
  <Input
69
+ ref={inputRef}
66
70
  type="date"
67
71
  value={String(value ?? '')}
68
72
  onChange={(e) => onChange(e.target.value as unknown as CellValue)}
@@ -74,6 +78,7 @@ const renderEditField = <TData extends DataRecord>(
74
78
  if (columnDef.fieldType === 'number') {
75
79
  return (
76
80
  <Input
81
+ ref={inputRef}
77
82
  type="number"
78
83
  value={String(value ?? '')}
79
84
  onChange={(e) => onChange(e.target.value as unknown as CellValue)}
@@ -84,11 +89,12 @@ const renderEditField = <TData extends DataRecord>(
84
89
 
85
90
  return (
86
91
  <Input
92
+ ref={inputRef}
87
93
  type="text"
88
94
  value={String(value ?? '')}
89
95
  onChange={(e) => onChange(e.target.value as unknown as CellValue)}
90
96
  className="w-full h-7"
91
- placeholder={`Enter ${column.id}`}
97
+ placeholder={placeholder || `Enter ${column.id}`}
92
98
  />
93
99
  );
94
100
  };
@@ -105,6 +111,16 @@ export function EditableRow<TData extends DataRecord>({
105
111
  hierarchical = false,
106
112
  }: EditableRowProps<TData>) {
107
113
  const rowId = getRowId ? getRowId(row.original, row.index) : String(row.id);
114
+ const firstInputRef = React.useRef<HTMLInputElement>(null);
115
+ const hasAssignedRef = React.useRef(false);
116
+
117
+ // Auto-focus first input field when entering edit mode
118
+ React.useEffect(() => {
119
+ if (firstInputRef.current) {
120
+ firstInputRef.current.focus();
121
+ firstInputRef.current.select();
122
+ }
123
+ }, []);
108
124
 
109
125
  return (
110
126
  <tr
@@ -116,34 +132,58 @@ export function EditableRow<TData extends DataRecord>({
116
132
  <td key={cell.id} role="cell">
117
133
  <div className={cell.column.columnDef.meta?.align === 'right' ? 'text-right' : ''}>
118
134
  {cell.column.id !== 'actions' ? (
119
- cell.column.columnDef.cell ? (
120
- flexRender(cell.column.columnDef.cell, {
121
- ...cell.getContext(),
122
- getIsEditing: () => true,
123
- setValue: (value: CellValue | Record<string, CellValue>) => {
135
+ // In edit mode, always use renderEditField for editable columns
136
+ // Custom cell renderers are for display mode only
137
+ (() => {
138
+ const columnDef = cell.column.columnDef as EditableColumnDef<TData>;
139
+ // If column is explicitly marked as not editable, check if custom cell renderer handles editing
140
+ if (columnDef.editable === false) {
141
+ // Not editable - use custom cell renderer if available, otherwise show static value
142
+ return cell.column.columnDef.cell ? (
143
+ flexRender(cell.column.columnDef.cell, {
144
+ ...cell.getContext(),
145
+ getIsEditing: () => true,
146
+ setValue: (value: CellValue | Record<string, CellValue>) => {
147
+ if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
148
+ onEditingDataChange({ ...editingData, ...(value as Record<string, CellValue>) });
149
+ } else {
150
+ onEditingDataChange({ ...editingData, [cell.column.id]: value as CellValue });
151
+ }
152
+ }
153
+ })
154
+ ) : (
155
+ <span className="text-sm text-gray-600">{String(cell.getValue() ?? '')}</span>
156
+ );
157
+ }
158
+
159
+ // Column is editable - always use renderEditField in EditableRow
160
+ const shouldGetRef = !hasAssignedRef.current;
161
+ if (shouldGetRef) {
162
+ hasAssignedRef.current = true;
163
+ }
164
+
165
+ return renderEditField(
166
+ cell.column,
167
+ editingData[cell.column.id] ?? (cell.getValue() as CellValue),
168
+ (value) => {
124
169
  if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
125
170
  onEditingDataChange({ ...editingData, ...(value as Record<string, CellValue>) });
126
171
  } else {
127
172
  onEditingDataChange({ ...editingData, [cell.column.id]: value as CellValue });
128
173
  }
129
- }
130
- })
131
- ) : (
132
- renderEditField(cell.column, editingData[cell.column.id], (value) => {
133
- if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
134
- onEditingDataChange({ ...editingData, ...(value as Record<string, CellValue>) });
135
- } else {
136
- onEditingDataChange({ ...editingData, [cell.column.id]: value as CellValue });
137
- }
138
- }, editingData)
139
- )
174
+ },
175
+ editingData,
176
+ undefined, // placeholder
177
+ shouldGetRef ? firstInputRef : undefined
178
+ );
179
+ })()
140
180
  ) : (
141
181
  <div className="flex gap-1">
142
- <Button onClick={onSave} size="sm" variant="default">
143
- Save
182
+ <Button onClick={onSave} size="sm" variant="default" aria-label="Save changes">
183
+ <Check className="h-4 w-4" />
144
184
  </Button>
145
- <Button onClick={onCancel} size="sm" variant="outline">
146
- Cancel
185
+ <Button onClick={onCancel} size="sm" variant="outline" aria-label="Cancel editing">
186
+ <X className="h-4 w-4" />
147
187
  </Button>
148
188
  </div>
149
189
  )}
@@ -319,109 +319,33 @@ const RowComponent = React.memo(({
319
319
  const subRowsCount = row.subRows?.length || 0;
320
320
  const isExpanded = row.getIsExpanded();
321
321
 
322
+ // Only render the group header row - sub-rows will be rendered by the standard rendering loop
322
323
  return (
323
- <React.Fragment>
324
- {/* Group Header Row */}
325
- <tr className="bg-sec-50 hover:bg-sec-100" style={style}>
326
- <td
327
- className={getTableCellClasses({
328
- isCompact: true,
329
- className: "px-3 py-2 flex items-center font-medium"
330
- })}
331
- colSpan={row.getAllCells().length}
324
+ <tr className="bg-sec-50 hover:bg-sec-100" style={style}>
325
+ <td
326
+ className={getTableCellClasses({
327
+ isCompact: true,
328
+ className: "px-3 py-2 flex items-center font-medium"
329
+ })}
330
+ colSpan={row.getAllCells().length}
331
+ >
332
+ <Button
333
+ variant="ghost"
334
+ size="sm"
335
+ onClick={() => row.toggleExpanded()}
336
+ className="p-0 h-auto mr-2"
332
337
  >
333
- <Button
334
- variant="ghost"
335
- size="sm"
336
- onClick={() => row.toggleExpanded()}
337
- className="p-0 h-auto mr-2"
338
- >
339
- {isExpanded ? (
340
- <ChevronDown className="h-4 w-4" />
341
- ) : (
342
- <ChevronRight className="h-4 w-4" />
343
- )}
344
- </Button>
345
- <span className="text-sm">
346
- {String(groupValue)} ({subRowsCount} items)
347
- </span>
348
- </td>
349
- </tr>
350
-
351
- {/* Render sub-rows if expanded */}
352
- {isExpanded && row.subRows?.map((subRow: any) => {
353
- const subRowId = getRowId ? getRowId(subRow.original, subRow.index) : String(subRow.index);
354
- const isSubRowEditing = editingRowId === subRowId;
355
-
356
- return (
357
- <tr key={subRow.id} className="border-l-2 border-l-blue-200" style={style}>
358
- {subRow.getVisibleCells().map((cell: any) => (
359
- <td
360
- key={cell.id}
361
- className={getTableCellClasses({
362
- isCompact: true,
363
- className: `px-3 py-2 pl-8 ${cell.column.id === 'actions' ? 'whitespace-nowrap' : 'whitespace-normal break-words'} ${cell.column.columnDef.meta?.align === 'right' ? 'text-right' : ''}`
364
- })}
365
- >
366
- {isSubRowEditing && cell.column.id !== 'actions' ? (
367
- // Check if column has a custom cell renderer - if so, use it in edit mode
368
- cell.column.columnDef.cell ? (
369
- flexRender(cell.column.columnDef.cell, {
370
- ...cell.getContext(),
371
- hierarchical: hierarchical,
372
- isParent: false,
373
- isChild: true,
374
- isHierarchical: false,
375
- rowId: subRowId,
376
- isExpanded: false,
377
- hasChildren: false,
378
- getIsEditing: () => true, // Always true in edit mode
379
- setValue: (value: any) => {
380
- if (typeof value === 'object' && value !== null) {
381
- onEditingDataChange?.({ ...editingData, ...value });
382
- } else {
383
- onEditingDataChange?.({ ...editingData, [cell.column.id]: value });
384
- }
385
- }
386
- })
387
- ) : (
388
- // Fall back to default edit field rendering when no custom cell renderer
389
- renderEditField(cell.column, editingData?.[cell.column.id], (value) => {
390
- if (typeof value === 'object' && value !== null) {
391
- onEditingDataChange?.({ ...editingData, ...value });
392
- } else {
393
- onEditingDataChange?.({ ...editingData, [cell.column.id]: value });
394
- }
395
- }, editingData)
396
- )
397
- ) : cell.column.id === 'actions' ? (
398
- <ActionButtons
399
- row={row}
400
- actions={actions}
401
- isEditing={isEditing}
402
- isParent={isParent}
403
- hierarchical={!!hierarchical}
404
- rbac={rbac}
405
- permissions={permissions}
406
- />
407
- ) : (
408
- flexRender(cell.column.columnDef.cell, {
409
- ...cell.getContext(),
410
- hierarchical: hierarchical,
411
- isParent: false,
412
- isChild: true,
413
- isHierarchical: false,
414
- rowId: subRowId,
415
- isExpanded: false,
416
- hasChildren: false
417
- })
418
- )}
419
- </td>
420
- ))}
421
- </tr>
422
- );
423
- })}
424
- </React.Fragment>
338
+ {isExpanded ? (
339
+ <ChevronDown className="h-4 w-4" />
340
+ ) : (
341
+ <ChevronRight className="h-4 w-4" />
342
+ )}
343
+ </Button>
344
+ <span className="text-sm">
345
+ {String(groupValue)} ({subRowsCount} items)
346
+ </span>
347
+ </td>
348
+ </tr>
425
349
  );
426
350
  }
427
351