@jmruthers/pace-core 0.5.186 → 0.5.188
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataTable-IX2NBUTP.js → DataTable-GUFUNZ3N.js} +7 -7
- package/dist/{DataTable-Z9NLVJh0.d.ts → DataTable-IVYljGJ6.d.ts} +1 -1
- package/dist/{PublicPageProvider-DIzEzwKl.d.ts → PublicPageProvider-DrLDztHt.d.ts} +211 -106
- package/dist/{UnifiedAuthProvider-A4BCQRJY.js → UnifiedAuthProvider-643PUAIM.js} +2 -2
- package/dist/{api-BMFCXVQX.js → api-YP7XD5L6.js} +3 -3
- package/dist/{audit-WRS3KJKI.js → audit-B5P6FFIR.js} +2 -2
- package/dist/{chunk-HGPQUCBC.js → chunk-2UUZZJFT.js} +3 -3
- package/dist/{chunk-445GEP27.js → chunk-3GOZZZYH.js} +33 -8
- package/dist/chunk-3GOZZZYH.js.map +1 -0
- package/dist/{chunk-FSFQFJCU.js → chunk-63FOKYGO.js} +174 -6
- package/dist/chunk-63FOKYGO.js.map +1 -0
- package/dist/{chunk-DAGICKHT.js → chunk-DDM4CCYT.js} +3 -3
- package/dist/{chunk-XAUHJD3L.js → chunk-E7UAOUMY.js} +2 -2
- package/dist/{chunk-HDCUMOOI.js → chunk-EFCLXK7F.js} +792 -559
- package/dist/chunk-EFCLXK7F.js.map +1 -0
- package/dist/{chunk-U6WNSFX5.js → chunk-HEHYGYOX.js} +279 -44
- package/dist/chunk-HEHYGYOX.js.map +1 -0
- package/dist/{chunk-GRIQLQ52.js → chunk-IM4QE42D.js} +27 -23
- package/dist/chunk-IM4QE42D.js.map +1 -0
- package/dist/{chunk-OALXJH4Y.js → chunk-IPCH26AG.js} +8 -8
- package/dist/chunk-IPCH26AG.js.map +1 -0
- package/dist/{chunk-UQWSHFVX.js → chunk-SAUPYVLF.js} +1 -1
- package/dist/{chunk-UQWSHFVX.js.map → chunk-SAUPYVLF.js.map} +1 -1
- package/dist/{chunk-TC7D3CR3.js → chunk-UNOTYLQF.js} +556 -101
- package/dist/chunk-UNOTYLQF.js.map +1 -0
- package/dist/{chunk-FXFJRTKI.js → chunk-VGZZXKBR.js} +5 -5
- package/dist/chunk-VGZZXKBR.js.map +1 -0
- package/dist/chunk-YHCN776L.js +447 -0
- package/dist/chunk-YHCN776L.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +12 -10
- package/dist/components.js.map +1 -1
- package/dist/{file-reference-PRTSLxKx.d.ts → file-reference-D037xOFK.d.ts} +0 -1
- package/dist/hooks.d.ts +221 -6
- package/dist/hooks.js +146 -49
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +24 -9
- package/dist/index.js +62 -28
- package/dist/index.js.map +1 -1
- package/dist/providers.js +1 -1
- package/dist/rbac/index.d.ts +124 -7
- package/dist/rbac/index.js +27 -7
- package/dist/{types-DUyCRSTj.d.ts → types-Bwgl--Xo.d.ts} +162 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-D71QLlg4.d.ts → usePublicRouteParams-CTDELQ7H.d.ts} +2 -2
- package/dist/utils.d.ts +213 -3
- package/dist/utils.js +22 -2
- package/dist/utils.js.map +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +21 -17
- package/docs/api/classes/RBACCache.md +31 -23
- package/docs/api/classes/RBACEngine.md +5 -5
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +241 -0
- package/docs/api/interfaces/AddressFieldRef.md +94 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +75 -0
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +15 -15
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +120 -0
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +26 -3
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +5 -5
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +138 -0
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +318 -59
- package/docs/best-practices/performance.md +11 -0
- package/docs/getting-started/examples/README.md +2 -2
- package/docs/implementation-guides/file-upload-storage.md +29 -0
- package/docs/implementation-guides/public-pages.md +140 -1230
- package/docs/rbac/README.md +2 -1
- package/docs/rbac/api-reference.md +11 -0
- package/docs/rbac/performance.md +320 -0
- package/docs/standards/01-architecture-standard.md +5 -0
- package/docs/standards/05-security-standard.md +14 -0
- package/docs/standards/07-rbac-and-rls-standard.md +356 -0
- package/package.json +1 -1
- package/src/__tests__/public-recipe-view.test.ts +199 -0
- package/src/__tests__/rls-policies.test.ts +333 -0
- package/src/components/AddressField/AddressField.test.tsx +411 -0
- package/src/components/AddressField/AddressField.tsx +323 -0
- package/src/components/AddressField/README.md +336 -0
- package/src/components/AddressField/index.ts +10 -0
- package/src/components/AddressField/types.ts +65 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +454 -0
- package/src/components/FileDisplay/FileDisplay.tsx +28 -1
- package/src/components/index.ts +2 -0
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +30 -5
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +11 -10
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +31 -6
- package/src/hooks/index.ts +6 -0
- package/src/hooks/public/usePublicFileDisplay.ts +8 -10
- package/src/hooks/useAddressAutocomplete.test.ts +318 -0
- package/src/hooks/useAddressAutocomplete.ts +268 -0
- package/src/hooks/useFileDisplay.ts +3 -15
- package/src/hooks/useFileReference.test.ts +20 -3
- package/src/hooks/useFileReference.ts +3 -24
- package/src/hooks/useFileUrlCache.ts +246 -0
- package/src/hooks/useInactivityTracker.ts +31 -20
- package/src/hooks/useOrganisationSecurity.test.ts +10 -7
- package/src/hooks/useOrganisationSecurity.ts +3 -3
- package/src/hooks/useQueryCache.ts +315 -0
- package/src/index.ts +2 -0
- package/src/providers/services/EventServiceProvider.tsx +4 -1
- package/src/rbac/api.test.ts +21 -6
- package/src/rbac/api.ts +32 -11
- package/src/rbac/audit-batched.ts +223 -0
- package/src/rbac/audit-enhanced.ts +2 -2
- package/src/rbac/audit.test.ts +6 -5
- package/src/rbac/audit.ts +34 -6
- package/src/rbac/cache-invalidation.ts +63 -12
- package/src/rbac/cache.test.ts +2 -2
- package/src/rbac/cache.ts +61 -14
- package/src/rbac/components/PagePermissionGuard.tsx +19 -10
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +248 -0
- package/src/rbac/config.ts +9 -0
- package/src/rbac/engine.ts +2 -21
- package/src/rbac/hooks/usePermissions.ts +21 -5
- package/src/rbac/index.ts +19 -0
- package/src/rbac/performance.ts +210 -0
- package/src/rbac/request-deduplication.ts +87 -0
- package/src/rbac/utils/deep-equal.ts +93 -0
- package/src/services/OrganisationService.ts +5 -4
- package/src/types/file-reference.ts +0 -1
- package/src/utils/file-reference/__tests__/file-reference.test.ts +31 -4
- package/src/utils/file-reference/index.ts +44 -15
- package/src/utils/google-places/googlePlacesUtils.test.ts +403 -0
- package/src/utils/google-places/googlePlacesUtils.ts +475 -0
- package/src/utils/google-places/index.ts +26 -0
- package/src/utils/google-places/loadGoogleMapsScript.ts +207 -0
- package/src/utils/google-places/types.ts +94 -0
- package/src/utils/index.ts +23 -0
- package/src/utils/request-deduplication.ts +165 -0
- package/src/utils/storage/helpers.ts +143 -4
- package/dist/chunk-445GEP27.js.map +0 -1
- package/dist/chunk-FMUCXFII.js +0 -76
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-FSFQFJCU.js.map +0 -1
- package/dist/chunk-FXFJRTKI.js.map +0 -1
- package/dist/chunk-GRIQLQ52.js.map +0 -1
- package/dist/chunk-HDCUMOOI.js.map +0 -1
- package/dist/chunk-OALXJH4Y.js.map +0 -1
- package/dist/chunk-TC7D3CR3.js.map +0 -1
- package/dist/chunk-U6WNSFX5.js.map +0 -1
- /package/dist/{DataTable-IX2NBUTP.js.map → DataTable-GUFUNZ3N.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-A4BCQRJY.js.map → UnifiedAuthProvider-643PUAIM.js.map} +0 -0
- /package/dist/{api-BMFCXVQX.js.map → api-YP7XD5L6.js.map} +0 -0
- /package/dist/{audit-WRS3KJKI.js.map → audit-B5P6FFIR.js.map} +0 -0
- /package/dist/{chunk-HGPQUCBC.js.map → chunk-2UUZZJFT.js.map} +0 -0
- /package/dist/{chunk-DAGICKHT.js.map → chunk-DDM4CCYT.js.map} +0 -0
- /package/dist/{chunk-XAUHJD3L.js.map → chunk-E7UAOUMY.js.map} +0 -0
package/dist/components.js.map
CHANGED
|
@@ -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"]}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
export { u as useToast } from './useToast-C8gR5ir4.js';
|
|
2
|
-
import {
|
|
3
|
-
export { O as OrganisationSecurityHook,
|
|
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 {
|
|
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-
|
|
16
|
+
} from "./chunk-IPCH26AG.js";
|
|
17
17
|
import {
|
|
18
18
|
useSecureDataAccess
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-DDM4CCYT.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
|
-
|
|
39
|
+
usePublicFileDisplay,
|
|
40
|
+
useQueryCache
|
|
41
|
+
} from "./chunk-UNOTYLQF.js";
|
|
38
42
|
import {
|
|
39
43
|
useDataTablePerformance,
|
|
40
44
|
useToast
|
|
41
45
|
} from "./chunk-6C4YBBJM.js";
|
|
42
|
-
import "./chunk-
|
|
46
|
+
import "./chunk-E7UAOUMY.js";
|
|
43
47
|
import "./chunk-KQCRWDSA.js";
|
|
44
|
-
import "./chunk-
|
|
45
|
-
import "./chunk-
|
|
48
|
+
import "./chunk-VGZZXKBR.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-
|
|
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
|
|
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] =
|
|
360
|
-
const [columnFilters, setColumnFilters] =
|
|
361
|
-
const [expanded, setExpanded] =
|
|
362
|
-
const [pageSize, setPageSize] =
|
|
363
|
-
const [pageIndex, setPageIndex] =
|
|
364
|
-
const [selectedRows, setSelectedRows] =
|
|
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
|
|
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
|
-
|
|
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
|
|
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] =
|
|
484
|
-
const [uploadError, setUploadError] =
|
|
485
|
-
const [isListing, setIsListing] =
|
|
486
|
-
const [listError, setListError] =
|
|
487
|
-
const [files, setFiles] =
|
|
488
|
-
const handleUploadFile =
|
|
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 =
|
|
609
|
+
const handleGetPublicUrl = useCallback7((path) => {
|
|
517
610
|
return getPublicUrl(supabase, path);
|
|
518
611
|
}, [supabase]);
|
|
519
|
-
const handleGetSignedUrl =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
611
|
-
const [isUploading, setIsUploading] =
|
|
612
|
-
const [uploadError, setUploadError] =
|
|
613
|
-
const uploadWithProgress =
|
|
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,
|