@jmruthers/pace-core 0.5.186 → 0.5.187

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 (284) hide show
  1. package/dist/{DataTable-Z9NLVJh0.d.ts → DataTable-IVYljGJ6.d.ts} +1 -1
  2. package/dist/{DataTable-IX2NBUTP.js → DataTable-K3RJRSOX.js} +7 -7
  3. package/dist/{PublicPageProvider-DIzEzwKl.d.ts → PublicPageProvider-DrLDztHt.d.ts} +211 -106
  4. package/dist/{UnifiedAuthProvider-A4BCQRJY.js → UnifiedAuthProvider-B76OWOAT.js} +2 -2
  5. package/dist/{api-BMFCXVQX.js → api-YP7XD5L6.js} +3 -3
  6. package/dist/{audit-WRS3KJKI.js → audit-B5P6FFIR.js} +2 -2
  7. package/dist/{chunk-445GEP27.js → chunk-3IC5WCMO.js} +33 -8
  8. package/dist/chunk-3IC5WCMO.js.map +1 -0
  9. package/dist/{chunk-OALXJH4Y.js → chunk-3NFNJOO7.js} +8 -8
  10. package/dist/chunk-3NFNJOO7.js.map +1 -0
  11. package/dist/{chunk-FSFQFJCU.js → chunk-63FOKYGO.js} +174 -6
  12. package/dist/chunk-63FOKYGO.js.map +1 -0
  13. package/dist/{chunk-TC7D3CR3.js → chunk-C4OYJOV4.js} +556 -101
  14. package/dist/chunk-C4OYJOV4.js.map +1 -0
  15. package/dist/{chunk-HGPQUCBC.js → chunk-FMTK4XNN.js} +3 -3
  16. package/dist/{chunk-U6WNSFX5.js → chunk-HEHYGYOX.js} +279 -44
  17. package/dist/chunk-HEHYGYOX.js.map +1 -0
  18. package/dist/{chunk-XAUHJD3L.js → chunk-K2JGDXGU.js} +2 -2
  19. package/dist/{chunk-HDCUMOOI.js → chunk-LBBUPSSC.js} +792 -559
  20. package/dist/chunk-LBBUPSSC.js.map +1 -0
  21. package/dist/{chunk-UQWSHFVX.js → chunk-SAUPYVLF.js} +1 -1
  22. package/dist/{chunk-UQWSHFVX.js.map → chunk-SAUPYVLF.js.map} +1 -1
  23. package/dist/{chunk-GRIQLQ52.js → chunk-T6ZJVI3A.js} +27 -23
  24. package/dist/chunk-T6ZJVI3A.js.map +1 -0
  25. package/dist/{chunk-DAGICKHT.js → chunk-ULX5FYEM.js} +3 -3
  26. package/dist/{chunk-FXFJRTKI.js → chunk-WK2Y6TGA.js} +3 -3
  27. package/dist/chunk-WK2Y6TGA.js.map +1 -0
  28. package/dist/chunk-YHCN776L.js +447 -0
  29. package/dist/chunk-YHCN776L.js.map +1 -0
  30. package/dist/components.d.ts +4 -4
  31. package/dist/components.js +12 -10
  32. package/dist/components.js.map +1 -1
  33. package/dist/{file-reference-PRTSLxKx.d.ts → file-reference-D037xOFK.d.ts} +0 -1
  34. package/dist/hooks.d.ts +221 -6
  35. package/dist/hooks.js +146 -49
  36. package/dist/hooks.js.map +1 -1
  37. package/dist/index.d.ts +24 -9
  38. package/dist/index.js +62 -28
  39. package/dist/index.js.map +1 -1
  40. package/dist/providers.js +1 -1
  41. package/dist/rbac/index.d.ts +124 -7
  42. package/dist/rbac/index.js +27 -7
  43. package/dist/{types-DUyCRSTj.d.ts → types-Bwgl--Xo.d.ts} +162 -1
  44. package/dist/types.d.ts +1 -1
  45. package/dist/types.js +1 -1
  46. package/dist/{usePublicRouteParams-D71QLlg4.d.ts → usePublicRouteParams-CTDELQ7H.d.ts} +2 -2
  47. package/dist/utils.d.ts +213 -3
  48. package/dist/utils.js +22 -2
  49. package/dist/utils.js.map +1 -1
  50. package/docs/api/classes/ColumnFactory.md +1 -1
  51. package/docs/api/classes/ErrorBoundary.md +1 -1
  52. package/docs/api/classes/InvalidScopeError.md +1 -1
  53. package/docs/api/classes/Logger.md +1 -1
  54. package/docs/api/classes/MissingUserContextError.md +1 -1
  55. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  56. package/docs/api/classes/PermissionDeniedError.md +1 -1
  57. package/docs/api/classes/RBACAuditManager.md +21 -17
  58. package/docs/api/classes/RBACCache.md +31 -23
  59. package/docs/api/classes/RBACEngine.md +5 -5
  60. package/docs/api/classes/RBACError.md +1 -1
  61. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  62. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  63. package/docs/api/classes/StorageUtils.md +1 -1
  64. package/docs/api/enums/FileCategory.md +1 -1
  65. package/docs/api/enums/LogLevel.md +1 -1
  66. package/docs/api/enums/RBACErrorCode.md +1 -1
  67. package/docs/api/enums/RPCFunction.md +1 -1
  68. package/docs/api/interfaces/AddressFieldProps.md +241 -0
  69. package/docs/api/interfaces/AddressFieldRef.md +94 -0
  70. package/docs/api/interfaces/AggregateConfig.md +1 -1
  71. package/docs/api/interfaces/AutocompleteOptions.md +75 -0
  72. package/docs/api/interfaces/BadgeProps.md +1 -1
  73. package/docs/api/interfaces/ButtonProps.md +1 -1
  74. package/docs/api/interfaces/CalendarProps.md +1 -1
  75. package/docs/api/interfaces/CardProps.md +1 -1
  76. package/docs/api/interfaces/ColorPalette.md +1 -1
  77. package/docs/api/interfaces/ColorShade.md +1 -1
  78. package/docs/api/interfaces/ComplianceResult.md +1 -1
  79. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  80. package/docs/api/interfaces/DataRecord.md +1 -1
  81. package/docs/api/interfaces/DataTableAction.md +1 -1
  82. package/docs/api/interfaces/DataTableColumn.md +1 -1
  83. package/docs/api/interfaces/DataTableProps.md +1 -1
  84. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  85. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  86. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  87. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  88. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  89. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  90. package/docs/api/interfaces/ExportColumn.md +1 -1
  91. package/docs/api/interfaces/ExportOptions.md +1 -1
  92. package/docs/api/interfaces/FileDisplayProps.md +15 -15
  93. package/docs/api/interfaces/FileMetadata.md +1 -1
  94. package/docs/api/interfaces/FileReference.md +1 -1
  95. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  96. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  97. package/docs/api/interfaces/FileUploadProps.md +1 -1
  98. package/docs/api/interfaces/FooterProps.md +1 -1
  99. package/docs/api/interfaces/FormFieldProps.md +1 -1
  100. package/docs/api/interfaces/FormProps.md +1 -1
  101. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoggerConfig.md +1 -1
  106. package/docs/api/interfaces/LoginFormProps.md +1 -1
  107. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  108. package/docs/api/interfaces/NavigationContextType.md +1 -1
  109. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  110. package/docs/api/interfaces/NavigationItem.md +1 -1
  111. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  112. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  113. package/docs/api/interfaces/Organisation.md +1 -1
  114. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  115. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  116. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  117. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  118. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  119. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  120. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  121. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  122. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  123. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  124. package/docs/api/interfaces/PaletteData.md +1 -1
  125. package/docs/api/interfaces/ParsedAddress.md +120 -0
  126. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  127. package/docs/api/interfaces/ProgressProps.md +1 -1
  128. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  131. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  132. package/docs/api/interfaces/QuickFix.md +1 -1
  133. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  134. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  135. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  136. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  137. package/docs/api/interfaces/RBACConfig.md +26 -3
  138. package/docs/api/interfaces/RBACContext.md +1 -1
  139. package/docs/api/interfaces/RBACLogger.md +5 -5
  140. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  141. package/docs/api/interfaces/RBACPerformanceMetrics.md +138 -0
  142. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  143. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  144. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  145. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  146. package/docs/api/interfaces/RBACResult.md +1 -1
  147. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  148. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  149. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  150. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  151. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  152. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  153. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  154. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  155. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  156. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  157. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  158. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  159. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  160. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  161. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  162. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  163. package/docs/api/interfaces/RouteConfig.md +1 -1
  164. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  165. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  166. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  167. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  168. package/docs/api/interfaces/SetupIssue.md +1 -1
  169. package/docs/api/interfaces/StorageConfig.md +1 -1
  170. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  171. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  172. package/docs/api/interfaces/StorageListOptions.md +1 -1
  173. package/docs/api/interfaces/StorageListResult.md +1 -1
  174. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  175. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  176. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  177. package/docs/api/interfaces/StyleImport.md +1 -1
  178. package/docs/api/interfaces/SwitchProps.md +1 -1
  179. package/docs/api/interfaces/TabsContentProps.md +1 -1
  180. package/docs/api/interfaces/TabsListProps.md +1 -1
  181. package/docs/api/interfaces/TabsProps.md +1 -1
  182. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  183. package/docs/api/interfaces/TextareaProps.md +1 -1
  184. package/docs/api/interfaces/ToastActionElement.md +1 -1
  185. package/docs/api/interfaces/ToastProps.md +1 -1
  186. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  187. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  188. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  189. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  190. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  191. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  192. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  193. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  194. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  195. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  196. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  197. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  198. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  199. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  200. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  201. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  202. package/docs/api/interfaces/UserEventAccess.md +1 -1
  203. package/docs/api/interfaces/UserMenuProps.md +1 -1
  204. package/docs/api/interfaces/UserProfile.md +1 -1
  205. package/docs/api/modules.md +318 -59
  206. package/docs/best-practices/performance.md +11 -0
  207. package/docs/implementation-guides/file-upload-storage.md +29 -0
  208. package/docs/rbac/README.md +2 -1
  209. package/docs/rbac/api-reference.md +11 -0
  210. package/docs/rbac/performance.md +320 -0
  211. package/docs/standards/01-architecture-standard.md +5 -0
  212. package/docs/standards/05-security-standard.md +12 -0
  213. package/package.json +1 -1
  214. package/src/components/AddressField/AddressField.test.tsx +411 -0
  215. package/src/components/AddressField/AddressField.tsx +323 -0
  216. package/src/components/AddressField/README.md +336 -0
  217. package/src/components/AddressField/index.ts +10 -0
  218. package/src/components/AddressField/types.ts +65 -0
  219. package/src/components/FileDisplay/FileDisplay.test.tsx +454 -0
  220. package/src/components/FileDisplay/FileDisplay.tsx +28 -1
  221. package/src/components/index.ts +2 -0
  222. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +30 -5
  223. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +11 -10
  224. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +31 -6
  225. package/src/hooks/index.ts +6 -0
  226. package/src/hooks/public/usePublicFileDisplay.ts +8 -10
  227. package/src/hooks/useAddressAutocomplete.test.ts +318 -0
  228. package/src/hooks/useAddressAutocomplete.ts +268 -0
  229. package/src/hooks/useFileDisplay.ts +3 -15
  230. package/src/hooks/useFileReference.test.ts +20 -3
  231. package/src/hooks/useFileReference.ts +3 -24
  232. package/src/hooks/useFileUrlCache.ts +246 -0
  233. package/src/hooks/useInactivityTracker.ts +31 -20
  234. package/src/hooks/useOrganisationSecurity.test.ts +10 -7
  235. package/src/hooks/useOrganisationSecurity.ts +3 -3
  236. package/src/hooks/useQueryCache.ts +315 -0
  237. package/src/index.ts +2 -0
  238. package/src/providers/services/EventServiceProvider.tsx +4 -1
  239. package/src/rbac/api.test.ts +21 -6
  240. package/src/rbac/api.ts +32 -11
  241. package/src/rbac/audit-batched.ts +223 -0
  242. package/src/rbac/audit-enhanced.ts +2 -2
  243. package/src/rbac/audit.test.ts +6 -5
  244. package/src/rbac/audit.ts +34 -6
  245. package/src/rbac/cache-invalidation.ts +63 -12
  246. package/src/rbac/cache.test.ts +2 -2
  247. package/src/rbac/cache.ts +61 -14
  248. package/src/rbac/components/PagePermissionGuard.tsx +19 -10
  249. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +248 -0
  250. package/src/rbac/config.ts +9 -0
  251. package/src/rbac/engine.ts +2 -21
  252. package/src/rbac/hooks/usePermissions.ts +21 -5
  253. package/src/rbac/index.ts +19 -0
  254. package/src/rbac/performance.ts +210 -0
  255. package/src/rbac/request-deduplication.ts +87 -0
  256. package/src/rbac/utils/deep-equal.ts +93 -0
  257. package/src/types/file-reference.ts +0 -1
  258. package/src/utils/file-reference/__tests__/file-reference.test.ts +31 -4
  259. package/src/utils/file-reference/index.ts +44 -15
  260. package/src/utils/google-places/googlePlacesUtils.test.ts +403 -0
  261. package/src/utils/google-places/googlePlacesUtils.ts +475 -0
  262. package/src/utils/google-places/index.ts +26 -0
  263. package/src/utils/google-places/loadGoogleMapsScript.ts +207 -0
  264. package/src/utils/google-places/types.ts +94 -0
  265. package/src/utils/index.ts +23 -0
  266. package/src/utils/request-deduplication.ts +165 -0
  267. package/src/utils/storage/helpers.ts +143 -4
  268. package/dist/chunk-445GEP27.js.map +0 -1
  269. package/dist/chunk-FMUCXFII.js +0 -76
  270. package/dist/chunk-FMUCXFII.js.map +0 -1
  271. package/dist/chunk-FSFQFJCU.js.map +0 -1
  272. package/dist/chunk-FXFJRTKI.js.map +0 -1
  273. package/dist/chunk-GRIQLQ52.js.map +0 -1
  274. package/dist/chunk-HDCUMOOI.js.map +0 -1
  275. package/dist/chunk-OALXJH4Y.js.map +0 -1
  276. package/dist/chunk-TC7D3CR3.js.map +0 -1
  277. package/dist/chunk-U6WNSFX5.js.map +0 -1
  278. /package/dist/{DataTable-IX2NBUTP.js.map → DataTable-K3RJRSOX.js.map} +0 -0
  279. /package/dist/{UnifiedAuthProvider-A4BCQRJY.js.map → UnifiedAuthProvider-B76OWOAT.js.map} +0 -0
  280. /package/dist/{api-BMFCXVQX.js.map → api-YP7XD5L6.js.map} +0 -0
  281. /package/dist/{audit-WRS3KJKI.js.map → audit-B5P6FFIR.js.map} +0 -0
  282. /package/dist/{chunk-HGPQUCBC.js.map → chunk-FMTK4XNN.js.map} +0 -0
  283. /package/dist/{chunk-XAUHJD3L.js.map → chunk-K2JGDXGU.js.map} +0 -0
  284. /package/dist/{chunk-DAGICKHT.js.map → chunk-ULX5FYEM.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/DateTimeField/DateTimeField.tsx","../src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx"],"sourcesContent":["/**\n * @file DateTimeField Component\n * @package @jmruthers/pace-core\n * @module Components/DateTimeField\n * @since 0.1.0\n *\n * Form input component for datetime values with timezone support.\n * Handles UTC ↔ timezone conversion automatically.\n *\n * Features:\n * - Automatic UTC ↔ timezone conversion\n * - Prevents unwanted conversions during user editing\n * - Shows timezone information when not UTC\n * - Supports both ISO string and Date object values\n * - Uses native datetime-local input type\n * - Accessible form field with proper labels\n *\n * @example\n * ```tsx\n * import { DateTimeField } from '@jmruthers/pace-core/components';\n * import { useState } from 'react';\n *\n * function EventForm() {\n * const [startTime, setStartTime] = useState<string>();\n *\n * return (\n * <DateTimeField\n * label=\"Start Time\"\n * value={startTime}\n * onChange={setStartTime}\n * timezone=\"America/New_York\"\n * required\n * />\n * );\n * }\n * ```\n *\n * @accessibility\n * - Proper label association with htmlFor\n * - Required field indicators\n * - Screen reader friendly\n * - Keyboard navigation support\n * - Focus management\n */\n\nimport * as React from 'react';\nimport { format, parse } from 'date-fns';\nimport { Label } from '../Label';\nimport { Input } from '../Input';\nimport { cn } from '../../utils/core/cn';\nimport { toZonedTime, fromZonedTime, getUserTimeZone } from '../../utils/timezone';\n\n/**\n * Props for the DateTimeField component\n */\nexport interface DateTimeFieldProps {\n /**\n * Field label\n */\n label: string;\n /**\n * UTC date value (ISO string, Date object, or undefined)\n */\n value: string | Date | undefined;\n /**\n * Change handler that receives UTC value (ISO string or Date object)\n */\n onChange: (value: string | Date | undefined) => void;\n /**\n * Target timezone for display (default: 'UTC')\n */\n timezone?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * If true, onChange returns Date object instead of ISO string\n */\n returnAsDate?: boolean;\n /**\n * Input id (auto-generated if not provided)\n */\n id?: string;\n /**\n * Helper text to display below the label\n */\n helperText?: string;\n /**\n * Error message to display\n */\n error?: string;\n}\n\n/**\n * DateTimeField component\n * Form input for datetime values with automatic timezone conversion\n *\n * @param props - DateTimeField configuration\n * @returns JSX.Element - The rendered datetime field\n */\nexport function DateTimeField({\n label,\n value,\n onChange,\n timezone = 'UTC',\n required = false,\n className,\n returnAsDate = false,\n id,\n helperText,\n error\n}: DateTimeFieldProps) {\n const [isEditing, setIsEditing] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const fieldId = id || `datetime-field-${React.useId()}`;\n\n // Convert UTC value to timezone for display\n const getDisplayValue = React.useCallback((): string => {\n if (!value) {\n return '';\n }\n\n try {\n let dateObj: Date;\n if (typeof value === 'string') {\n dateObj = new Date(value);\n } else {\n dateObj = value;\n }\n\n if (!dateObj || isNaN(dateObj.getTime())) {\n return '';\n }\n\n // Convert UTC to timezone\n const zonedDate = toZonedTime(dateObj, timezone);\n\n // Format for datetime-local input (YYYY-MM-DDTHH:mm)\n return format(zonedDate, \"yyyy-MM-dd'T'HH:mm\");\n } catch {\n return '';\n }\n }, [value, timezone]);\n\n const displayValue = isEditing ? undefined : getDisplayValue();\n\n // Handle input change\n const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n setIsEditing(true);\n const inputValue = e.target.value;\n\n if (!inputValue) {\n onChange(undefined);\n return;\n }\n\n try {\n // Parse the datetime-local value (in timezone)\n const localDate = parse(inputValue, \"yyyy-MM-dd'T'HH:mm\", new Date());\n\n if (isNaN(localDate.getTime())) {\n onChange(undefined);\n return;\n }\n\n // Convert from timezone to UTC\n const utcDate = fromZonedTime(localDate, timezone);\n\n // Return as ISO string or Date object\n if (returnAsDate) {\n onChange(utcDate);\n } else {\n onChange(utcDate.toISOString());\n }\n } catch {\n onChange(undefined);\n }\n }, [timezone, returnAsDate, onChange]);\n\n // Handle blur to stop editing mode\n const handleBlur = React.useCallback(() => {\n setIsEditing(false);\n }, []);\n\n // Get timezone display text\n const getTimezoneDisplay = React.useCallback((): string => {\n if (timezone === 'UTC') {\n return '';\n }\n\n const userTz = getUserTimeZone();\n if (timezone === userTz) {\n return 'Local';\n }\n\n return timezone;\n }, [timezone]);\n\n const timezoneDisplay = getTimezoneDisplay();\n\n return (\n <div className={cn('space-y-2', className)}>\n <Label htmlFor={fieldId} required={required} helperText={helperText} error={error}>\n {label}\n </Label>\n <div className=\"relative\">\n <Input\n ref={inputRef}\n id={fieldId}\n type=\"datetime-local\"\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n required={required}\n error={!!error}\n className=\"w-full\"\n />\n {timezoneDisplay && (\n <span className=\"absolute right-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground pointer-events-none\">\n {timezoneDisplay}\n </span>\n )}\n </div>\n </div>\n );\n}\n\n","/**\n * @file DatePickerWithTimezone Component\n * @package @jmruthers/pace-core\n * @module Components/DatePickerWithTimezone\n * @since 0.1.0\n *\n * Date picker component that displays timezone information alongside the calendar.\n * Provides a calendar interface with timezone context for date selection.\n *\n * Features:\n * - Calendar date selection\n * - Timezone display (shows \"Local\" when matches user timezone)\n * - Optional \"Done\" button\n * - Accessible date selection\n * - Keyboard navigation support\n *\n * @example\n * ```tsx\n * import { DatePickerWithTimezone } from '@jmruthers/pace-core/components';\n * import { useState } from 'react';\n *\n * function DateSelector() {\n * const [date, setDate] = useState<Date>();\n *\n * return (\n * <DatePickerWithTimezone\n * selected={date}\n * onSelect={setDate}\n * timezone=\"America/New_York\"\n * onDone={() => console.log('Date selected')}\n * />\n * );\n * }\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Keyboard navigation support\n * - Screen reader friendly\n * - Focus management\n * - Proper ARIA attributes\n */\n\nimport * as React from 'react';\nimport { Calendar } from '../Calendar';\nimport { Button } from '../Button';\nimport { Clock } from 'lucide-react';\nimport { getUserTimeZone } from '../../utils/timezone';\nimport { cn } from '../../utils/core/cn';\n\n/**\n * Props for the DatePickerWithTimezone component\n */\nexport interface DatePickerWithTimezoneProps {\n /**\n * Currently selected date\n */\n selected?: Date;\n /**\n * Date selection handler\n */\n onSelect: (date: Date | undefined) => void;\n /**\n * Optional callback when \"Done\" button is clicked\n */\n onDone?: () => void;\n /**\n * Timezone to display (defaults to user's timezone)\n */\n timezone?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n}\n\n/**\n * DatePickerWithTimezone component\n * Date picker with timezone information display\n *\n * @param props - DatePickerWithTimezone configuration\n * @returns JSX.Element - The rendered date picker with timezone\n */\nexport function DatePickerWithTimezone({\n selected,\n onSelect,\n onDone,\n timezone,\n className\n}: DatePickerWithTimezoneProps) {\n const userTimezone = getUserTimeZone();\n const displayTimezone = timezone || userTimezone;\n const timezoneDisplay = displayTimezone === userTimezone ? 'Local' : displayTimezone;\n\n return (\n <div className={cn('flex flex-col', className)}>\n <div className=\"p-3\">\n <Calendar\n mode=\"single\"\n selected={selected}\n onSelect={onSelect}\n initialFocus\n captionLayout=\"dropdown-buttons\"\n fromYear={1900}\n toYear={2100}\n className=\"p-0\"\n />\n </div>\n\n <div className=\"flex items-center justify-between border-t border-border px-3 py-2\">\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Clock className=\"h-4 w-4\" aria-hidden=\"true\" />\n <span>\n Timezone: <span aria-label={`Timezone ${timezoneDisplay}`}>{timezoneDisplay}</span>\n </span>\n </div>\n {onDone && (\n <Button onClick={onDone} size=\"sm\" className=\"h-8\">\n Done\n </Button>\n )}\n </div>\n </div>\n );\n}\n\n"],"mappings":"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","names":["jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/components/DateTimeField/DateTimeField.tsx","../src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx"],"sourcesContent":["/**\n * @file DateTimeField Component\n * @package @jmruthers/pace-core\n * @module Components/DateTimeField\n * @since 0.1.0\n *\n * Form input component for datetime values with timezone support.\n * Handles UTC ↔ timezone conversion automatically.\n *\n * Features:\n * - Automatic UTC ↔ timezone conversion\n * - Prevents unwanted conversions during user editing\n * - Shows timezone information when not UTC\n * - Supports both ISO string and Date object values\n * - Uses native datetime-local input type\n * - Accessible form field with proper labels\n *\n * @example\n * ```tsx\n * import { DateTimeField } from '@jmruthers/pace-core/components';\n * import { useState } from 'react';\n *\n * function EventForm() {\n * const [startTime, setStartTime] = useState<string>();\n *\n * return (\n * <DateTimeField\n * label=\"Start Time\"\n * value={startTime}\n * onChange={setStartTime}\n * timezone=\"America/New_York\"\n * required\n * />\n * );\n * }\n * ```\n *\n * @accessibility\n * - Proper label association with htmlFor\n * - Required field indicators\n * - Screen reader friendly\n * - Keyboard navigation support\n * - Focus management\n */\n\nimport * as React from 'react';\nimport { format, parse } from 'date-fns';\nimport { Label } from '../Label';\nimport { Input } from '../Input';\nimport { cn } from '../../utils/core/cn';\nimport { toZonedTime, fromZonedTime, getUserTimeZone } from '../../utils/timezone';\n\n/**\n * Props for the DateTimeField component\n */\nexport interface DateTimeFieldProps {\n /**\n * Field label\n */\n label: string;\n /**\n * UTC date value (ISO string, Date object, or undefined)\n */\n value: string | Date | undefined;\n /**\n * Change handler that receives UTC value (ISO string or Date object)\n */\n onChange: (value: string | Date | undefined) => void;\n /**\n * Target timezone for display (default: 'UTC')\n */\n timezone?: string;\n /**\n * Whether the field is required\n */\n required?: boolean;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * If true, onChange returns Date object instead of ISO string\n */\n returnAsDate?: boolean;\n /**\n * Input id (auto-generated if not provided)\n */\n id?: string;\n /**\n * Helper text to display below the label\n */\n helperText?: string;\n /**\n * Error message to display\n */\n error?: string;\n}\n\n/**\n * DateTimeField component\n * Form input for datetime values with automatic timezone conversion\n *\n * @param props - DateTimeField configuration\n * @returns JSX.Element - The rendered datetime field\n */\nexport function DateTimeField({\n label,\n value,\n onChange,\n timezone = 'UTC',\n required = false,\n className,\n returnAsDate = false,\n id,\n helperText,\n error\n}: DateTimeFieldProps) {\n const [isEditing, setIsEditing] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const fieldId = id || `datetime-field-${React.useId()}`;\n\n // Convert UTC value to timezone for display\n const getDisplayValue = React.useCallback((): string => {\n if (!value) {\n return '';\n }\n\n try {\n let dateObj: Date;\n if (typeof value === 'string') {\n dateObj = new Date(value);\n } else {\n dateObj = value;\n }\n\n if (!dateObj || isNaN(dateObj.getTime())) {\n return '';\n }\n\n // Convert UTC to timezone\n const zonedDate = toZonedTime(dateObj, timezone);\n\n // Format for datetime-local input (YYYY-MM-DDTHH:mm)\n return format(zonedDate, \"yyyy-MM-dd'T'HH:mm\");\n } catch {\n return '';\n }\n }, [value, timezone]);\n\n const displayValue = isEditing ? undefined : getDisplayValue();\n\n // Handle input change\n const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n setIsEditing(true);\n const inputValue = e.target.value;\n\n if (!inputValue) {\n onChange(undefined);\n return;\n }\n\n try {\n // Parse the datetime-local value (in timezone)\n const localDate = parse(inputValue, \"yyyy-MM-dd'T'HH:mm\", new Date());\n\n if (isNaN(localDate.getTime())) {\n onChange(undefined);\n return;\n }\n\n // Convert from timezone to UTC\n const utcDate = fromZonedTime(localDate, timezone);\n\n // Return as ISO string or Date object\n if (returnAsDate) {\n onChange(utcDate);\n } else {\n onChange(utcDate.toISOString());\n }\n } catch {\n onChange(undefined);\n }\n }, [timezone, returnAsDate, onChange]);\n\n // Handle blur to stop editing mode\n const handleBlur = React.useCallback(() => {\n setIsEditing(false);\n }, []);\n\n // Get timezone display text\n const getTimezoneDisplay = React.useCallback((): string => {\n if (timezone === 'UTC') {\n return '';\n }\n\n const userTz = getUserTimeZone();\n if (timezone === userTz) {\n return 'Local';\n }\n\n return timezone;\n }, [timezone]);\n\n const timezoneDisplay = getTimezoneDisplay();\n\n return (\n <div className={cn('space-y-2', className)}>\n <Label htmlFor={fieldId} required={required} helperText={helperText} error={error}>\n {label}\n </Label>\n <div className=\"relative\">\n <Input\n ref={inputRef}\n id={fieldId}\n type=\"datetime-local\"\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n required={required}\n error={!!error}\n className=\"w-full\"\n />\n {timezoneDisplay && (\n <span className=\"absolute right-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground pointer-events-none\">\n {timezoneDisplay}\n </span>\n )}\n </div>\n </div>\n );\n}\n\n","/**\n * @file DatePickerWithTimezone Component\n * @package @jmruthers/pace-core\n * @module Components/DatePickerWithTimezone\n * @since 0.1.0\n *\n * Date picker component that displays timezone information alongside the calendar.\n * Provides a calendar interface with timezone context for date selection.\n *\n * Features:\n * - Calendar date selection\n * - Timezone display (shows \"Local\" when matches user timezone)\n * - Optional \"Done\" button\n * - Accessible date selection\n * - Keyboard navigation support\n *\n * @example\n * ```tsx\n * import { DatePickerWithTimezone } from '@jmruthers/pace-core/components';\n * import { useState } from 'react';\n *\n * function DateSelector() {\n * const [date, setDate] = useState<Date>();\n *\n * return (\n * <DatePickerWithTimezone\n * selected={date}\n * onSelect={setDate}\n * timezone=\"America/New_York\"\n * onDone={() => console.log('Date selected')}\n * />\n * );\n * }\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Keyboard navigation support\n * - Screen reader friendly\n * - Focus management\n * - Proper ARIA attributes\n */\n\nimport * as React from 'react';\nimport { Calendar } from '../Calendar';\nimport { Button } from '../Button';\nimport { Clock } from 'lucide-react';\nimport { getUserTimeZone } from '../../utils/timezone';\nimport { cn } from '../../utils/core/cn';\n\n/**\n * Props for the DatePickerWithTimezone component\n */\nexport interface DatePickerWithTimezoneProps {\n /**\n * Currently selected date\n */\n selected?: Date;\n /**\n * Date selection handler\n */\n onSelect: (date: Date | undefined) => void;\n /**\n * Optional callback when \"Done\" button is clicked\n */\n onDone?: () => void;\n /**\n * Timezone to display (defaults to user's timezone)\n */\n timezone?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n}\n\n/**\n * DatePickerWithTimezone component\n * Date picker with timezone information display\n *\n * @param props - DatePickerWithTimezone configuration\n * @returns JSX.Element - The rendered date picker with timezone\n */\nexport function DatePickerWithTimezone({\n selected,\n onSelect,\n onDone,\n timezone,\n className\n}: DatePickerWithTimezoneProps) {\n const userTimezone = getUserTimeZone();\n const displayTimezone = timezone || userTimezone;\n const timezoneDisplay = displayTimezone === userTimezone ? 'Local' : displayTimezone;\n\n return (\n <div className={cn('flex flex-col', className)}>\n <div className=\"p-3\">\n <Calendar\n mode=\"single\"\n selected={selected}\n onSelect={onSelect}\n initialFocus\n captionLayout=\"dropdown-buttons\"\n fromYear={1900}\n toYear={2100}\n className=\"p-0\"\n />\n </div>\n\n <div className=\"flex items-center justify-between border-t border-border px-3 py-2\">\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Clock className=\"h-4 w-4\" aria-hidden=\"true\" />\n <span>\n Timezone: <span aria-label={`Timezone ${timezoneDisplay}`}>{timezoneDisplay}</span>\n </span>\n </div>\n {onDone && (\n <Button onClick={onDone} size=\"sm\" className=\"h-8\">\n Done\n </Button>\n )}\n </div>\n </div>\n );\n}\n\n"],"mappings":"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","names":["jsx","jsxs"]}
@@ -76,7 +76,6 @@ interface FileReferenceService {
76
76
  }
77
77
  interface StorageUploadOptions {
78
78
  orgId: string;
79
- category: FileCategory;
80
79
  isPublic?: boolean;
81
80
  customPath?: string;
82
81
  }
package/dist/hooks.d.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  export { u as useToast } from './useToast-C8gR5ir4.js';
2
- import { S as StorageUploadOptions, a as StorageUploadResult, b as StorageListOptions, c as StorageListResult, d as StorageFileInfo } from './usePublicRouteParams-D71QLlg4.js';
3
- export { O as OrganisationSecurityHook, l as UseAppConfigReturn, i as UseFormDialogOptions, j as UseFormDialogReturn, U as UseOrganisationPermissionsReturn, F as UsePublicEventLogoOptions, E as UsePublicEventLogoReturn, B as UsePublicEventOptions, A as UsePublicEventReturn, D as UsePublicFileDisplayOptions, C as UsePublicFileDisplayReturn, G as UsePublicRouteParamsReturn, n as clearPublicEventCache, q as clearPublicFileDisplayCache, t as clearPublicLogoCache, z as extractEventCodeFromPath, y as generatePublicRoutePath, o as getPublicEventCacheStats, r as getPublicFileDisplayCacheStats, v as getPublicLogoCacheStats, k as useAppConfig, u as useEventTheme, h as useFormDialog, e as useOrganisationPermissions, f as useOrganisationSecurity, m as usePublicEvent, x as usePublicEventCode, s as usePublicEventLogo, p as usePublicFileDisplay, w as usePublicRouteParams, g as useZodForm } from './usePublicRouteParams-D71QLlg4.js';
2
+ import { a as StorageUploadOptions, c as StorageUploadResult, e as StorageListOptions, f as StorageListResult, m as StorageFileInfo } from './usePublicRouteParams-CTDELQ7H.js';
3
+ export { O as OrganisationSecurityHook, K as UseAppConfigReturn, U as UseFormDialogOptions, h as UseFormDialogReturn, J as UseOrganisationPermissionsReturn, H as UsePublicEventLogoOptions, G as UsePublicEventLogoReturn, C as UsePublicEventOptions, B as UsePublicEventReturn, E as UsePublicFileDisplayOptions, D as UsePublicFileDisplayReturn, I as UsePublicRouteParamsReturn, o as clearPublicEventCache, r as clearPublicFileDisplayCache, v as clearPublicLogoCache, A as extractEventCodeFromPath, z as generatePublicRoutePath, p as getPublicEventCacheStats, s as getPublicFileDisplayCacheStats, w as getPublicLogoCacheStats, k as useAppConfig, l as useEventTheme, g as useFormDialog, i as useOrganisationPermissions, j as useOrganisationSecurity, n as usePublicEvent, y as usePublicEventCode, t as usePublicEventLogo, q as usePublicFileDisplay, x as usePublicRouteParams, u as useZodForm } from './usePublicRouteParams-CTDELQ7H.js';
4
4
  import * as React$1 from 'react';
5
5
  import { SortingState, ColumnFiltersState, ExpandedState } from '@tanstack/react-table';
6
+ import { d as DataRecord, g as PerformanceConfig, S as ServerSideConfig, C as ChunkingConfig, i as SearchIndexConfig, h as PaginationMode, k as ServerSideParams, l as ServerSideResponse, A as AutocompleteOptions, m as GooglePlaceAutocompletePrediction, P as ParsedAddress } from './types-Bwgl--Xo.js';
6
7
  export { u as useComponentPerformance } from './useComponentPerformance-DE9l5RkL.js';
7
- import { c as DataRecord, P as PerformanceConfig, S as ServerSideConfig, C as ChunkingConfig, g as SearchIndexConfig, f as PaginationMode, i as ServerSideParams, j as ServerSideResponse } from './types-DUyCRSTj.js';
8
8
  import { SupabaseClient } from '@supabase/supabase-js';
9
- import { F as FileCategory, a as FileReference } from './file-reference-PRTSLxKx.js';
9
+ import { D as Database } from './database.generated-DI89OQeI.js';
10
+ import { F as FileCategory, a as FileReference } from './file-reference-D037xOFK.js';
10
11
  import 'react-hook-form';
11
12
  import 'zod';
12
13
  import './event-CW5YB_2p.js';
13
14
  import './core-CUElvH_C.js';
14
- import './database.generated-DI89OQeI.js';
15
15
 
16
16
  /**
17
17
  * @file Error Handling Stubs
@@ -227,6 +227,64 @@ declare function useDataTableState(options: UseDataTableStateOptions): {
227
227
  };
228
228
  };
229
229
 
230
+ /**
231
+ * @file Address Autocomplete Hook
232
+ * @package @jmruthers/pace-core
233
+ * @module Hooks/AddressAutocomplete
234
+ * @since 0.1.0
235
+ *
236
+ * Hook for managing Google Places API autocomplete functionality.
237
+ * Provides debounced input, caching, and address selection.
238
+ */
239
+
240
+ interface UseAddressAutocompleteOptions {
241
+ /** Google Places API options */
242
+ autocompleteOptions?: AutocompleteOptions;
243
+ /** Debounce delay in milliseconds (default: 300) */
244
+ debounceDelay?: number;
245
+ /** Enable caching (default: true) */
246
+ cacheEnabled?: boolean;
247
+ /** Cache TTL configuration */
248
+ cacheTTL?: {
249
+ /** Autocomplete cache TTL in seconds (default: 3600 = 1 hour) */
250
+ autocomplete?: number;
251
+ /** Place details cache TTL in seconds (default: 86400 = 24 hours) */
252
+ placeDetails?: number;
253
+ };
254
+ }
255
+ interface UseAddressAutocompleteReturn {
256
+ /** Array of autocomplete suggestions */
257
+ suggestions: GooglePlaceAutocompletePrediction[];
258
+ /** Loading state */
259
+ isLoading: boolean;
260
+ /** Error state */
261
+ error: Error | null;
262
+ /** Select an address by place_id */
263
+ selectAddress: (placeId: string) => Promise<ParsedAddress | null>;
264
+ /** Get address by place_id (uses cache) */
265
+ getAddressByPlaceId: (placeId: string) => Promise<ParsedAddress | null>;
266
+ /** Clear suggestions */
267
+ clearSuggestions: () => void;
268
+ }
269
+ /**
270
+ * Hook for Google Places API autocomplete with caching
271
+ *
272
+ * @param apiKey - Google Places API key
273
+ * @param inputValue - Current input value
274
+ * @param options - Hook configuration options
275
+ * @returns Autocomplete state and functions
276
+ *
277
+ * @example
278
+ * ```tsx
279
+ * const { suggestions, isLoading, selectAddress } = useAddressAutocomplete(
280
+ * apiKey,
281
+ * inputValue,
282
+ * { debounceDelay: 300, cacheEnabled: true }
283
+ * );
284
+ * ```
285
+ */
286
+ declare function useAddressAutocomplete(apiKey: string, inputValue: string, options?: UseAddressAutocompleteOptions): UseAddressAutocompleteReturn;
287
+
230
288
  /**
231
289
  * @file useSecureDataAccess Hook
232
290
  * @package @jmruthers/pace-core
@@ -359,6 +417,86 @@ declare function usePerformanceMonitor(componentName: string, enabled?: boolean,
359
417
  endMeasurement: () => void;
360
418
  };
361
419
 
420
+ /**
421
+ * Query Result Caching Hook
422
+ * @package @jmruthers/pace-core
423
+ * @module Hooks/QueryCache
424
+ * @since 2.0.0
425
+ *
426
+ * Provides in-memory caching for frequently accessed data to eliminate duplicate queries.
427
+ * Useful for caching user profiles, app pages, and other relatively static data.
428
+ */
429
+
430
+ interface UseQueryCacheOptions {
431
+ /** Time to live in seconds (default: 300 = 5 minutes) */
432
+ ttl?: number;
433
+ /** Whether to enable caching (default: true) */
434
+ enabled?: boolean;
435
+ }
436
+ interface UseQueryCacheReturn {
437
+ /** Get cached query result or fetch if not cached */
438
+ getCachedQuery: <T>(table: string, filterKey: string, filterValue: string, fetchFn: () => Promise<T>, options?: UseQueryCacheOptions) => Promise<T>;
439
+ /** Invalidate a specific cached query */
440
+ invalidateQuery: (table: string, filterKey: string, filterValue: string) => void;
441
+ /** Clear all cached queries */
442
+ clearCache: () => void;
443
+ /** Get cache statistics */
444
+ getCacheStats: () => {
445
+ size: number;
446
+ keys: string[];
447
+ };
448
+ }
449
+ /**
450
+ * Hook for query result caching
451
+ *
452
+ * Provides caching for frequently accessed data to eliminate duplicate queries.
453
+ * Automatically handles cache expiration and cleanup.
454
+ *
455
+ * @param supabase - Supabase client (optional, can be passed in fetchFn)
456
+ * @returns Query cache utilities
457
+ *
458
+ * @example
459
+ * ```tsx
460
+ * const { getCachedQuery } = useQueryCache(supabase);
461
+ *
462
+ * const person = await getCachedQuery(
463
+ * 'pace_person',
464
+ * 'user_id',
465
+ * userId,
466
+ * async () => {
467
+ * const { data } = await supabase
468
+ * .from('pace_person')
469
+ * .select('id, first_name, last_name, email')
470
+ * .eq('user_id', userId)
471
+ * .single();
472
+ * return data;
473
+ * },
474
+ * { ttl: 300 } // 5 minutes
475
+ * );
476
+ * ```
477
+ */
478
+ declare function useQueryCache(supabase?: SupabaseClient<Database>): UseQueryCacheReturn;
479
+ /**
480
+ * Pre-configured cache helpers for common queries
481
+ */
482
+ declare const queryCacheHelpers: {
483
+ /**
484
+ * Cache pace_person queries by user_id
485
+ * TTL: 5 minutes
486
+ */
487
+ pacePersonByUserId: <T>(supabase: SupabaseClient<Database>, userId: string, fetchFn: () => Promise<T>) => Promise<T>;
488
+ /**
489
+ * Cache pace_member queries by person_id
490
+ * TTL: 5 minutes
491
+ */
492
+ paceMemberByPersonId: <T>(supabase: SupabaseClient<Database>, personId: string, fetchFn: () => Promise<T>) => Promise<T>;
493
+ /**
494
+ * Cache rbac_app_pages queries by app_id
495
+ * TTL: 15 minutes (app pages are relatively static)
496
+ */
497
+ rbacAppPagesByAppId: <T>(supabase: SupabaseClient<Database>, appId: string, fetchFn: () => Promise<T>) => Promise<T>;
498
+ };
499
+
362
500
  /**
363
501
  * @file File Display Hook (Authenticated)
364
502
  * @package @jmruthers/pace-core
@@ -459,6 +597,83 @@ declare function getFileDisplayCacheStats(): {
459
597
  */
460
598
  declare function invalidateFileDisplayCache(table_name: string, record_id: string, organisation_id: string, category?: FileCategory): void;
461
599
 
600
+ /**
601
+ * @file File URL Cache Hook
602
+ * @package @jmruthers/pace-core
603
+ * @module Hooks
604
+ *
605
+ * Centralized caching hook for file URLs to prevent duplicate requests
606
+ * and improve performance across components.
607
+ *
608
+ * Features:
609
+ * - TTL-based caching matching signed URL expiration (3600s)
610
+ * - Automatic cache cleanup
611
+ * - Supports both public and signed URLs
612
+ * - Thread-safe cache operations
613
+ *
614
+ * @example
615
+ * ```tsx
616
+ * import { useFileUrlCache } from '@jmruthers/pace-core';
617
+ *
618
+ * function MyComponent() {
619
+ * const { getUrl, setUrl, clearCache } = useFileUrlCache();
620
+ *
621
+ * const url = await getUrl(fileReference, supabase, organisationId);
622
+ * return <img src={url} alt="File" />;
623
+ * }
624
+ * ```
625
+ */
626
+
627
+ interface UseFileUrlCacheReturn {
628
+ /**
629
+ * Get URL for a file reference, using cache if available
630
+ * @param fileReference - File reference to get URL for
631
+ * @param supabase - Supabase client instance
632
+ * @param organisationId - Organisation ID for signed URLs
633
+ * @param ttl - Time to live in milliseconds (default: 3600000 = 1 hour)
634
+ * @returns Promise resolving to URL string or null
635
+ */
636
+ getUrl: (fileReference: FileReference, supabase: SupabaseClient, organisationId: string, ttl?: number) => Promise<string | null>;
637
+ /**
638
+ * Set URL in cache
639
+ * @param fileReference - File reference
640
+ * @param url - URL to cache
641
+ * @param ttl - Time to live in milliseconds (default: 3600000 = 1 hour)
642
+ */
643
+ setUrl: (fileReference: FileReference, url: string, ttl?: number) => void;
644
+ /**
645
+ * Get URL from cache without generating if missing
646
+ * @param fileReference - File reference
647
+ * @returns Cached URL or null if not in cache or expired
648
+ */
649
+ getCachedUrl: (fileReference: FileReference) => string | null;
650
+ /**
651
+ * Clear cache for a specific file reference
652
+ * @param fileReference - File reference to clear
653
+ */
654
+ clearFile: (fileReference: FileReference) => void;
655
+ /**
656
+ * Clear all cached URLs
657
+ */
658
+ clearCache: () => void;
659
+ /**
660
+ * Get cache statistics
661
+ */
662
+ getCacheStats: () => {
663
+ size: number;
664
+ maxSize: number;
665
+ };
666
+ }
667
+ /**
668
+ * Hook for centralized file URL caching
669
+ *
670
+ * This hook provides a shared cache for file URLs across all components,
671
+ * preventing duplicate requests for the same file.
672
+ *
673
+ * @returns Cache operations and utilities
674
+ */
675
+ declare function useFileUrlCache(): UseFileUrlCacheReturn;
676
+
462
677
  /**
463
678
  * React hook for storage operations
464
679
  */
@@ -549,4 +764,4 @@ interface UsePreventTabReloadOptions {
549
764
  */
550
765
  declare function usePreventTabReload(options?: UsePreventTabReloadOptions): void;
551
766
 
552
- export { type SecureDataAccessReturn, type UseDataTablePerformanceOptions, type UseDataTablePerformanceReturn, type UseFileDisplayOptions, type UseFileDisplayReturn, type UsePreventTabReloadOptions, type UseStorageOptions, type UseStorageReturn, clearFileDisplayCache, getFileDisplayCacheStats, invalidateFileDisplayCache, useDataTablePerformance, useDataTableState, useDebounce, useFileDisplay, useFileUpload, useFocusManagement, useFocusTrap, useIsMobile, useKeyboardShortcuts, usePerformanceMonitor, usePreventTabReload, useSecureDataAccess, useStorage };
767
+ export { type SecureDataAccessReturn, type UseAddressAutocompleteOptions, type UseAddressAutocompleteReturn, type UseDataTablePerformanceOptions, type UseDataTablePerformanceReturn, type UseFileDisplayOptions, type UseFileDisplayReturn, type UseFileUrlCacheReturn, type UsePreventTabReloadOptions, type UseQueryCacheOptions, type UseQueryCacheReturn, type UseStorageOptions, type UseStorageReturn, clearFileDisplayCache, getFileDisplayCacheStats, invalidateFileDisplayCache, queryCacheHelpers, useAddressAutocomplete, useDataTablePerformance, useDataTableState, useDebounce, useFileDisplay, useFileUpload, useFileUrlCache, useFocusManagement, useFocusTrap, useIsMobile, useKeyboardShortcuts, usePerformanceMonitor, usePreventTabReload, useQueryCache, useSecureDataAccess, useStorage };
package/dist/hooks.js CHANGED
@@ -13,10 +13,10 @@ import {
13
13
  usePublicEventLogo,
14
14
  usePublicRouteParams,
15
15
  useZodForm
16
- } from "./chunk-OALXJH4Y.js";
16
+ } from "./chunk-3NFNJOO7.js";
17
17
  import {
18
18
  useSecureDataAccess
19
- } from "./chunk-DAGICKHT.js";
19
+ } from "./chunk-ULX5FYEM.js";
20
20
  import {
21
21
  archiveFile,
22
22
  clearFileDisplayCache,
@@ -28,21 +28,25 @@ import {
28
28
  getSignedUrl,
29
29
  invalidateFileDisplayCache,
30
30
  listFiles,
31
+ queryCacheHelpers,
31
32
  uploadFile,
33
+ useAddressAutocomplete,
32
34
  useAppConfig,
35
+ useDebounce,
33
36
  useEventTheme,
34
37
  useFileDisplay,
35
38
  usePreventTabReload,
36
- usePublicFileDisplay
37
- } from "./chunk-TC7D3CR3.js";
39
+ usePublicFileDisplay,
40
+ useQueryCache
41
+ } from "./chunk-C4OYJOV4.js";
38
42
  import {
39
43
  useDataTablePerformance,
40
44
  useToast
41
45
  } from "./chunk-6C4YBBJM.js";
42
- import "./chunk-XAUHJD3L.js";
46
+ import "./chunk-K2JGDXGU.js";
43
47
  import "./chunk-KQCRWDSA.js";
44
- import "./chunk-FXFJRTKI.js";
45
- import "./chunk-UQWSHFVX.js";
48
+ import "./chunk-WK2Y6TGA.js";
49
+ import "./chunk-SAUPYVLF.js";
46
50
  import "./chunk-QXHPKYJV.js";
47
51
  import {
48
52
  useComponentPerformance
@@ -50,7 +54,7 @@ import {
50
54
  import {
51
55
  PERFORMANCE_BUDGETS,
52
56
  performanceBudgetMonitor
53
- } from "./chunk-FMUCXFII.js";
57
+ } from "./chunk-YHCN776L.js";
54
58
  import "./chunk-VBXEHIUJ.js";
55
59
  import "./chunk-SQGMNID3.js";
56
60
  import {
@@ -337,31 +341,16 @@ function useIsMobile() {
337
341
  return !!isMobile;
338
342
  }
339
343
 
340
- // src/hooks/useDebounce.ts
341
- import { useState as useState2, useEffect as useEffect5 } from "react";
342
- function useDebounce(value, delay) {
343
- const [debouncedValue, setDebouncedValue] = useState2(value);
344
- useEffect5(() => {
345
- const handler = setTimeout(() => {
346
- setDebouncedValue(value);
347
- }, delay);
348
- return () => {
349
- clearTimeout(handler);
350
- };
351
- }, [value, delay]);
352
- return debouncedValue;
353
- }
354
-
355
344
  // src/hooks/useDataTableState.ts
356
- import { useState as useState3, useCallback as useCallback4, useMemo } from "react";
345
+ import { useState as useState2, useCallback as useCallback4, useMemo } from "react";
357
346
  function useDataTableState(options) {
358
347
  const { initialPageSize = 10, data } = options;
359
- const [sorting, setSorting] = useState3([]);
360
- const [columnFilters, setColumnFilters] = useState3([]);
361
- const [expanded, setExpanded] = useState3({});
362
- const [pageSize, setPageSize] = useState3(initialPageSize);
363
- const [pageIndex, setPageIndex] = useState3(0);
364
- const [selectedRows, setSelectedRows] = useState3([]);
348
+ const [sorting, setSorting] = useState2([]);
349
+ const [columnFilters, setColumnFilters] = useState2([]);
350
+ const [expanded, setExpanded] = useState2({});
351
+ const [pageSize, setPageSize] = useState2(initialPageSize);
352
+ const [pageIndex, setPageIndex] = useState2(0);
353
+ const [selectedRows, setSelectedRows] = useState2([]);
365
354
  const resetState = useCallback4(() => {
366
355
  setSorting([]);
367
356
  setColumnFilters([]);
@@ -412,7 +401,7 @@ function useDataTableState(options) {
412
401
  }
413
402
 
414
403
  // src/hooks/usePerformanceMonitor.ts
415
- import { useEffect as useEffect6, useRef as useRef3, useCallback as useCallback5 } from "react";
404
+ import { useEffect as useEffect5, useRef as useRef3, useCallback as useCallback5 } from "react";
416
405
  var log = createLogger("usePerformanceMonitor");
417
406
  function usePerformanceMonitor(componentName, enabled = import.meta.env.MODE === "development", budgetName = "COMPONENT_RENDER") {
418
407
  const renderStartTime = useRef3(0);
@@ -463,7 +452,7 @@ function usePerformanceMonitor(componentName, enabled = import.meta.env.MODE ===
463
452
  efficiency: budget.threshold > 0 ? (budget.threshold - averageTime) / budget.threshold : 0
464
453
  };
465
454
  }, [budgetName, getAverageRenderTime]);
466
- useEffect6(() => {
455
+ useEffect5(() => {
467
456
  startMeasurement();
468
457
  return endMeasurement;
469
458
  });
@@ -476,16 +465,120 @@ function usePerformanceMonitor(componentName, enabled = import.meta.env.MODE ===
476
465
  };
477
466
  }
478
467
 
468
+ // src/hooks/useFileUrlCache.ts
469
+ import { useRef as useRef4, useCallback as useCallback6 } from "react";
470
+ var globalUrlCache = /* @__PURE__ */ new Map();
471
+ var MAX_CACHE_SIZE = 500;
472
+ var DEFAULT_TTL_MS = 3600 * 1e3;
473
+ function getCacheKey(fileReference) {
474
+ return `file-url:${fileReference.id}:${fileReference.is_public ? "public" : "private"}`;
475
+ }
476
+ function cleanupCache() {
477
+ const now = Date.now();
478
+ for (const [key, value] of globalUrlCache.entries()) {
479
+ if (value.expiresAt < now) {
480
+ globalUrlCache.delete(key);
481
+ }
482
+ }
483
+ if (globalUrlCache.size > MAX_CACHE_SIZE) {
484
+ const entries = Array.from(globalUrlCache.entries());
485
+ entries.sort((a, b) => a[1].expiresAt - b[1].expiresAt);
486
+ const toRemove = Math.floor(MAX_CACHE_SIZE * 0.2);
487
+ for (let i = 0; i < toRemove && i < entries.length; i++) {
488
+ globalUrlCache.delete(entries[i][0]);
489
+ }
490
+ }
491
+ }
492
+ function useFileUrlCache() {
493
+ const cleanupIntervalRef = useRef4(null);
494
+ if (cleanupIntervalRef.current === null && typeof window !== "undefined") {
495
+ cleanupIntervalRef.current = window.setInterval(() => {
496
+ cleanupCache();
497
+ }, 5 * 60 * 1e3);
498
+ }
499
+ const getUrl = useCallback6(async (fileReference, supabase, organisationId, ttl = DEFAULT_TTL_MS) => {
500
+ const cacheKey = getCacheKey(fileReference);
501
+ const cached = globalUrlCache.get(cacheKey);
502
+ const now = Date.now();
503
+ if (cached && cached.expiresAt > now) {
504
+ return cached.url;
505
+ }
506
+ let url = null;
507
+ try {
508
+ if (fileReference.is_public) {
509
+ url = getPublicUrl(supabase, fileReference.file_path, true);
510
+ } else {
511
+ const signedUrlResult = await getSignedUrl(supabase, fileReference.file_path, {
512
+ appName: "pace-core",
513
+ orgId: organisationId,
514
+ expiresIn: Math.floor(ttl / 1e3)
515
+ // Convert ms to seconds
516
+ });
517
+ url = signedUrlResult?.url || null;
518
+ }
519
+ if (url) {
520
+ globalUrlCache.set(cacheKey, {
521
+ url,
522
+ expiresAt: now + ttl
523
+ });
524
+ cleanupCache();
525
+ }
526
+ return url;
527
+ } catch (error) {
528
+ console.error("Failed to generate file URL:", error);
529
+ return null;
530
+ }
531
+ }, []);
532
+ const setUrl = useCallback6((fileReference, url, ttl = DEFAULT_TTL_MS) => {
533
+ const cacheKey = getCacheKey(fileReference);
534
+ globalUrlCache.set(cacheKey, {
535
+ url,
536
+ expiresAt: Date.now() + ttl
537
+ });
538
+ cleanupCache();
539
+ }, []);
540
+ const getCachedUrl = useCallback6((fileReference) => {
541
+ const cacheKey = getCacheKey(fileReference);
542
+ const cached = globalUrlCache.get(cacheKey);
543
+ const now = Date.now();
544
+ if (cached && cached.expiresAt > now) {
545
+ return cached.url;
546
+ }
547
+ return null;
548
+ }, []);
549
+ const clearFile = useCallback6((fileReference) => {
550
+ const cacheKey = getCacheKey(fileReference);
551
+ globalUrlCache.delete(cacheKey);
552
+ }, []);
553
+ const clearCache = useCallback6(() => {
554
+ globalUrlCache.clear();
555
+ }, []);
556
+ const getCacheStats = useCallback6(() => {
557
+ return {
558
+ size: globalUrlCache.size,
559
+ maxSize: MAX_CACHE_SIZE
560
+ };
561
+ }, []);
562
+ return {
563
+ getUrl,
564
+ setUrl,
565
+ getCachedUrl,
566
+ clearFile,
567
+ clearCache,
568
+ getCacheStats
569
+ };
570
+ }
571
+
479
572
  // src/hooks/useStorage.ts
480
- import { useState as useState4, useCallback as useCallback6 } from "react";
573
+ import { useState as useState3, useCallback as useCallback7 } from "react";
481
574
  var log2 = createLogger("useStorage");
482
575
  function useStorage({ supabase, appName, orgId }) {
483
- const [isUploading, setIsUploading] = useState4(false);
484
- const [uploadError, setUploadError] = useState4(null);
485
- const [isListing, setIsListing] = useState4(false);
486
- const [listError, setListError] = useState4(null);
487
- const [files, setFiles] = useState4([]);
488
- const handleUploadFile = useCallback6(async (file, options = {}) => {
576
+ const [isUploading, setIsUploading] = useState3(false);
577
+ const [uploadError, setUploadError] = useState3(null);
578
+ const [isListing, setIsListing] = useState3(false);
579
+ const [listError, setListError] = useState3(null);
580
+ const [files, setFiles] = useState3([]);
581
+ const handleUploadFile = useCallback7(async (file, options = {}) => {
489
582
  setIsUploading(true);
490
583
  setUploadError(null);
491
584
  try {
@@ -513,10 +606,10 @@ function useStorage({ supabase, appName, orgId }) {
513
606
  setIsUploading(false);
514
607
  }
515
608
  }, [supabase, appName, orgId]);
516
- const handleGetPublicUrl = useCallback6((path) => {
609
+ const handleGetPublicUrl = useCallback7((path) => {
517
610
  return getPublicUrl(supabase, path);
518
611
  }, [supabase]);
519
- const handleGetSignedUrl = useCallback6(async (path, expiresIn) => {
612
+ const handleGetSignedUrl = useCallback7(async (path, expiresIn) => {
520
613
  try {
521
614
  const result = await getSignedUrl(supabase, path, {
522
615
  appName,
@@ -529,7 +622,7 @@ function useStorage({ supabase, appName, orgId }) {
529
622
  return null;
530
623
  }
531
624
  }, [supabase, appName, orgId]);
532
- const handleDeleteFile = useCallback6(async (path) => {
625
+ const handleDeleteFile = useCallback7(async (path) => {
533
626
  try {
534
627
  const result = await deleteFile(supabase, path);
535
628
  if (result.success) {
@@ -543,7 +636,7 @@ function useStorage({ supabase, appName, orgId }) {
543
636
  };
544
637
  }
545
638
  }, [supabase]);
546
- const handleArchiveFile = useCallback6(async (path) => {
639
+ const handleArchiveFile = useCallback7(async (path) => {
547
640
  try {
548
641
  const result = await archiveFile(supabase, path, { appName, orgId });
549
642
  if (result.success) {
@@ -557,7 +650,7 @@ function useStorage({ supabase, appName, orgId }) {
557
650
  };
558
651
  }
559
652
  }, [supabase, appName, orgId]);
560
- const handleListFiles = useCallback6(async (options = {}) => {
653
+ const handleListFiles = useCallback7(async (options = {}) => {
561
654
  setIsListing(true);
562
655
  setListError(null);
563
656
  try {
@@ -577,7 +670,7 @@ function useStorage({ supabase, appName, orgId }) {
577
670
  setIsListing(false);
578
671
  }
579
672
  }, [supabase, appName, orgId]);
580
- const refreshFiles = useCallback6(async () => {
673
+ const refreshFiles = useCallback7(async () => {
581
674
  await handleListFiles();
582
675
  }, [handleListFiles]);
583
676
  return {
@@ -607,10 +700,10 @@ function useStorage({ supabase, appName, orgId }) {
607
700
  };
608
701
  }
609
702
  function useFileUpload({ supabase, appName, orgId }) {
610
- const [uploadProgress, setUploadProgress] = useState4(0);
611
- const [isUploading, setIsUploading] = useState4(false);
612
- const [uploadError, setUploadError] = useState4(null);
613
- const uploadWithProgress = useCallback6(async (file, options = {}) => {
703
+ const [uploadProgress, setUploadProgress] = useState3(0);
704
+ const [isUploading, setIsUploading] = useState3(false);
705
+ const [uploadError, setUploadError] = useState3(null);
706
+ const uploadWithProgress = useCallback7(async (file, options = {}) => {
614
707
  setIsUploading(true);
615
708
  setUploadProgress(0);
616
709
  setUploadError(null);
@@ -662,6 +755,8 @@ export {
662
755
  getPublicFileDisplayCacheStats,
663
756
  getPublicLogoCacheStats,
664
757
  invalidateFileDisplayCache,
758
+ queryCacheHelpers,
759
+ useAddressAutocomplete,
665
760
  useAppConfig,
666
761
  useComponentPerformance,
667
762
  useDataTablePerformance,
@@ -670,6 +765,7 @@ export {
670
765
  useEventTheme,
671
766
  useFileDisplay,
672
767
  useFileUpload,
768
+ useFileUrlCache,
673
769
  useFocusManagement,
674
770
  useFocusTrap,
675
771
  useFormDialog,
@@ -684,6 +780,7 @@ export {
684
780
  usePublicEventLogo,
685
781
  usePublicFileDisplay,
686
782
  usePublicRouteParams,
783
+ useQueryCache,
687
784
  useSecureDataAccess,
688
785
  useStorage,
689
786
  useToast,