@jmruthers/pace-core 0.5.185 → 0.5.186
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/{PublicPageProvider-BABf6JCh.d.ts → PublicPageProvider-DIzEzwKl.d.ts} +4 -2
- package/dist/{chunk-STTZQK2I.js → chunk-DAGICKHT.js} +7 -5
- package/dist/chunk-DAGICKHT.js.map +1 -0
- package/dist/{chunk-AISXLWGZ.js → chunk-GRIQLQ52.js} +2 -2
- package/dist/{chunk-HC67NW5K.js → chunk-HDCUMOOI.js} +125 -47
- package/dist/chunk-HDCUMOOI.js.map +1 -0
- package/dist/{chunk-OKI34GZD.js → chunk-OALXJH4Y.js} +2 -2
- package/dist/{chunk-MX3EIJGQ.js → chunk-TC7D3CR3.js} +86 -7
- package/dist/chunk-TC7D3CR3.js.map +1 -0
- package/dist/{chunk-IXSNYUCT.js → chunk-UQWSHFVX.js} +1 -1
- package/dist/chunk-UQWSHFVX.js.map +1 -0
- package/dist/components.d.ts +2 -2
- package/dist/components.js +3 -3
- package/dist/{database.generated-CBmg2950.d.ts → database.generated-DI89OQeI.d.ts} +63 -9
- package/dist/{file-reference-BjR39ktt.d.ts → file-reference-PRTSLxKx.d.ts} +3 -0
- package/dist/hooks.d.ts +49 -5
- package/dist/hooks.js +6 -4
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +2 -2
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-CvnC3d-e.d.ts → usePublicRouteParams-D71QLlg4.d.ts} +2 -2
- package/dist/utils.d.ts +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 +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +5 -5
- 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/AggregateConfig.md +1 -1
- 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 +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +33 -9
- package/docs/api/interfaces/FileUploadProps.md +36 -14
- 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 +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
- 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 +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- 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 +2 -2
- 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 +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- 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 +17 -17
- package/docs/api-reference/components.md +26 -12
- package/docs/implementation-guides/file-reference-system.md +24 -2
- package/docs/implementation-guides/file-upload-storage.md +9 -1
- package/package.json +1 -1
- package/scripts/check-pace-core-compliance.js +512 -0
- package/src/components/FileUpload/FileUpload.test.tsx +2 -0
- package/src/components/FileUpload/FileUpload.tsx +7 -1
- package/src/components/Header/Header.tsx +2 -5
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +134 -1
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useFileReference.test.ts +1 -0
- package/src/hooks/usePreventTabReload.ts +106 -0
- package/src/hooks/useSecureDataAccess.ts +2 -2
- package/src/rbac/__tests__/rbac-role-isolation.test.ts +456 -0
- package/src/styles/core.css +5 -5
- package/src/types/database.generated.ts +63 -9
- package/src/types/file-reference.ts +3 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +58 -4
- package/src/utils/file-reference/index.ts +12 -2
- package/src/utils/security/secureDataAccess.ts +1 -1
- package/src/utils/storage/helpers.ts +68 -0
- package/dist/chunk-HC67NW5K.js.map +0 -1
- package/dist/chunk-IXSNYUCT.js.map +0 -1
- package/dist/chunk-MX3EIJGQ.js.map +0 -1
- package/dist/chunk-STTZQK2I.js.map +0 -1
- /package/dist/{chunk-AISXLWGZ.js.map → chunk-GRIQLQ52.js.map} +0 -0
- /package/dist/{chunk-OKI34GZD.js.map → chunk-OALXJH4Y.js.map} +0 -0
|
@@ -16,7 +16,7 @@ import { z } from 'zod';
|
|
|
16
16
|
import { User, SupabaseClient, createClient } from '@supabase/supabase-js';
|
|
17
17
|
import { P as Permission, A as AccessLevel } from './types-UU913iLA.js';
|
|
18
18
|
import { E as Event, O as Organisation } from './event-CW5YB_2p.js';
|
|
19
|
-
import { F as FileCategory, e as FileUploadResult, U as UploadProgress, c as FileUploadOptions, a as FileReference } from './file-reference-
|
|
19
|
+
import { F as FileCategory, e as FileUploadResult, U as UploadProgress, c as FileUploadOptions, a as FileReference } from './file-reference-PRTSLxKx.js';
|
|
20
20
|
|
|
21
21
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
22
22
|
/** Visual variant of the button */
|
|
@@ -3496,7 +3496,9 @@ interface FileUploadProps {
|
|
|
3496
3496
|
organisation_id: string;
|
|
3497
3497
|
app_id?: string;
|
|
3498
3498
|
category: FileCategory;
|
|
3499
|
+
folder: string;
|
|
3499
3500
|
pageContext: string;
|
|
3501
|
+
event_id?: string;
|
|
3500
3502
|
accept?: string;
|
|
3501
3503
|
maxSize?: number;
|
|
3502
3504
|
multiple?: boolean;
|
|
@@ -3510,7 +3512,7 @@ interface FileUploadProps {
|
|
|
3510
3512
|
onProgress?: (progress: UploadProgress) => void;
|
|
3511
3513
|
children?: React__default.ReactNode;
|
|
3512
3514
|
}
|
|
3513
|
-
declare function FileUpload({ supabase, table_name, record_id, organisation_id, app_id, category, pageContext, accept, maxSize, // 10MB default
|
|
3515
|
+
declare function FileUpload({ supabase, table_name, record_id, organisation_id, app_id, category, folder, pageContext, event_id, accept, maxSize, // 10MB default
|
|
3514
3516
|
multiple, disabled, isPublic, className, showPreview, showProgress, onUploadSuccess, onUploadError, onProgress, children }: FileUploadProps): react_jsx_runtime.JSX.Element;
|
|
3515
3517
|
|
|
3516
3518
|
interface FileDisplayProps {
|
|
@@ -70,8 +70,9 @@ function useSecureDataAccess() {
|
|
|
70
70
|
"medi_profile_versions",
|
|
71
71
|
"pace_consent",
|
|
72
72
|
"pace_contact",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
73
|
+
"pace_identification",
|
|
74
|
+
"pace_identification_type",
|
|
75
|
+
"pace_qualification",
|
|
75
76
|
"form_responses",
|
|
76
77
|
"form_response_values",
|
|
77
78
|
"forms",
|
|
@@ -207,8 +208,9 @@ function useSecureDataAccess() {
|
|
|
207
208
|
"medi_profile_versions",
|
|
208
209
|
"pace_consent",
|
|
209
210
|
"pace_contact",
|
|
210
|
-
"
|
|
211
|
-
"
|
|
211
|
+
"pace_identification",
|
|
212
|
+
"pace_identification_type",
|
|
213
|
+
"pace_qualification",
|
|
212
214
|
"form_responses",
|
|
213
215
|
"form_response_values",
|
|
214
216
|
"forms",
|
|
@@ -386,4 +388,4 @@ function useSecureDataAccess() {
|
|
|
386
388
|
export {
|
|
387
389
|
useSecureDataAccess
|
|
388
390
|
};
|
|
389
|
-
//# sourceMappingURL=chunk-
|
|
391
|
+
//# sourceMappingURL=chunk-DAGICKHT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useSecureDataAccess.ts"],"sourcesContent":["/**\n * @file useSecureDataAccess Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useSecureDataAccess\n * @since 0.4.0\n *\n * Hook for secure database operations with mandatory organisation context.\n * Ensures all data access is properly scoped to the user's current organisation.\n *\n * @example\n * ```tsx\n * function DataComponent() {\n * const { secureQuery, secureInsert, secureUpdate, secureDelete } = useSecureDataAccess();\n * \n * const loadData = async () => {\n * try {\n * // Automatically includes organisation_id filter\n * const events = await secureQuery('event', '*', { is_visible: true });\n * console.log('Organisation events:', events);\n * } catch (error) {\n * console.error('Failed to load data:', error);\n * }\n * };\n * \n * const createEvent = async (eventData) => {\n * try {\n * // Automatically sets organisation_id\n * const newEvent = await secureInsert('event', eventData);\n * console.log('Created event:', newEvent);\n * } catch (error) {\n * console.error('Failed to create event:', error);\n * }\n * };\n * \n * return (\n * <div>\n * <button onClick={loadData}>Load Data</button>\n * <button onClick={() => createEvent({ event_name: 'New Event' })}>\n * Create Event\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @security\n * - All queries automatically include organisation_id filter\n * - Validates organisation context before any operation\n * - Prevents data leaks between organisations\n * - Error handling for security violations\n * - Type-safe database operations\n */\n\nimport { useCallback, useState, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\nimport { EventServiceContext } from '../providers/services/EventServiceProvider';\nimport { setOrganisationContext } from '../utils/context/organisationContext';\nimport { logger } from '../utils/core/logger';\nimport type { Permission } from '../rbac/types';\nimport type { OrganisationSecurityError } from '../types/organisation';\n\nexport interface SecureDataAccessReturn {\n /** Execute a secure query with organisation filtering */\n secureQuery: <T = any>(\n table: string,\n columns: string,\n filters?: Record<string, any>,\n options?: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n }\n ) => Promise<T[]>;\n \n /** Execute a secure insert with organisation context */\n secureInsert: <T = any>(\n table: string,\n data: Record<string, any>\n ) => Promise<T>;\n \n /** Execute a secure update with organisation filtering */\n secureUpdate: <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ) => Promise<T[]>;\n \n /** Execute a secure delete with organisation filtering */\n secureDelete: (\n table: string,\n filters: Record<string, any>\n ) => Promise<void>;\n \n /** Execute a secure RPC call with organisation context */\n secureRpc: <T = any>(\n functionName: string,\n params?: Record<string, any>\n ) => Promise<T>;\n \n /** Get current organisation ID */\n getCurrentOrganisationId: () => string;\n \n /** Validate organisation context */\n validateContext: () => void;\n \n // NEW: Phase 1 - Enhanced Security Features\n /** Check if data access is allowed for a table and operation */\n isDataAccessAllowed: (table: string, operation: string) => boolean;\n \n /** Get all data access permissions for current user */\n getDataAccessPermissions: () => Record<string, string[]>;\n \n /** Check if strict mode is enabled */\n isStrictMode: boolean;\n \n /** Check if audit logging is enabled */\n isAuditLogEnabled: boolean;\n \n /** Get data access history */\n getDataAccessHistory: () => DataAccessRecord[];\n \n /** Clear data access history */\n clearDataAccessHistory: () => void;\n \n /** Validate data access attempt */\n validateDataAccess: (table: string, operation: string) => boolean;\n}\n\nexport interface DataAccessRecord {\n table: string;\n operation: string;\n userId: string;\n organisationId: string;\n allowed: boolean;\n timestamp: string;\n query?: string;\n filters?: Record<string, any>;\n}\n\n/**\n * Hook for secure data access with automatic organisation filtering\n * \n * All database operations automatically include organisation context:\n * - Queries filter by organisation_id\n * - Inserts include organisation_id\n * - Updates/deletes are scoped to organisation\n * - RPC calls include organisation_id parameter\n */\nexport function useSecureDataAccess(): SecureDataAccessReturn {\n const { supabase, user, session } = useUnifiedAuth();\n const { ensureOrganisationContext } = useOrganisations();\n \n // Get selected event for event-scoped RPC calls\n // Use useContext directly to safely check if EventServiceProvider is available\n const eventServiceContext = useContext(EventServiceContext);\n const selectedEvent = eventServiceContext?.eventService?.getSelectedEvent() || null;\n\n const validateContext = useCallback((): void => {\n if (!supabase) {\n throw new Error('No Supabase client available') as OrganisationSecurityError;\n }\n if (!user || !session) {\n throw new Error('User must be authenticated with valid session') as OrganisationSecurityError;\n }\n \n try {\n ensureOrganisationContext();\n } catch (error) {\n throw new Error('Organisation context is required for data access') as OrganisationSecurityError;\n }\n }, [supabase, user, session, ensureOrganisationContext]);\n\n const getCurrentOrganisationId = useCallback((): string => {\n validateContext();\n const currentOrg = ensureOrganisationContext();\n return currentOrg.id;\n }, [validateContext, ensureOrganisationContext]);\n\n // Set organisation context in database session\n const setOrganisationContextInSession = useCallback(async (organisationId: string): Promise<void> => {\n if (!supabase) {\n throw new Error('No Supabase client available') as OrganisationSecurityError;\n }\n\n await setOrganisationContext(supabase, organisationId);\n }, [supabase]);\n\n const secureQuery = useCallback(async <T = any>(\n table: string,\n columns: string,\n filters: Record<string, any> = {},\n options: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n } = {}\n ): Promise<T[]> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build query with organisation filter\n let query = supabase!\n .from(table)\n .select(columns);\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'event', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',\n // SECURITY: Phase 3A additions - medical and personal data\n 'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',\n 'pace_consent', 'pace_contact', 'pace_identification', 'pace_identification_type', 'pace_qualification',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'\n ];\n \n if (tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply additional filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n // Handle qualified column names (e.g., 'users.role')\n const columnName = key.includes('.') ? key.split('.').pop()! : key;\n query = query.eq(columnName, value);\n }\n });\n\n // Apply options\n if (options.orderBy) {\n // Only use the column name, not a qualified name\n const orderByColumn = options.orderBy.split('.').pop();\n if (orderByColumn) {\n query = query.order(orderByColumn, { ascending: options.ascending ?? true });\n }\n }\n \n if (options.limit) {\n query = query.limit(options.limit);\n }\n \n if (options.offset) {\n query = query.range(options.offset, options.offset + (options.limit || 100) - 1);\n }\n\n const { data, error } = await query;\n \n if (error) {\n logger.error('useSecureDataAccess', 'Query failed', { table, columns, filters, error });\n // NEW: Phase 1 - Record failed data access attempt\n recordDataAccess(table, 'read', false, `SELECT ${columns} FROM ${table}`, filters);\n throw error;\n }\n\n // NEW: Phase 1 - Record successful data access attempt\n recordDataAccess(table, 'read', true, `SELECT ${columns} FROM ${table}`, filters);\n\n return (data as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureInsert = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>\n ): Promise<T> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Ensure organisation_id is set\n const secureData = {\n ...data,\n organisation_id: organisationId\n };\n\n const { data: insertData, error } = await supabase!\n .from(table)\n .insert(secureData)\n .select()\n .single();\n\n if (error) {\n logger.error('useSecureDataAccess', 'Insert failed', { table, data: secureData, error });\n throw error;\n }\n\n return insertData as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureUpdate = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ): Promise<T[]> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Filter out organisation_id from data to prevent manipulation\n const { organisation_id, ...secureData } = data;\n \n // Build update query with organisation filter\n let query = supabase!\n .from(table)\n .update(secureData);\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'event', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member'\n ];\n \n if (tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { data: updateData, error } = await query.select();\n\n if (error) {\n logger.error('useSecureDataAccess', 'Update failed', { table, data: secureData, filters, error });\n throw error;\n }\n\n return (updateData as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureDelete = useCallback(async (\n table: string,\n filters: Record<string, any>\n ): Promise<void> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build delete query with organisation filter\n let query = supabase!\n .from(table)\n .delete();\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'event', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',\n // SECURITY: Phase 3A additions - medical and personal data\n 'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',\n 'pace_consent', 'pace_contact', 'pace_identification', 'pace_identification_type', 'pace_qualification',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'\n ];\n \n if (tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { error } = await query;\n\n if (error) {\n logger.error('useSecureDataAccess', 'Delete failed', { table, filters, error });\n throw error;\n }\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);\n\n const secureRpc = useCallback(async <T = any>(\n functionName: string,\n params: Record<string, any> = {}\n ): Promise<T> => {\n validateContext();\n const organisationId = getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Include organisation_id in RPC parameters\n // Some functions use p_organisation_id instead of organisation_id (to avoid conflicts with RETURNS TABLE columns)\n const functionsWithPOrganisationId = [\n 'data_cake_diners_list',\n 'data_cake_mealplans_list'\n ];\n \n const paramName = functionsWithPOrganisationId.includes(functionName) \n ? 'p_organisation_id' \n : 'organisation_id';\n \n // Functions that need p_event_id for event-app role permission checks\n // Note: Even org-scoped functions (like items, packages, suppliers) need event_id\n // for permission checks when users have event-app roles\n const functionsNeedingEventId = [\n 'data_cake_items_list',\n 'data_cake_packages_list',\n 'data_cake_suppliers_list',\n 'data_cake_diettypes_list',\n 'data_cake_mealtypes_list',\n 'data_cake_diners_list',\n 'data_cake_mealplans_list',\n 'data_cake_dishes_list',\n 'data_cake_recipes_list',\n 'data_cake_meals_list',\n 'data_cake_units_list',\n 'data_cake_orders_list',\n 'app_cake_item_create',\n 'app_cake_item_update',\n 'app_cake_package_create',\n 'app_cake_package_update',\n 'app_cake_supplier_create',\n 'app_cake_supplier_update',\n 'app_cake_supplier_delete',\n 'app_cake_meal_create',\n 'app_cake_meal_update',\n 'app_cake_meal_delete',\n 'app_cake_unit_create',\n 'app_cake_unit_update',\n 'app_cake_unit_delete',\n 'app_cake_delivery_upsert'\n ];\n \n // Build secureParams with correct parameter order\n // For functions that require p_event_id as first parameter, ensure it's first\n const secureParams: Record<string, any> = {};\n \n // Functions where p_event_id is the FIRST required parameter (no default)\n const functionsWithEventIdFirst = [\n 'data_cake_meals_list',\n 'data_cake_units_list'\n ];\n \n // Add p_user_id explicitly for functions that need it (even though it has a default)\n // This ensures parameter matching works correctly\n if (user?.id) {\n secureParams.p_user_id = user.id;\n }\n \n // Add organisation_id parameter\n secureParams[paramName] = organisationId;\n \n // Add p_event_id if function needs it and event is selected\n // CRITICAL: This must be added AFTER organisation_id but BEFORE caller params\n // to ensure it's not overwritten. For data_cake_items_list, p_event_id is the 3rd param.\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n \n // Add any other params passed by caller (limit, offset, etc.)\n // NOTE: This will NOT overwrite p_event_id if caller passes it, but we want to ensure\n // our value takes precedence if event is selected\n Object.assign(secureParams, params);\n \n // Ensure p_event_id is set if needed (after Object.assign, so it overrides caller params)\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n\n const { data, error } = await supabase!.rpc(functionName, secureParams);\n\n if (error) {\n logger.error('useSecureDataAccess', 'RPC failed', { functionName, params: secureParams, error });\n throw error;\n }\n\n return data as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id]);\n\n // NEW: Phase 1 - Enhanced Security Features\n const [dataAccessHistory, setDataAccessHistory] = useState<DataAccessRecord[]>([]);\n const [isStrictMode] = useState(true); // Always enabled in Phase 1\n const [isAuditLogEnabled] = useState(true); // Always enabled in Phase 1\n\n // Check if data access is allowed for a table and operation\n const isDataAccessAllowed = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Use the existing RBAC system to check data access permissions\n // This is a synchronous check for the context - actual permission checking\n // happens in the secure data operations using the RBAC engine\n const permission = `${operation}:data.${table}` as Permission;\n \n // For now, we'll return true and let the secure data operations\n // handle the actual permission checking asynchronously\n // This context is mainly for tracking and audit purposes\n return true;\n }, [user?.id]);\n\n // Get all data access permissions for current user\n const getDataAccessPermissions = useCallback((): Record<string, string[]> => {\n if (!user?.id) return {};\n \n // For now, return empty object - this will be enhanced with actual permission checking\n // when we integrate with the existing RBAC system\n return {};\n }, [user?.id]);\n\n // Get data access history\n const getDataAccessHistory = useCallback((): DataAccessRecord[] => {\n return [...dataAccessHistory];\n }, [dataAccessHistory]);\n\n // Clear data access history\n const clearDataAccessHistory = useCallback(() => {\n setDataAccessHistory([]);\n }, []);\n\n // Validate data access attempt\n const validateDataAccess = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Validate organisation context\n try {\n validateContext();\n } catch (error) {\n logger.error('useSecureDataAccess', 'Organisation context validation failed', { table, operation, error });\n return false;\n }\n \n return isDataAccessAllowed(table, operation);\n }, [user?.id, validateContext, isDataAccessAllowed]);\n\n // Record data access attempt\n const recordDataAccess = useCallback((\n table: string,\n operation: string,\n allowed: boolean,\n query?: string,\n filters?: Record<string, any>\n ) => {\n if (!isAuditLogEnabled || !user?.id) return;\n \n const record: DataAccessRecord = {\n table,\n operation,\n userId: user.id,\n organisationId: getCurrentOrganisationId(),\n allowed,\n timestamp: new Date().toISOString(),\n query,\n filters\n };\n \n setDataAccessHistory(prev => {\n const newHistory = [record, ...prev];\n return newHistory.slice(0, 1000); // Keep last 1000 records\n });\n \n if (isStrictMode && !allowed) {\n logger.error('useSecureDataAccess', 'STRICT MODE VIOLATION: User attempted data access without permission', {\n table,\n operation,\n userId: user.id,\n organisationId: getCurrentOrganisationId(),\n timestamp: new Date().toISOString()\n });\n }\n }, [isAuditLogEnabled, isStrictMode, user?.id, getCurrentOrganisationId]);\n\n return {\n secureQuery,\n secureInsert,\n secureUpdate,\n secureDelete,\n secureRpc,\n getCurrentOrganisationId,\n validateContext,\n // NEW: Phase 1 - Enhanced Security Features\n isDataAccessAllowed,\n getDataAccessPermissions,\n isStrictMode,\n isAuditLogEnabled,\n getDataAccessHistory,\n clearDataAccessHistory,\n validateDataAccess\n };\n} "],"mappings":"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","names":[]}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./chunk-U6WNSFX5.js";
|
|
14
14
|
import {
|
|
15
15
|
useSecureDataAccess
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DAGICKHT.js";
|
|
17
17
|
import {
|
|
18
18
|
useUnifiedAuth
|
|
19
19
|
} from "./chunk-FXFJRTKI.js";
|
|
@@ -2105,4 +2105,4 @@ export {
|
|
|
2105
2105
|
getDirectSupabaseAuthFixes,
|
|
2106
2106
|
getQuickFixes
|
|
2107
2107
|
};
|
|
2108
|
-
//# sourceMappingURL=chunk-
|
|
2108
|
+
//# sourceMappingURL=chunk-GRIQLQ52.js.map
|
|
@@ -46,8 +46,9 @@ import {
|
|
|
46
46
|
useEventTheme,
|
|
47
47
|
useFileDisplay,
|
|
48
48
|
useIsPublicPage,
|
|
49
|
+
usePreventTabReload,
|
|
49
50
|
usePublicFileDisplay
|
|
50
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-TC7D3CR3.js";
|
|
51
52
|
import {
|
|
52
53
|
useToast
|
|
53
54
|
} from "./chunk-6C4YBBJM.js";
|
|
@@ -1839,7 +1840,7 @@ function Header({
|
|
|
1839
1840
|
return /* @__PURE__ */ jsx16("header", { className: cn(
|
|
1840
1841
|
"w-full border-b border-main-200 h-16 shadow-sm bg-main-100 ",
|
|
1841
1842
|
className
|
|
1842
|
-
), role: "banner", children: /* @__PURE__ */ jsxs10("nav", { className: "px-4 w-[min(var(--app-width),100%)] mx-auto
|
|
1843
|
+
), role: "banner", children: /* @__PURE__ */ jsxs10("nav", { className: "px-4 w-[min(var(--app-width),100%)] mx-auto flex items-center gap-4 h-full", children: [
|
|
1843
1844
|
logo ? logoHref ? /* @__PURE__ */ jsx16(Link, { to: logoHref, className: "cursor-pointer hover:opacity-80 transition-opacity", children: logo }) : logo : logoUrl ? logoHref ? /* @__PURE__ */ jsx16(Link, { to: logoHref, className: "cursor-pointer hover:opacity-80 transition-opacity", children: /* @__PURE__ */ jsx16(
|
|
1844
1845
|
"img",
|
|
1845
1846
|
{
|
|
@@ -1880,7 +1881,7 @@ function Header({
|
|
|
1880
1881
|
itemsPreFiltered: true
|
|
1881
1882
|
}
|
|
1882
1883
|
),
|
|
1883
|
-
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-4
|
|
1884
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-4 ml-auto", children: [
|
|
1884
1885
|
showOrgSelector ? /* @__PURE__ */ jsx16(
|
|
1885
1886
|
OrganisationSelector,
|
|
1886
1887
|
{
|
|
@@ -1897,9 +1898,7 @@ function Header({
|
|
|
1897
1898
|
className: "w-96",
|
|
1898
1899
|
"data-testid": "event-selector"
|
|
1899
1900
|
}
|
|
1900
|
-
) : null
|
|
1901
|
-
] }),
|
|
1902
|
-
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-4", children: [
|
|
1901
|
+
) : null,
|
|
1903
1902
|
actions,
|
|
1904
1903
|
showUserMenu && (userMenu ? userMenu : /* @__PURE__ */ jsx16(
|
|
1905
1904
|
UserMenu,
|
|
@@ -2509,7 +2508,7 @@ var SessionRestorationLoader = ({
|
|
|
2509
2508
|
};
|
|
2510
2509
|
|
|
2511
2510
|
// src/components/ProtectedRoute/ProtectedRoute.tsx
|
|
2512
|
-
import { useMemo as useMemo6 } from "react";
|
|
2511
|
+
import { useMemo as useMemo6, useEffect as useEffect5, useRef as useRef3, useState as useState8 } from "react";
|
|
2513
2512
|
import { Navigate, Outlet as Outlet2 } from "react-router-dom";
|
|
2514
2513
|
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2515
2514
|
function ProtectedRoute({
|
|
@@ -2525,6 +2524,67 @@ function ProtectedRoute({
|
|
|
2525
2524
|
const events = requireEvent ? eventsContext.events || [] : [];
|
|
2526
2525
|
const eventLoading = requireEvent ? eventsContext.isLoading || false : false;
|
|
2527
2526
|
const sessionRestoration = useSessionRestoration();
|
|
2527
|
+
usePreventTabReload({ enabled: true, gracePeriodMs: 2e3 });
|
|
2528
|
+
const wasAuthenticatedRef = useRef3(false);
|
|
2529
|
+
const [shouldRedirect, setShouldRedirect] = useState8(false);
|
|
2530
|
+
const tabJustBecameVisibleRef = useRef3(false);
|
|
2531
|
+
useEffect5(() => {
|
|
2532
|
+
if (isAuthenticated) {
|
|
2533
|
+
wasAuthenticatedRef.current = true;
|
|
2534
|
+
setShouldRedirect(false);
|
|
2535
|
+
tabJustBecameVisibleRef.current = false;
|
|
2536
|
+
}
|
|
2537
|
+
}, [isAuthenticated]);
|
|
2538
|
+
useEffect5(() => {
|
|
2539
|
+
if (typeof document === "undefined") return;
|
|
2540
|
+
let timeoutId = null;
|
|
2541
|
+
let wasHidden = document.hidden;
|
|
2542
|
+
const handleVisibilityChange = () => {
|
|
2543
|
+
const isNowVisible = !document.hidden;
|
|
2544
|
+
if (isNowVisible && wasHidden) {
|
|
2545
|
+
if (!isAuthenticated && wasAuthenticatedRef.current) {
|
|
2546
|
+
tabJustBecameVisibleRef.current = true;
|
|
2547
|
+
setShouldRedirect(false);
|
|
2548
|
+
if (timeoutId) {
|
|
2549
|
+
clearTimeout(timeoutId);
|
|
2550
|
+
}
|
|
2551
|
+
timeoutId = setTimeout(() => {
|
|
2552
|
+
tabJustBecameVisibleRef.current = false;
|
|
2553
|
+
setShouldRedirect((prev) => {
|
|
2554
|
+
return prev;
|
|
2555
|
+
});
|
|
2556
|
+
}, 2e3);
|
|
2557
|
+
}
|
|
2558
|
+
} else if (!isNowVisible) {
|
|
2559
|
+
tabJustBecameVisibleRef.current = false;
|
|
2560
|
+
if (timeoutId) {
|
|
2561
|
+
clearTimeout(timeoutId);
|
|
2562
|
+
timeoutId = null;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
wasHidden = !isNowVisible;
|
|
2566
|
+
};
|
|
2567
|
+
if (!document.hidden && !isAuthenticated && wasAuthenticatedRef.current) {
|
|
2568
|
+
tabJustBecameVisibleRef.current = true;
|
|
2569
|
+
setShouldRedirect(false);
|
|
2570
|
+
timeoutId = setTimeout(() => {
|
|
2571
|
+
tabJustBecameVisibleRef.current = false;
|
|
2572
|
+
}, 2e3);
|
|
2573
|
+
}
|
|
2574
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
2575
|
+
return () => {
|
|
2576
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
2577
|
+
if (timeoutId) {
|
|
2578
|
+
clearTimeout(timeoutId);
|
|
2579
|
+
}
|
|
2580
|
+
};
|
|
2581
|
+
}, [isAuthenticated]);
|
|
2582
|
+
useEffect5(() => {
|
|
2583
|
+
if (isAuthenticated) {
|
|
2584
|
+
setShouldRedirect(false);
|
|
2585
|
+
tabJustBecameVisibleRef.current = false;
|
|
2586
|
+
}
|
|
2587
|
+
}, [isAuthenticated]);
|
|
2528
2588
|
const isRestoringSession = useMemo6(() => {
|
|
2529
2589
|
return sessionRestoration.isRestoring && !sessionRestoration.restorationComplete && !sessionRestoration.restorationError && !sessionRestoration.hasTimedOut;
|
|
2530
2590
|
}, [
|
|
@@ -2548,6 +2608,20 @@ function ProtectedRoute({
|
|
|
2548
2608
|
timedOut: sessionRestoration.hasTimedOut,
|
|
2549
2609
|
error: sessionRestoration.restorationError?.message
|
|
2550
2610
|
});
|
|
2611
|
+
return /* @__PURE__ */ jsx21(Navigate, { to: loginPath, replace: true });
|
|
2612
|
+
}
|
|
2613
|
+
if (!wasAuthenticatedRef.current) {
|
|
2614
|
+
return /* @__PURE__ */ jsx21(Navigate, { to: loginPath, replace: true });
|
|
2615
|
+
}
|
|
2616
|
+
const isTabVisible = typeof document !== "undefined" && !document.hidden;
|
|
2617
|
+
if (tabJustBecameVisibleRef.current || isTabVisible && wasAuthenticatedRef.current && isLoading) {
|
|
2618
|
+
return loadingFallback || /* @__PURE__ */ jsx21("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", height: "100vh" }, children: /* @__PURE__ */ jsx21(LoadingSpinner, {}) });
|
|
2619
|
+
}
|
|
2620
|
+
if (shouldRedirect) {
|
|
2621
|
+
return /* @__PURE__ */ jsx21(Navigate, { to: loginPath, replace: true });
|
|
2622
|
+
}
|
|
2623
|
+
if (isLoading) {
|
|
2624
|
+
return loadingFallback || /* @__PURE__ */ jsx21("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", height: "100vh" }, children: /* @__PURE__ */ jsx21(LoadingSpinner, {}) });
|
|
2551
2625
|
}
|
|
2552
2626
|
return /* @__PURE__ */ jsx21(Navigate, { to: loginPath, replace: true });
|
|
2553
2627
|
}
|
|
@@ -2568,11 +2642,11 @@ function ProtectedRoute({
|
|
|
2568
2642
|
}
|
|
2569
2643
|
|
|
2570
2644
|
// src/hooks/useFileReference.ts
|
|
2571
|
-
import { useState as
|
|
2645
|
+
import { useState as useState9, useCallback as useCallback5, useEffect as useEffect6, useRef as useRef4, useMemo as useMemo7 } from "react";
|
|
2572
2646
|
var log = createLogger("useFileReference");
|
|
2573
2647
|
function useFileReference(supabase) {
|
|
2574
|
-
const [isLoading, setIsLoading] =
|
|
2575
|
-
const [error, setError] =
|
|
2648
|
+
const [isLoading, setIsLoading] = useState9(false);
|
|
2649
|
+
const [error, setError] = useState9(null);
|
|
2576
2650
|
const service = useMemo7(() => createFileReferenceService(supabase), [supabase]);
|
|
2577
2651
|
const uploadFile = useCallback5(async (options, file) => {
|
|
2578
2652
|
setIsLoading(true);
|
|
@@ -2736,11 +2810,11 @@ function useFileReferenceForRecord(supabase, table_name, record_id, organisation
|
|
|
2736
2810
|
getFileCount,
|
|
2737
2811
|
clearError
|
|
2738
2812
|
} = useFileReference(supabase);
|
|
2739
|
-
const [fileUrl, setFileUrl] =
|
|
2740
|
-
const [fileReference, setFileReference] =
|
|
2741
|
-
const [fileReferences, setFileReferences] =
|
|
2742
|
-
const [fileCount, setFileCount] =
|
|
2743
|
-
const urlRefreshIntervalRef =
|
|
2813
|
+
const [fileUrl, setFileUrl] = useState9(null);
|
|
2814
|
+
const [fileReference, setFileReference] = useState9(null);
|
|
2815
|
+
const [fileReferences, setFileReferences] = useState9([]);
|
|
2816
|
+
const [fileCount, setFileCount] = useState9(0);
|
|
2817
|
+
const urlRefreshIntervalRef = useRef4(null);
|
|
2744
2818
|
const loadFileReference = useCallback5(async () => {
|
|
2745
2819
|
const reference = await getFileReference(table_name, record_id, organisation_id);
|
|
2746
2820
|
setFileReference(reference);
|
|
@@ -2770,7 +2844,7 @@ function useFileReferenceForRecord(supabase, table_name, record_id, organisation
|
|
|
2770
2844
|
}
|
|
2771
2845
|
return success;
|
|
2772
2846
|
}, [deleteFileReference, table_name, record_id, organisation_id, loadFileCount]);
|
|
2773
|
-
|
|
2847
|
+
useEffect6(() => {
|
|
2774
2848
|
if (!fileReference || fileReference.is_public) {
|
|
2775
2849
|
if (urlRefreshIntervalRef.current) {
|
|
2776
2850
|
clearInterval(urlRefreshIntervalRef.current);
|
|
@@ -2811,8 +2885,8 @@ function useFileReferenceById(supabase, fileReferenceId, organisationId) {
|
|
|
2811
2885
|
getFileReferenceById,
|
|
2812
2886
|
clearError
|
|
2813
2887
|
} = useFileReference(supabase);
|
|
2814
|
-
const [fileReference, setFileReference] =
|
|
2815
|
-
const [fileUrl, setFileUrl] =
|
|
2888
|
+
const [fileReference, setFileReference] = useState9(null);
|
|
2889
|
+
const [fileUrl, setFileUrl] = useState9(null);
|
|
2816
2890
|
const loadFileReference = useCallback5(async () => {
|
|
2817
2891
|
if (!fileReferenceId || !organisationId) {
|
|
2818
2892
|
setFileReference(null);
|
|
@@ -2823,10 +2897,10 @@ function useFileReferenceById(supabase, fileReferenceId, organisationId) {
|
|
|
2823
2897
|
setFileReference(reference);
|
|
2824
2898
|
return reference;
|
|
2825
2899
|
}, [getFileReferenceById, fileReferenceId, organisationId]);
|
|
2826
|
-
|
|
2900
|
+
useEffect6(() => {
|
|
2827
2901
|
loadFileReference();
|
|
2828
2902
|
}, [loadFileReference]);
|
|
2829
|
-
|
|
2903
|
+
useEffect6(() => {
|
|
2830
2904
|
if (!fileReference || !fileReferenceId || !organisationId) {
|
|
2831
2905
|
setFileUrl(null);
|
|
2832
2906
|
return;
|
|
@@ -2858,8 +2932,8 @@ function useFilesByCategory(supabase, table_name, record_id, category, organisat
|
|
|
2858
2932
|
getFilesByCategory,
|
|
2859
2933
|
clearError
|
|
2860
2934
|
} = useFileReference(supabase);
|
|
2861
|
-
const [fileReferences, setFileReferences] =
|
|
2862
|
-
const [fileUrls, setFileUrls] =
|
|
2935
|
+
const [fileReferences, setFileReferences] = useState9([]);
|
|
2936
|
+
const [fileUrls, setFileUrls] = useState9(/* @__PURE__ */ new Map());
|
|
2863
2937
|
const loadFiles = useCallback5(async () => {
|
|
2864
2938
|
if (!category || !organisation_id) {
|
|
2865
2939
|
setFileReferences([]);
|
|
@@ -2892,7 +2966,7 @@ function useFilesByCategory(supabase, table_name, record_id, category, organisat
|
|
|
2892
2966
|
setFileUrls(urlMap);
|
|
2893
2967
|
return files;
|
|
2894
2968
|
}, [table_name, record_id, category, organisation_id, supabase, getFilesByCategory]);
|
|
2895
|
-
|
|
2969
|
+
useEffect6(() => {
|
|
2896
2970
|
loadFiles();
|
|
2897
2971
|
}, [loadFiles]);
|
|
2898
2972
|
return {
|
|
@@ -2906,7 +2980,7 @@ function useFilesByCategory(supabase, table_name, record_id, category, organisat
|
|
|
2906
2980
|
}
|
|
2907
2981
|
|
|
2908
2982
|
// src/components/FileUpload/FileUpload.tsx
|
|
2909
|
-
import { useState as
|
|
2983
|
+
import { useState as useState10, useCallback as useCallback6, useRef as useRef5, useEffect as useEffect7, useMemo as useMemo8 } from "react";
|
|
2910
2984
|
import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2911
2985
|
function FileUpload({
|
|
2912
2986
|
supabase,
|
|
@@ -2915,7 +2989,9 @@ function FileUpload({
|
|
|
2915
2989
|
organisation_id,
|
|
2916
2990
|
app_id,
|
|
2917
2991
|
category,
|
|
2992
|
+
folder,
|
|
2918
2993
|
pageContext,
|
|
2994
|
+
event_id,
|
|
2919
2995
|
accept = "*/*",
|
|
2920
2996
|
maxSize = 10 * 1024 * 1024,
|
|
2921
2997
|
// 10MB default
|
|
@@ -2930,14 +3006,14 @@ function FileUpload({
|
|
|
2930
3006
|
onProgress,
|
|
2931
3007
|
children
|
|
2932
3008
|
}) {
|
|
2933
|
-
const [isDragging, setIsDragging] =
|
|
2934
|
-
const [uploadStates, setUploadStates] =
|
|
2935
|
-
const [resolvedAppId, setResolvedAppId] =
|
|
2936
|
-
const [isResolvingAppId, setIsResolvingAppId] =
|
|
2937
|
-
const [appIdError, setAppIdError] =
|
|
2938
|
-
const fileInputRef =
|
|
3009
|
+
const [isDragging, setIsDragging] = useState10(false);
|
|
3010
|
+
const [uploadStates, setUploadStates] = useState10(/* @__PURE__ */ new Map());
|
|
3011
|
+
const [resolvedAppId, setResolvedAppId] = useState10(app_id || null);
|
|
3012
|
+
const [isResolvingAppId, setIsResolvingAppId] = useState10(!app_id);
|
|
3013
|
+
const [appIdError, setAppIdError] = useState10(null);
|
|
3014
|
+
const fileInputRef = useRef5(null);
|
|
2939
3015
|
const { uploadFile, isLoading, error } = useFileReference(supabase);
|
|
2940
|
-
|
|
3016
|
+
useEffect7(() => {
|
|
2941
3017
|
if (app_id) {
|
|
2942
3018
|
setResolvedAppId(app_id);
|
|
2943
3019
|
setIsResolvingAppId(false);
|
|
@@ -3103,7 +3179,9 @@ function FileUpload({
|
|
|
3103
3179
|
organisation_id,
|
|
3104
3180
|
app_id: resolvedAppId ? assertAppId(resolvedAppId) : assertAppId(""),
|
|
3105
3181
|
category,
|
|
3182
|
+
folder,
|
|
3106
3183
|
pageContext,
|
|
3184
|
+
event_id,
|
|
3107
3185
|
is_public: isPublic
|
|
3108
3186
|
}, file);
|
|
3109
3187
|
clearInterval(progressInterval);
|
|
@@ -3190,7 +3268,7 @@ function FileUpload({
|
|
|
3190
3268
|
onUploadError?.(errorMessage, file);
|
|
3191
3269
|
}
|
|
3192
3270
|
}
|
|
3193
|
-
}, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError]);
|
|
3271
|
+
}, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, folder, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError]);
|
|
3194
3272
|
const handleDragOver = useCallback6((e) => {
|
|
3195
3273
|
e.preventDefault();
|
|
3196
3274
|
e.stopPropagation();
|
|
@@ -3371,17 +3449,17 @@ function FileUpload({
|
|
|
3371
3449
|
}
|
|
3372
3450
|
|
|
3373
3451
|
// src/components/FileDisplay/FileDisplay.tsx
|
|
3374
|
-
import { useState as
|
|
3452
|
+
import { useState as useState12, useEffect as useEffect9, useRef as useRef7, useContext as useContext2, useMemo as useMemo9 } from "react";
|
|
3375
3453
|
|
|
3376
3454
|
// src/hooks/useFileUrl.ts
|
|
3377
|
-
import { useState as
|
|
3455
|
+
import { useState as useState11, useEffect as useEffect8, useCallback as useCallback7, useRef as useRef6 } from "react";
|
|
3378
3456
|
var log2 = createLogger("useFileUrl");
|
|
3379
3457
|
function useFileUrl(fileReference, options) {
|
|
3380
3458
|
const { organisation_id, supabase, autoLoad = true } = options;
|
|
3381
|
-
const [url, setUrl] =
|
|
3382
|
-
const [isLoading, setIsLoading] =
|
|
3383
|
-
const [error, setError] =
|
|
3384
|
-
const fileReferenceIdRef =
|
|
3459
|
+
const [url, setUrl] = useState11(null);
|
|
3460
|
+
const [isLoading, setIsLoading] = useState11(false);
|
|
3461
|
+
const [error, setError] = useState11(null);
|
|
3462
|
+
const fileReferenceIdRef = useRef6(null);
|
|
3385
3463
|
const loadUrl = useCallback7(async () => {
|
|
3386
3464
|
if (!fileReference) {
|
|
3387
3465
|
setUrl(null);
|
|
@@ -3424,7 +3502,7 @@ function useFileUrl(fileReference, options) {
|
|
|
3424
3502
|
setIsLoading(false);
|
|
3425
3503
|
fileReferenceIdRef.current = null;
|
|
3426
3504
|
}, []);
|
|
3427
|
-
|
|
3505
|
+
useEffect8(() => {
|
|
3428
3506
|
if (autoLoad) {
|
|
3429
3507
|
if (fileReferenceIdRef.current !== fileReference?.id) {
|
|
3430
3508
|
setUrl(null);
|
|
@@ -3484,10 +3562,10 @@ function FileDisplayContent({
|
|
|
3484
3562
|
fallbackText,
|
|
3485
3563
|
fallbackSize = "md"
|
|
3486
3564
|
}) {
|
|
3487
|
-
const [imageError, setImageError] =
|
|
3488
|
-
const [internalFileUrls, setInternalFileUrls] =
|
|
3489
|
-
const [deleteDialogOpen, setDeleteDialogOpen] =
|
|
3490
|
-
const fileReferencesRef =
|
|
3565
|
+
const [imageError, setImageError] = useState12(false);
|
|
3566
|
+
const [internalFileUrls, setInternalFileUrls] = useState12(new Map(fileUrls));
|
|
3567
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState12(false);
|
|
3568
|
+
const fileReferencesRef = useRef7([]);
|
|
3491
3569
|
const computedFallbackText = useMemo9(() => {
|
|
3492
3570
|
if (fallbackText) return fallbackText;
|
|
3493
3571
|
const fileName = fileReference?.file_metadata?.fileName;
|
|
@@ -3498,7 +3576,7 @@ function FileDisplayContent({
|
|
|
3498
3576
|
const baseClasses = "flex items-center justify-center bg-sec-100 text-sec-600 font-semibold rounded";
|
|
3499
3577
|
return `${baseClasses} ${sizeClass} ${className}`.trim();
|
|
3500
3578
|
}, [fallbackSize, className]);
|
|
3501
|
-
|
|
3579
|
+
useEffect9(() => {
|
|
3502
3580
|
const currentIds = fileReferences.map((f) => f.id).join(",");
|
|
3503
3581
|
const prevIds = fileReferencesRef.current.map((f) => f.id).join(",");
|
|
3504
3582
|
if (currentIds !== prevIds) {
|
|
@@ -3888,7 +3966,7 @@ function FileDisplayAuthenticated({
|
|
|
3888
3966
|
category,
|
|
3889
3967
|
{ supabase }
|
|
3890
3968
|
);
|
|
3891
|
-
const [displayOnlyFileReference, setDisplayOnlyFileReference] =
|
|
3969
|
+
const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState12(null);
|
|
3892
3970
|
const displayOnlyFileUrlFromMap = displayOnlyFileReference ? fileUrls.get(displayOnlyFileReference.id) : null;
|
|
3893
3971
|
const displayOnlyFileUrlHook = useFileUrl(
|
|
3894
3972
|
displayOnlyFileReference && !displayOnlyFileUrlFromMap ? displayOnlyFileReference : null,
|
|
@@ -3899,7 +3977,7 @@ function FileDisplayAuthenticated({
|
|
|
3899
3977
|
}
|
|
3900
3978
|
);
|
|
3901
3979
|
const displayOnlyFileUrl = displayOnlyFileUrlFromMap || displayOnlyFileUrlHook.url;
|
|
3902
|
-
|
|
3980
|
+
useEffect9(() => {
|
|
3903
3981
|
if (displayOnly && !category && fileReferences.length > 0) {
|
|
3904
3982
|
const imageFiles = fileReferences.filter(
|
|
3905
3983
|
(f) => f.file_metadata.fileType?.startsWith("image/")
|
|
@@ -4279,4 +4357,4 @@ export {
|
|
|
4279
4357
|
PublicPageFooter,
|
|
4280
4358
|
PublicPageLayout
|
|
4281
4359
|
};
|
|
4282
|
-
//# sourceMappingURL=chunk-
|
|
4360
|
+
//# sourceMappingURL=chunk-HDCUMOOI.js.map
|