@jmruthers/pace-core 0.5.113 → 0.5.114

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 (114) hide show
  1. package/dist/{DataTable-YNEB5WDK.js → DataTable-3JRLZXER.js} +2 -2
  2. package/dist/{chunk-YUKG6PXE.js → chunk-5YIZFEUQ.js} +13 -5
  3. package/dist/chunk-5YIZFEUQ.js.map +1 -0
  4. package/dist/{chunk-KISJX3XZ.js → chunk-JHWQNJP3.js} +4 -3
  5. package/dist/{chunk-KISJX3XZ.js.map → chunk-JHWQNJP3.js.map} +1 -1
  6. package/dist/components.js +2 -2
  7. package/dist/index.js +2 -2
  8. package/dist/utils.js +1 -1
  9. package/docs/api/classes/ColumnFactory.md +1 -1
  10. package/docs/api/classes/ErrorBoundary.md +1 -1
  11. package/docs/api/classes/InvalidScopeError.md +1 -1
  12. package/docs/api/classes/MissingUserContextError.md +1 -1
  13. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  14. package/docs/api/classes/PermissionDeniedError.md +1 -1
  15. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  16. package/docs/api/classes/RBACAuditManager.md +1 -1
  17. package/docs/api/classes/RBACCache.md +1 -1
  18. package/docs/api/classes/RBACEngine.md +1 -1
  19. package/docs/api/classes/RBACError.md +1 -1
  20. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  21. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  22. package/docs/api/classes/StorageUtils.md +1 -1
  23. package/docs/api/enums/FileCategory.md +1 -1
  24. package/docs/api/interfaces/AggregateConfig.md +1 -1
  25. package/docs/api/interfaces/ButtonProps.md +1 -1
  26. package/docs/api/interfaces/CardProps.md +1 -1
  27. package/docs/api/interfaces/ColorPalette.md +1 -1
  28. package/docs/api/interfaces/ColorShade.md +1 -1
  29. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  30. package/docs/api/interfaces/DataRecord.md +1 -1
  31. package/docs/api/interfaces/DataTableAction.md +1 -1
  32. package/docs/api/interfaces/DataTableColumn.md +1 -1
  33. package/docs/api/interfaces/DataTableProps.md +1 -1
  34. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  35. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  36. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  37. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  38. package/docs/api/interfaces/FileMetadata.md +1 -1
  39. package/docs/api/interfaces/FileReference.md +1 -1
  40. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  41. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  42. package/docs/api/interfaces/FileUploadProps.md +1 -1
  43. package/docs/api/interfaces/FooterProps.md +1 -1
  44. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  45. package/docs/api/interfaces/InputProps.md +1 -1
  46. package/docs/api/interfaces/LabelProps.md +1 -1
  47. package/docs/api/interfaces/LoginFormProps.md +1 -1
  48. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  49. package/docs/api/interfaces/NavigationContextType.md +1 -1
  50. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  51. package/docs/api/interfaces/NavigationItem.md +1 -1
  52. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  53. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  54. package/docs/api/interfaces/Organisation.md +1 -1
  55. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  56. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  57. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  58. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  59. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  60. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  61. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  62. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  63. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  64. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  65. package/docs/api/interfaces/PaletteData.md +1 -1
  66. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  67. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  68. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  69. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  70. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  71. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  72. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  73. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  74. package/docs/api/interfaces/RBACConfig.md +1 -1
  75. package/docs/api/interfaces/RBACLogger.md +1 -1
  76. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  77. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  78. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  79. package/docs/api/interfaces/RouteConfig.md +1 -1
  80. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  81. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  82. package/docs/api/interfaces/StorageConfig.md +1 -1
  83. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  84. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  85. package/docs/api/interfaces/StorageListOptions.md +1 -1
  86. package/docs/api/interfaces/StorageListResult.md +1 -1
  87. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  88. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  89. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  90. package/docs/api/interfaces/StyleImport.md +1 -1
  91. package/docs/api/interfaces/SwitchProps.md +1 -1
  92. package/docs/api/interfaces/ToastActionElement.md +1 -1
  93. package/docs/api/interfaces/ToastProps.md +1 -1
  94. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  95. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  96. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  97. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  98. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  99. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  100. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  101. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  102. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  103. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  104. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  105. package/docs/api/interfaces/UserEventAccess.md +1 -1
  106. package/docs/api/interfaces/UserMenuProps.md +1 -1
  107. package/docs/api/interfaces/UserProfile.md +1 -1
  108. package/docs/api/modules.md +4 -4
  109. package/docs/architecture/rpc-function-standards.md +131 -11
  110. package/package.json +1 -1
  111. package/src/components/Dialog/Dialog.tsx +3 -2
  112. package/src/components/NavigationMenu/NavigationMenu.tsx +29 -5
  113. package/dist/chunk-YUKG6PXE.js.map +0 -1
  114. /package/dist/{DataTable-YNEB5WDK.js.map → DataTable-3JRLZXER.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / StorageListOptions
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / StorageListOptions
2
2
 
3
3
  # Interface: StorageListOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / StorageListResult
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / StorageListResult
2
2
 
3
3
  # Interface: StorageListResult
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / StorageUploadOptions
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / StorageUploadOptions
2
2
 
3
3
  # Interface: StorageUploadOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / StorageUploadResult
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / StorageUploadResult
2
2
 
3
3
  # Interface: StorageUploadResult
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / StorageUrlOptions
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / StorageUrlOptions
2
2
 
3
3
  # Interface: StorageUrlOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / StyleImport
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / StyleImport
2
2
 
3
3
  # Interface: StyleImport
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / SwitchProps
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / SwitchProps
2
2
 
3
3
  # Interface: SwitchProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / ToastActionElement
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / ToastActionElement
2
2
 
3
3
  # Interface: ToastActionElement
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / ToastProps
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / ToastProps
2
2
 
3
3
  # Interface: ToastProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UnifiedAuthContextType
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UnifiedAuthContextType
2
2
 
3
3
  # Interface: UnifiedAuthContextType
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UnifiedAuthProviderProps
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UnifiedAuthProviderProps
2
2
 
3
3
  # Interface: UnifiedAuthProviderProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UseInactivityTrackerOptions
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UseInactivityTrackerOptions
2
2
 
3
3
  # Interface: UseInactivityTrackerOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UseInactivityTrackerReturn
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UseInactivityTrackerReturn
2
2
 
3
3
  # Interface: UseInactivityTrackerReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UsePublicEventOptions
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UsePublicEventOptions
2
2
 
3
3
  # Interface: UsePublicEventOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UsePublicEventReturn
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UsePublicEventReturn
2
2
 
3
3
  # Interface: UsePublicEventReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayOptions
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayOptions
2
2
 
3
3
  # Interface: UsePublicFileDisplayOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayReturn
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UsePublicFileDisplayReturn
2
2
 
3
3
  # Interface: UsePublicFileDisplayReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UsePublicRouteParamsReturn
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UsePublicRouteParamsReturn
2
2
 
3
3
  # Interface: UsePublicRouteParamsReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UseResolvedScopeOptions
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UseResolvedScopeOptions
2
2
 
3
3
  # Interface: UseResolvedScopeOptions
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UseResolvedScopeReturn
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UseResolvedScopeReturn
2
2
 
3
3
  # Interface: UseResolvedScopeReturn
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UserEventAccess
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UserEventAccess
2
2
 
3
3
  # Interface: UserEventAccess
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UserMenuProps
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UserMenuProps
2
2
 
3
3
  # Interface: UserMenuProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.113](../README.md) / [Exports](../modules.md) / UserProfile
1
+ [@jmruthers/pace-core - v0.5.114](../README.md) / [Exports](../modules.md) / UserProfile
2
2
 
3
3
  # Interface: UserProfile
4
4
 
@@ -1,6 +1,6 @@
1
- [@jmruthers/pace-core - v0.5.113](README.md) / Exports
1
+ [@jmruthers/pace-core - v0.5.114](README.md) / Exports
2
2
 
3
- # @jmruthers/pace-core - v0.5.113
3
+ # @jmruthers/pace-core - v0.5.114
4
4
 
5
5
  **`File`**
6
6
 
@@ -1572,7 +1572,7 @@ ___
1572
1572
 
1573
1573
  #### Defined in
1574
1574
 
1575
- [packages/core/src/components/Dialog/Dialog.tsx:706](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/Dialog/Dialog.tsx#L706)
1575
+ [packages/core/src/components/Dialog/Dialog.tsx:707](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/Dialog/Dialog.tsx#L707)
1576
1576
 
1577
1577
  ___
1578
1578
 
@@ -1592,7 +1592,7 @@ ___
1592
1592
 
1593
1593
  #### Defined in
1594
1594
 
1595
- [packages/core/src/components/Dialog/Dialog.tsx:741](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/Dialog/Dialog.tsx#L741)
1595
+ [packages/core/src/components/Dialog/Dialog.tsx:742](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/Dialog/Dialog.tsx#L742)
1596
1596
 
1597
1597
  ___
1598
1598
 
@@ -120,13 +120,16 @@ BEGIN
120
120
 
121
121
  -- 4. Dynamic RBAC permission check (replaces manual organisation/event checks)
122
122
  -- Use rbac_check_permission_simplified() for consistency with RLS policies
123
+ -- ⚠️ CRITICAL: Permission format must match what rbac_permissions_get returns
124
+ -- Format: 'operation:page.{pageName}' (e.g., 'read:page.dishes', 'create:page.meals')
125
+ -- The permission string is passed to rbac_permissions_get and must match exactly
123
126
  IF NOT rbac_check_permission_simplified(
124
127
  COALESCE(p_user_id, auth.uid()),
125
- 'read:resource', -- Format: operation:resource (e.g., 'read:meals', 'create:events')
128
+ 'read:page.dishes', -- Format: operation:page.{pageName} - MUST match rbac_permissions_get output
126
129
  p_organisation_id,
127
- NULL, -- p_event_id if event-scoped, NULL if org-scoped
130
+ p_event_id, -- p_event_id if event-scoped, NULL if org-scoped
128
131
  (SELECT id FROM rbac_apps WHERE name = 'CAKE' LIMIT 1), -- p_app_id
129
- 'resource' -- p_page_id (page name from rbac_app_pages)
132
+ 'dishes' -- p_page_id (page name from rbac_app_pages)
130
133
  ) THEN
131
134
  RETURN; -- No permission
132
135
  END IF;
@@ -258,13 +261,15 @@ BEGIN
258
261
  END IF;
259
262
 
260
263
  -- Use rbac_check_permission_simplified for dynamic RBAC checking
264
+ -- ⚠️ CRITICAL: Permission format must be 'operation:page.{pageName}'
265
+ -- This matches the format returned by rbac_permissions_get()
261
266
  IF NOT rbac_check_permission_simplified(
262
267
  p_user_id,
263
- 'read:meals',
268
+ 'read:page.meals', -- Format: operation:page.{pageName}
264
269
  p_organisation_id,
265
270
  p_event_id,
266
271
  (SELECT id FROM rbac_apps WHERE name = 'CAKE' LIMIT 1),
267
- 'meals'
272
+ 'meals' -- Page name (resolved to page ID internally)
268
273
  ) THEN
269
274
  RETURN; -- No permission
270
275
  END IF;
@@ -376,11 +381,11 @@ CREATE POLICY "rbac_cake_meal_select" ON cake_meal
376
381
  is_user_super_admin()
377
382
  OR rbac_check_permission_simplified(
378
383
  auth.uid(),
379
- 'read:meals',
384
+ 'read:page.meals', -- Format: operation:page.{pageName}
380
385
  organisation_id,
381
386
  meal_event_id,
382
387
  (SELECT id FROM rbac_apps WHERE name = 'CAKE' LIMIT 1),
383
- 'meals'
388
+ 'meals' -- Page name
384
389
  )
385
390
  )
386
391
  );
@@ -396,11 +401,11 @@ BEGIN
396
401
  v_is_super_admin OR
397
402
  rbac_check_permission_simplified(
398
403
  p_user_id,
399
- 'read:meals',
404
+ 'read:page.meals', -- Format: operation:page.{pageName}
400
405
  p_organisation_id,
401
406
  p_event_id,
402
407
  (SELECT id FROM rbac_apps WHERE name = 'CAKE' LIMIT 1),
403
- 'meals'
408
+ 'meals' -- Page name
404
409
  )
405
410
  ) THEN
406
411
  RETURN;
@@ -598,11 +603,91 @@ SELECT * FROM data_user_events_get(NULL, 'user-id', 'org-123');
598
603
  -- Should return empty gracefully
599
604
  ```
600
605
 
606
+ ## Permission Format
607
+
608
+ ### ⚠️ CRITICAL: Correct Permission Format
609
+
610
+ **The permission string format is critical and must match exactly what `rbac_permissions_get()` returns.**
611
+
612
+ #### Correct Format
613
+
614
+ ```sql
615
+ -- ✅ CORRECT: Format is 'operation:page.{pageName}'
616
+ rbac_check_permission_simplified(
617
+ p_user_id,
618
+ 'create:page.dishes', -- Format: operation:page.{pageName}
619
+ p_organisation_id,
620
+ p_event_id,
621
+ v_app_id,
622
+ 'dishes' -- Page name (resolved to page ID internally)
623
+ )
624
+ ```
625
+
626
+ #### Common Mistakes
627
+
628
+ ```sql
629
+ -- ❌ WRONG: Missing 'page.' prefix
630
+ 'create:dishes'
631
+
632
+ -- ❌ WRONG: Wrong separator
633
+ 'create:page/dishes'
634
+
635
+ -- ❌ WRONG: Wrong format entirely
636
+ 'dishes:create'
637
+ ```
638
+
639
+ #### How to Determine the Correct Format
640
+
641
+ 1. **Check existing migrations** - Look at recent migrations that work
642
+ 2. **Check RLS policies** - Use the same format as RLS policies for the same resource
643
+ 3. **Verify with rbac_permissions_get** - The permission string must match what this function returns in the `permission_type` column
644
+
645
+ #### Examples by Operation Type
646
+
647
+ ```sql
648
+ -- Page-based permissions (most common)
649
+ 'read:page.dishes' -- Read dishes page
650
+ 'create:page.dishes' -- Create dishes
651
+ 'update:page.dishes' -- Update dishes
652
+ 'delete:page.dishes' -- Delete dishes
653
+
654
+ 'read:page.meals' -- Read meals page
655
+ 'read:page.distribution' -- Read distribution page
656
+ 'read:page.deliveries' -- Read deliveries page
657
+
658
+ -- File-based permissions (special case)
659
+ 'read:files' -- Read files
660
+ 'create:files' -- Create files
661
+ 'delete:files' -- Delete files
662
+ ```
663
+
664
+ #### Parameter Guidelines
665
+
666
+ ```sql
667
+ rbac_check_permission_simplified(
668
+ p_user_id, -- User ID (required)
669
+ 'create:page.dishes', -- Permission string (format: operation:page.{pageName})
670
+ p_organisation_id, -- Organisation ID (required for non-super-admin)
671
+ p_event_id, -- Event ID (optional, for event-scoped permissions)
672
+ v_app_id, -- App ID (required, usually CAKE app)
673
+ 'dishes' -- Page name (required, matches the page name in rbac_app_pages)
674
+ )
675
+ ```
676
+
601
677
  ## Migration Guidelines
602
678
 
603
679
  ### Creating New Functions
604
680
 
605
- 1. **Create migration file**: `YYYYMMDDHHMMSS_descriptive_name.sql`
681
+ 1. **Create migration file with correct timestamp**:
682
+ ```bash
683
+ # Generate timestamp: YYYYMMDDHHMMSS
684
+ date +%Y%m%d%H%M%S
685
+ # Example output: 20251104113510
686
+
687
+ # Create file: supabase/migrations/20251104113510_descriptive_name.sql
688
+ ```
689
+ ⚠️ **CRITICAL**: Always generate a NEW timestamp for each migration. Never reuse timestamps from previous migrations, even if they haven't been pushed yet.
690
+
606
691
  2. **Add function**: Use standard template above
607
692
  3. **Add comments**: Document purpose, parameters, returns
608
693
  4. **Add grants**: `GRANT EXECUTE ON FUNCTION ... TO authenticated;`
@@ -610,6 +695,28 @@ SELECT * FROM data_user_events_get(NULL, 'user-id', 'org-123');
610
695
  6. **Regenerate types**: Run `npm run generate-types` to automatically generate TypeScript types from the database schema
611
696
  7. **Verify types**: Check that `Database['public']['Functions']['function_name']` exists in `packages/core/src/types/database.generated.ts`
612
697
 
698
+ ### ⚠️ Common Migration Mistakes
699
+
700
+ 1. **Reusing old migration timestamps**:
701
+ - ❌ **WRONG**: Reusing `20251104113047` if it's already been pushed
702
+ - ✅ **CORRECT**: Generate new timestamp: `date +%Y%m%d%H%M%S` → `20251104113510`
703
+ - Always create a NEW file with a NEW timestamp for each migration
704
+
705
+ 2. **Wrong permission format**:
706
+ - ❌ **WRONG**: `'create:dishes'` or `'read:meals'`
707
+ - ✅ **CORRECT**: `'create:page.dishes'` or `'read:page.meals'`
708
+ - Always use `'operation:page.{pageName}'` format, not `'operation:resource'`
709
+ - The format must match exactly what `rbac_permissions_get()` returns
710
+
711
+ 3. **Missing page_id parameter**: Always provide the page name as the last parameter (e.g., `'dishes'`, `'meals'`, `'distribution'`)
712
+
713
+ 4. **Ambiguous column references**:
714
+ - ❌ **WRONG**: `SELECT mealtype_name FROM cake_mealtype`
715
+ - ✅ **CORRECT**: `SELECT cake_mealtype.mealtype_name FROM cake_mealtype`
716
+ - Always fully qualify table names when there might be ambiguity
717
+
718
+ 5. **Not checking existing migrations**: Before creating a new migration, check if a similar one already exists to avoid duplicates
719
+
613
720
  ### Updating Existing Functions
614
721
 
615
722
  1. **Use `CREATE OR REPLACE FUNCTION`** - Ensures idempotency
@@ -735,6 +842,7 @@ $$;
735
842
 
736
843
  When creating or reviewing an RPC function, ensure:
737
844
 
845
+ ### Function Structure
738
846
  - ✅ Follows naming convention: `<family>_<domain>_<verb>`
739
847
  - ✅ Uses `SECURITY DEFINER`
740
848
  - ✅ Sets `search_path TO 'public'`
@@ -742,7 +850,7 @@ When creating or reviewing an RPC function, ensure:
742
850
  - ✅ Checks super_admin status (if applicable)
743
851
  - ✅ Validates organisation membership (if applicable)
744
852
  - ✅ Validates event access (if event-scoped)
745
- - ✅ Uses proper error handling
853
+ - ✅ Uses proper error handling with `EXCEPTION WHEN OTHERS`
746
854
  - ✅ Returns empty on error (fail-secure)
747
855
  - ✅ Has documentation comments
748
856
  - ✅ Uses descriptive parameter names (`p_` prefix)
@@ -751,6 +859,18 @@ When creating or reviewing an RPC function, ensure:
751
859
  - ✅ Includes `GRANT EXECUTE` statement
752
860
  - ✅ Tested with various scenarios
753
861
 
862
+ ### ⚠️ Critical Permission Format
863
+ - ✅ **Permission format is correct**: `'operation:page.{pageName}'` (e.g., `'create:page.dishes'`, `'read:page.meals'`)
864
+ - ✅ **NOT using wrong format**: `'operation:resource'` (e.g., `'create:dishes'`, `'read:meals'`)
865
+ - ✅ **Page name parameter provided**: Last parameter is the page name (e.g., `'dishes'`, `'meals'`)
866
+ - ✅ **Permission format matches rbac_permissions_get output**: Verify by checking existing working migrations
867
+
868
+ ### ⚠️ Migration File Requirements
869
+ - ✅ **New timestamp generated**: Run `date +%Y%m%d%H%M%S` to get current timestamp
870
+ - ✅ **New file created**: Never reuse timestamps from previous migrations
871
+ - ✅ **File naming**: `YYYYMMDDHHMMSS_descriptive_name.sql`
872
+ - ✅ **No ambiguous column references**: Fully qualify table names (e.g., `cake_mealtype.mealtype_name`)
873
+
754
874
  ## References
755
875
 
756
876
  - [RBAC System Documentation](../core-concepts/rbac-system.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.113",
3
+ "version": "0.5.114",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -694,8 +694,9 @@ const DialogFooter = ({
694
694
  }: DialogFooterProps) => (
695
695
  <footer
696
696
  className={cn(
697
- 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
698
- sticky ? 'sticky bottom-0 z-10 bg-background p-6 pt-4 border-t' : 'p-6 pt-4',
697
+ // Only apply default layout classes if no custom className is provided
698
+ !className && 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
699
+ !className && (sticky ? 'sticky bottom-0 z-10 bg-background p-6 pt-4 border-t' : 'p-6 pt-4'),
699
700
  className
700
701
  )}
701
702
  {...props}
@@ -448,18 +448,22 @@ export const NavigationMenu = React.forwardRef<
448
448
 
449
449
  // Stabilize scope object to prevent unnecessary permission refetches
450
450
  // This prevents the permission map from being cleared when scope object reference changes
451
+ // IMPORTANT: Only update stable scope when scope is resolved (not loading) to prevent
452
+ // clearing permissions during event/scope transitions
451
453
  const stableScopeRef = React.useRef<{ organisationId: string; eventId?: string; appId?: string }>({
452
454
  organisationId: '',
453
455
  eventId: undefined,
454
456
  appId: undefined
455
457
  });
456
458
 
457
- // Only update stable scope if values actually changed
458
- if (resolvedScope?.organisationId) {
459
+ // Only update stable scope when scope is resolved (not loading) AND values actually changed
460
+ // This prevents triggering permission refetches during scope transitions
461
+ if (!scopeLoading && resolvedScope?.organisationId) {
459
462
  const newOrgId = resolvedScope.organisationId;
460
463
  const newEventId = resolvedScope.eventId;
461
464
  const newAppId = resolvedScope.appId;
462
465
 
466
+ // Only update if values actually changed
463
467
  if (stableScopeRef.current.organisationId !== newOrgId ||
464
468
  stableScopeRef.current.eventId !== newEventId ||
465
469
  stableScopeRef.current.appId !== newAppId) {
@@ -469,8 +473,8 @@ export const NavigationMenu = React.forwardRef<
469
473
  appId: newAppId
470
474
  };
471
475
  }
472
- } else if (!resolvedScope) {
473
- // Only reset if we had a previous value - don't clear on initial render
476
+ } else if (!scopeLoading && !resolvedScope) {
477
+ // Only reset if we had a previous value and scope is resolved - don't clear during loading
474
478
  if (stableScopeRef.current.organisationId !== '') {
475
479
  stableScopeRef.current = {
476
480
  organisationId: '',
@@ -479,6 +483,7 @@ export const NavigationMenu = React.forwardRef<
479
483
  };
480
484
  }
481
485
  }
486
+ // If scopeLoading is true, keep using the previous stable scope to prevent clearing permissions
482
487
 
483
488
  const stableScope = stableScopeRef.current;
484
489
 
@@ -491,15 +496,31 @@ export const NavigationMenu = React.forwardRef<
491
496
 
492
497
  // NEW: Phase 2 - Enhanced Security Features
493
498
  // Filter navigation items based on permissions using RBAC hooks
499
+ // Use ref to preserve previous filtered items during permission refetches (e.g., when event changes)
500
+ const previousFilteredItemsRef = React.useRef<NavigationItem[]>([]);
501
+
494
502
  const filteredItems = React.useMemo(() => {
495
503
  // Security: If filtering is enabled but we're missing required context or still loading, show NO items
496
504
  // This prevents security risk of showing items before permissions are verified
497
505
  if (filterByPermissions) {
498
- if (!authContext || !rbacContext || scopeLoading || permissionsLoading || !resolvedScope?.organisationId) {
506
+ // During initial load or when scope is loading, show nothing
507
+ if (!authContext || !rbacContext || scopeLoading || !resolvedScope?.organisationId) {
499
508
  // Still loading - show nothing to prevent security risk
500
509
  return [];
501
510
  }
502
511
 
512
+ // During permission refetch (after initial load), preserve previous items if we have them
513
+ // This prevents navigation from disappearing when switching events
514
+ if (permissionsLoading) {
515
+ // If we have previous items, keep showing them during refetch
516
+ // This prevents the navigation from disappearing when event changes
517
+ if (previousFilteredItemsRef.current.length > 0) {
518
+ return previousFilteredItemsRef.current;
519
+ }
520
+ // Otherwise, show nothing during initial load
521
+ return [];
522
+ }
523
+
503
524
  // If there's an error or empty permission map after loading, show nothing
504
525
  // Security: Better to show nothing than risk showing unauthorized items
505
526
  if (permissionsError || !permissionMap || Object.keys(permissionMap).length === 0) {
@@ -675,6 +696,9 @@ export const NavigationMenu = React.forwardRef<
675
696
  .map(item => filterItem(item))
676
697
  .filter((item): item is NavigationItem => item !== null);
677
698
 
699
+ // Update the ref with the new filtered items so we can preserve them during refetches
700
+ previousFilteredItemsRef.current = filtered;
701
+
678
702
  return filtered;
679
703
  }, [
680
704
  items,