@jmruthers/pace-core 0.5.183 → 0.5.185
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/CHANGELOG.md +38 -0
- package/README.md +60 -1
- package/core-usage-manifest.json +312 -0
- package/dist/{DataTable-QAB34V6K.js → DataTable-IX2NBUTP.js} +6 -6
- package/dist/{DataTable-Bz8ffqyA.d.ts → DataTable-Z9NLVJh0.d.ts} +1 -1
- package/dist/{index-Bl--n7-T.d.ts → PublicPageProvider-BABf6JCh.d.ts} +21 -10
- package/dist/{UnifiedAuthProvider-7F6T4B6K.js → UnifiedAuthProvider-A4BCQRJY.js} +4 -2
- package/dist/{UnifiedAuthProvider-F86d7dSi.d.ts → UnifiedAuthProvider-BG0AL5eE.d.ts} +2 -1
- package/dist/{api-ROMBCNKU.js → api-BMFCXVQX.js} +2 -2
- package/dist/{chunk-RA3JUFMW.js → chunk-445GEP27.js} +154 -4
- package/dist/{chunk-RA3JUFMW.js.map → chunk-445GEP27.js.map} +1 -1
- package/dist/{chunk-CSOFYHAG.js → chunk-AISXLWGZ.js} +374 -60
- package/dist/chunk-AISXLWGZ.js.map +1 -0
- package/dist/{chunk-FUEYYMX5.js → chunk-FXFJRTKI.js} +24 -3
- package/dist/chunk-FXFJRTKI.js.map +1 -0
- package/dist/{chunk-QETLRQI6.js → chunk-HC67NW5K.js} +380 -360
- package/dist/chunk-HC67NW5K.js.map +1 -0
- package/dist/chunk-HESYZWZW.js +388 -0
- package/dist/chunk-HESYZWZW.js.map +1 -0
- package/dist/{chunk-QUVSNGIP.js → chunk-HGPQUCBC.js} +34 -9
- package/dist/{chunk-QUVSNGIP.js.map → chunk-HGPQUCBC.js.map} +1 -1
- package/dist/{chunk-UHNYIBXL.js → chunk-IXSNYUCT.js} +1 -1
- package/dist/chunk-IXSNYUCT.js.map +1 -0
- package/dist/{chunk-MI7HBHN3.js → chunk-MX3EIJGQ.js} +4 -3
- package/dist/{chunk-MI7HBHN3.js.map → chunk-MX3EIJGQ.js.map} +1 -1
- package/dist/{chunk-PWAHJW4G.js → chunk-OKI34GZD.js} +86 -33
- package/dist/chunk-OKI34GZD.js.map +1 -0
- package/dist/{chunk-W22JP75J.js → chunk-STTZQK2I.js} +3 -3
- package/dist/chunk-THRPYOFK.js +215 -0
- package/dist/chunk-THRPYOFK.js.map +1 -0
- package/dist/{chunk-M7W4CP3M.js → chunk-U6WNSFX5.js} +2 -1
- package/dist/chunk-U6WNSFX5.js.map +1 -0
- package/dist/{chunk-QCDXODCA.js → chunk-XAUHJD3L.js} +2 -2
- package/dist/components.d.ts +182 -6
- package/dist/components.js +157 -11
- package/dist/components.js.map +1 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +406 -0
- package/dist/{file-reference-D06mEEWW.d.ts → file-reference-BjR39ktt.d.ts} +7 -1
- package/dist/hooks.d.ts +7 -14
- package/dist/hooks.js +10 -22
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +79 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +3 -1
- package/dist/rbac/index.d.ts +205 -14
- package/dist/rbac/index.js +28 -6
- package/dist/timezone-_pgH8qrY.d.ts +530 -0
- package/dist/{types-_x1f4QBF.d.ts → types-DUyCRSTj.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-JJczomYq.d.ts → usePublicRouteParams-CvnC3d-e.d.ts} +113 -2
- package/dist/utils.d.ts +109 -151
- package/dist/utils.js +128 -138
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +60 -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 +178 -0
- 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 +54 -0
- 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 +18 -2
- 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 +30 -0
- 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 +85 -0
- package/docs/api/interfaces/DatabaseIssue.md +41 -0
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +6 -6
- 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 +24 -8
- package/docs/api/interfaces/FileUploadProps.md +24 -13
- 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 +9 -9
- 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 +62 -0
- 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 +36 -23
- 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/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 +52 -0
- 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 +4 -4
- 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 +7 -7
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +5 -5
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +55 -0
- 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 +41 -0
- 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 +62 -0
- package/docs/api/interfaces/UseFormDialogReturn.md +117 -0
- 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 +738 -42
- package/docs/api-reference/hooks.md +111 -0
- package/docs/api-reference/rpc-functions.md +1 -1
- package/docs/api-reference/utilities.md +184 -0
- package/docs/getting-started/installation-guide.md +75 -16
- package/docs/getting-started/quick-start.md +61 -11
- package/docs/implementation-guides/authentication.md +88 -12
- package/docs/implementation-guides/file-reference-system.md +2 -1
- package/docs/implementation-guides/file-upload-storage.md +21 -0
- package/docs/rbac/README.md +1 -0
- package/docs/rbac/compliance/compliance-guide.md +544 -0
- package/docs/rbac/getting-started.md +158 -33
- package/docs/standards/pace-core-compliance.md +432 -0
- package/eslint-config-pace-core.cjs +93 -0
- package/package.json +15 -3
- package/scripts/analyze-bundle.js +232 -0
- package/scripts/build-css.js +56 -0
- package/scripts/build-docs-incremental.js +1015 -0
- package/scripts/check-pace-core-compliance.cjs +2353 -0
- package/scripts/generate-docs.js +157 -0
- package/scripts/setup-build-cache.js +73 -0
- package/scripts/utils/command-runner.js +131 -0
- package/scripts/utils/env.js +33 -0
- package/scripts/utils/index.js +10 -0
- package/scripts/utils/logger.js +88 -0
- package/scripts/utils/path-helpers.js +37 -0
- package/scripts/validate-formats.js +133 -0
- package/scripts/validate-master.js +155 -0
- package/scripts/validate-pre-publish.js +140 -0
- package/scripts/validate-theme.js +142 -0
- package/src/components/Calendar/Calendar.tsx +8 -1
- package/src/components/Card/Card.tsx +47 -8
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +314 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +126 -0
- package/src/components/DatePickerWithTimezone/README.md +135 -0
- package/src/components/DatePickerWithTimezone/index.ts +10 -0
- package/src/components/DateTimeField/DateTimeField.test.tsx +358 -0
- package/src/components/DateTimeField/DateTimeField.tsx +232 -0
- package/src/components/DateTimeField/README.md +148 -0
- package/src/components/DateTimeField/index.ts +10 -0
- package/src/components/FileUpload/FileUpload.tsx +3 -0
- package/src/components/Header/Header.test.tsx +47 -18
- package/src/components/Header/Header.tsx +24 -6
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +29 -20
- package/src/components/PaceAppLayout/README.md +9 -0
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +12 -4
- package/src/components/index.ts +8 -0
- package/src/eslint-rules/pace-core-compliance.cjs +406 -0
- package/src/eslint-rules/pace-core-compliance.js +640 -0
- package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useFileReference.test.ts +1 -0
- package/src/hooks/useFormDialog.ts +147 -0
- package/src/index.ts +27 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
- package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
- package/src/rbac/compliance/database-validator.ts +165 -0
- package/src/rbac/compliance/index.ts +38 -0
- package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
- package/src/rbac/compliance/runtime-compliance.ts +77 -0
- package/src/rbac/compliance/setup-validator.ts +131 -0
- package/src/rbac/components/PagePermissionGuard.tsx +8 -64
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
- package/src/rbac/docs/event-based-apps.md +285 -0
- package/src/rbac/errors.ts +11 -0
- package/src/rbac/hooks/useRoleManagement.ts +292 -12
- package/src/rbac/index.ts +30 -0
- package/src/services/OrganisationService.ts +4 -0
- package/src/types/file-reference.ts +6 -0
- package/src/utils/__tests__/timezone.test.ts +345 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +2 -0
- package/src/utils/file-reference/index.ts +1 -0
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
- package/src/utils/formatting/formatting.ts +179 -0
- package/src/utils/index.ts +27 -1
- package/src/utils/location/index.ts +16 -0
- package/src/utils/location/location.test.ts +286 -0
- package/src/utils/location/location.ts +175 -0
- package/src/utils/timezone/index.ts +17 -0
- package/src/utils/timezone/timezone.test.ts +349 -0
- package/src/utils/timezone/timezone.ts +281 -0
- package/dist/chunk-CSOFYHAG.js.map +0 -1
- package/dist/chunk-FUEYYMX5.js.map +0 -1
- package/dist/chunk-HKIT6O7W.js +0 -198
- package/dist/chunk-HKIT6O7W.js.map +0 -1
- package/dist/chunk-KUEN3HFB.js +0 -94
- package/dist/chunk-KUEN3HFB.js.map +0 -1
- package/dist/chunk-M7W4CP3M.js.map +0 -1
- package/dist/chunk-PWAHJW4G.js.map +0 -1
- package/dist/chunk-QETLRQI6.js.map +0 -1
- package/dist/chunk-UHNYIBXL.js.map +0 -1
- package/dist/formatting-5wETwiGF.d.ts +0 -162
- /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
- /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
- /package/dist/{chunk-W22JP75J.js.map → chunk-STTZQK2I.js.map} +0 -0
- /package/dist/{chunk-QCDXODCA.js.map → chunk-XAUHJD3L.js.map} +0 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useFormDialog Hook Unit Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Hooks/__tests__/useFormDialog
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive tests for the useFormDialog hook covering all critical functionality.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { renderHook, act } from '@testing-library/react';
|
|
11
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
12
|
+
import { useFormDialog, type UseFormDialogOptions } from '../useFormDialog';
|
|
13
|
+
|
|
14
|
+
interface TestFormData {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
email: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('useFormDialog', () => {
|
|
21
|
+
describe('Initial state', () => {
|
|
22
|
+
it('should initialize with closed state and null formData', () => {
|
|
23
|
+
const { result } = renderHook(() => useFormDialog());
|
|
24
|
+
|
|
25
|
+
expect(result.current.isOpen).toBe(false);
|
|
26
|
+
expect(result.current.formData).toBeNull();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should initialize with closed state for typed hook', () => {
|
|
30
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
31
|
+
|
|
32
|
+
expect(result.current.isOpen).toBe(false);
|
|
33
|
+
expect(result.current.formData).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('Opening dialog', () => {
|
|
38
|
+
it('should open dialog without data', () => {
|
|
39
|
+
const { result } = renderHook(() => useFormDialog());
|
|
40
|
+
|
|
41
|
+
act(() => {
|
|
42
|
+
result.current.openDialog();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(result.current.isOpen).toBe(true);
|
|
46
|
+
expect(result.current.formData).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should open dialog with data', () => {
|
|
50
|
+
const testData: TestFormData = {
|
|
51
|
+
id: '1',
|
|
52
|
+
name: 'John Doe',
|
|
53
|
+
email: 'john@example.com'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
57
|
+
|
|
58
|
+
act(() => {
|
|
59
|
+
result.current.openDialog(testData);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(result.current.isOpen).toBe(true);
|
|
63
|
+
expect(result.current.formData).toEqual(testData);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should open dialog with null data explicitly', () => {
|
|
67
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
68
|
+
|
|
69
|
+
act(() => {
|
|
70
|
+
result.current.openDialog(null);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result.current.isOpen).toBe(true);
|
|
74
|
+
expect(result.current.formData).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Closing dialog', () => {
|
|
79
|
+
it('should close dialog and reset formData by default', () => {
|
|
80
|
+
const testData: TestFormData = {
|
|
81
|
+
id: '1',
|
|
82
|
+
name: 'John Doe',
|
|
83
|
+
email: 'john@example.com'
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
87
|
+
|
|
88
|
+
act(() => {
|
|
89
|
+
result.current.openDialog(testData);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(result.current.isOpen).toBe(true);
|
|
93
|
+
expect(result.current.formData).toEqual(testData);
|
|
94
|
+
|
|
95
|
+
act(() => {
|
|
96
|
+
result.current.closeDialog();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(result.current.isOpen).toBe(false);
|
|
100
|
+
expect(result.current.formData).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should close dialog without resetting formData when resetOnClose is false', () => {
|
|
104
|
+
const testData: TestFormData = {
|
|
105
|
+
id: '1',
|
|
106
|
+
name: 'John Doe',
|
|
107
|
+
email: 'john@example.com'
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const { result } = renderHook(() =>
|
|
111
|
+
useFormDialog<TestFormData>({ resetOnClose: false })
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
act(() => {
|
|
115
|
+
result.current.openDialog(testData);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
act(() => {
|
|
119
|
+
result.current.closeDialog();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result.current.isOpen).toBe(false);
|
|
123
|
+
expect(result.current.formData).toEqual(testData);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('handleOpenChange', () => {
|
|
128
|
+
it('should update state when opening via handleOpenChange', () => {
|
|
129
|
+
const { result } = renderHook(() => useFormDialog());
|
|
130
|
+
|
|
131
|
+
act(() => {
|
|
132
|
+
result.current.handleOpenChange(true);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(result.current.isOpen).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should update state when closing via handleOpenChange', () => {
|
|
139
|
+
const { result } = renderHook(() => useFormDialog());
|
|
140
|
+
|
|
141
|
+
act(() => {
|
|
142
|
+
result.current.openDialog();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
act(() => {
|
|
146
|
+
result.current.handleOpenChange(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(result.current.isOpen).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should reset formData when closing via handleOpenChange with resetOnClose true', () => {
|
|
153
|
+
const testData: TestFormData = {
|
|
154
|
+
id: '1',
|
|
155
|
+
name: 'John Doe',
|
|
156
|
+
email: 'john@example.com'
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
160
|
+
|
|
161
|
+
act(() => {
|
|
162
|
+
result.current.openDialog(testData);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
act(() => {
|
|
166
|
+
result.current.handleOpenChange(false);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(result.current.isOpen).toBe(false);
|
|
170
|
+
expect(result.current.formData).toBeNull();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should not reset formData when closing via handleOpenChange with resetOnClose false', () => {
|
|
174
|
+
const testData: TestFormData = {
|
|
175
|
+
id: '1',
|
|
176
|
+
name: 'John Doe',
|
|
177
|
+
email: 'john@example.com'
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const { result } = renderHook(() =>
|
|
181
|
+
useFormDialog<TestFormData>({ resetOnClose: false })
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
act(() => {
|
|
185
|
+
result.current.openDialog(testData);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
act(() => {
|
|
189
|
+
result.current.handleOpenChange(false);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(result.current.isOpen).toBe(false);
|
|
193
|
+
expect(result.current.formData).toEqual(testData);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('onOpenChange callback', () => {
|
|
198
|
+
it('should call onOpenChange when opening dialog', () => {
|
|
199
|
+
const onOpenChange = vi.fn();
|
|
200
|
+
const { result } = renderHook(() =>
|
|
201
|
+
useFormDialog({ onOpenChange })
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
act(() => {
|
|
205
|
+
result.current.openDialog();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(onOpenChange).toHaveBeenCalledWith(true);
|
|
209
|
+
expect(onOpenChange).toHaveBeenCalledTimes(1);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should call onOpenChange when closing dialog', () => {
|
|
213
|
+
const onOpenChange = vi.fn();
|
|
214
|
+
const { result } = renderHook(() =>
|
|
215
|
+
useFormDialog({ onOpenChange })
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
act(() => {
|
|
219
|
+
result.current.openDialog();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
act(() => {
|
|
223
|
+
result.current.closeDialog();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
227
|
+
expect(onOpenChange).toHaveBeenCalledTimes(2); // Once for open, once for close
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should call onOpenChange when using handleOpenChange', () => {
|
|
231
|
+
const onOpenChange = vi.fn();
|
|
232
|
+
const { result } = renderHook(() =>
|
|
233
|
+
useFormDialog({ onOpenChange })
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
act(() => {
|
|
237
|
+
result.current.handleOpenChange(true);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(onOpenChange).toHaveBeenCalledWith(true);
|
|
241
|
+
|
|
242
|
+
act(() => {
|
|
243
|
+
result.current.handleOpenChange(false);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
247
|
+
expect(onOpenChange).toHaveBeenCalledTimes(2);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should not call onOpenChange if not provided', () => {
|
|
251
|
+
const { result } = renderHook(() => useFormDialog());
|
|
252
|
+
|
|
253
|
+
act(() => {
|
|
254
|
+
result.current.openDialog();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
act(() => {
|
|
258
|
+
result.current.closeDialog();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Should not throw
|
|
262
|
+
expect(result.current.isOpen).toBe(false);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
describe('Controlled vs uncontrolled usage', () => {
|
|
267
|
+
it('should work in uncontrolled mode (using openDialog/closeDialog)', () => {
|
|
268
|
+
const { result } = renderHook(() => useFormDialog());
|
|
269
|
+
|
|
270
|
+
act(() => {
|
|
271
|
+
result.current.openDialog();
|
|
272
|
+
});
|
|
273
|
+
expect(result.current.isOpen).toBe(true);
|
|
274
|
+
|
|
275
|
+
act(() => {
|
|
276
|
+
result.current.closeDialog();
|
|
277
|
+
});
|
|
278
|
+
expect(result.current.isOpen).toBe(false);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should work in controlled mode (using handleOpenChange)', () => {
|
|
282
|
+
const { result } = renderHook(() => useFormDialog());
|
|
283
|
+
|
|
284
|
+
act(() => {
|
|
285
|
+
result.current.handleOpenChange(true);
|
|
286
|
+
});
|
|
287
|
+
expect(result.current.isOpen).toBe(true);
|
|
288
|
+
|
|
289
|
+
act(() => {
|
|
290
|
+
result.current.handleOpenChange(false);
|
|
291
|
+
});
|
|
292
|
+
expect(result.current.isOpen).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should work with mixed usage', () => {
|
|
296
|
+
const { result } = renderHook(() => useFormDialog());
|
|
297
|
+
|
|
298
|
+
act(() => {
|
|
299
|
+
result.current.openDialog();
|
|
300
|
+
});
|
|
301
|
+
expect(result.current.isOpen).toBe(true);
|
|
302
|
+
|
|
303
|
+
act(() => {
|
|
304
|
+
result.current.handleOpenChange(false);
|
|
305
|
+
});
|
|
306
|
+
expect(result.current.isOpen).toBe(false);
|
|
307
|
+
|
|
308
|
+
act(() => {
|
|
309
|
+
result.current.handleOpenChange(true);
|
|
310
|
+
});
|
|
311
|
+
expect(result.current.isOpen).toBe(true);
|
|
312
|
+
|
|
313
|
+
act(() => {
|
|
314
|
+
result.current.closeDialog();
|
|
315
|
+
});
|
|
316
|
+
expect(result.current.isOpen).toBe(false);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('Edge cases', () => {
|
|
321
|
+
it('should handle rapid open/close operations', () => {
|
|
322
|
+
const { result } = renderHook(() => useFormDialog());
|
|
323
|
+
|
|
324
|
+
act(() => {
|
|
325
|
+
result.current.openDialog();
|
|
326
|
+
result.current.closeDialog();
|
|
327
|
+
result.current.openDialog();
|
|
328
|
+
result.current.closeDialog();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
expect(result.current.isOpen).toBe(false);
|
|
332
|
+
expect(result.current.formData).toBeNull();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should handle opening with undefined data', () => {
|
|
336
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
337
|
+
|
|
338
|
+
act(() => {
|
|
339
|
+
result.current.openDialog(undefined as unknown as TestFormData);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(result.current.isOpen).toBe(true);
|
|
343
|
+
// When undefined is passed, it becomes null in the state
|
|
344
|
+
expect(result.current.formData).toBeNull();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should handle replacing formData when dialog is already open', () => {
|
|
348
|
+
const initialData: TestFormData = {
|
|
349
|
+
id: '1',
|
|
350
|
+
name: 'John Doe',
|
|
351
|
+
email: 'john@example.com'
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const newData: TestFormData = {
|
|
355
|
+
id: '2',
|
|
356
|
+
name: 'Jane Doe',
|
|
357
|
+
email: 'jane@example.com'
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
361
|
+
|
|
362
|
+
act(() => {
|
|
363
|
+
result.current.openDialog(initialData);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
expect(result.current.formData).toEqual(initialData);
|
|
367
|
+
|
|
368
|
+
act(() => {
|
|
369
|
+
result.current.openDialog(newData);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
expect(result.current.isOpen).toBe(true);
|
|
373
|
+
expect(result.current.formData).toEqual(newData);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should handle closing when already closed', () => {
|
|
377
|
+
const { result } = renderHook(() => useFormDialog());
|
|
378
|
+
|
|
379
|
+
expect(result.current.isOpen).toBe(false);
|
|
380
|
+
|
|
381
|
+
act(() => {
|
|
382
|
+
result.current.closeDialog();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
expect(result.current.isOpen).toBe(false);
|
|
386
|
+
expect(result.current.formData).toBeNull();
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe('Type safety', () => {
|
|
391
|
+
it('should maintain type safety with generic type parameter', () => {
|
|
392
|
+
const testData: TestFormData = {
|
|
393
|
+
id: '1',
|
|
394
|
+
name: 'John Doe',
|
|
395
|
+
email: 'john@example.com'
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
399
|
+
|
|
400
|
+
act(() => {
|
|
401
|
+
result.current.openDialog(testData);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// TypeScript should infer the correct type
|
|
405
|
+
if (result.current.formData) {
|
|
406
|
+
expect(result.current.formData.id).toBe('1');
|
|
407
|
+
expect(result.current.formData.name).toBe('John Doe');
|
|
408
|
+
expect(result.current.formData.email).toBe('john@example.com');
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should work with primitive types', () => {
|
|
413
|
+
const { result } = renderHook(() => useFormDialog<string>());
|
|
414
|
+
|
|
415
|
+
act(() => {
|
|
416
|
+
result.current.openDialog('test string');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
expect(result.current.formData).toBe('test string');
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should work with number types', () => {
|
|
423
|
+
const { result } = renderHook(() => useFormDialog<number>());
|
|
424
|
+
|
|
425
|
+
act(() => {
|
|
426
|
+
result.current.openDialog(42);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(result.current.formData).toBe(42);
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
describe('Options handling', () => {
|
|
434
|
+
it('should use default resetOnClose value of true', () => {
|
|
435
|
+
const testData: TestFormData = {
|
|
436
|
+
id: '1',
|
|
437
|
+
name: 'John Doe',
|
|
438
|
+
email: 'john@example.com'
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const { result } = renderHook(() => useFormDialog<TestFormData>());
|
|
442
|
+
|
|
443
|
+
act(() => {
|
|
444
|
+
result.current.openDialog(testData);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
act(() => {
|
|
448
|
+
result.current.closeDialog();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// Should reset by default
|
|
452
|
+
expect(result.current.formData).toBeNull();
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('should respect resetOnClose option', () => {
|
|
456
|
+
const testData: TestFormData = {
|
|
457
|
+
id: '1',
|
|
458
|
+
name: 'John Doe',
|
|
459
|
+
email: 'john@example.com'
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const { result } = renderHook(() =>
|
|
463
|
+
useFormDialog<TestFormData>({ resetOnClose: false })
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
act(() => {
|
|
467
|
+
result.current.openDialog(testData);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
act(() => {
|
|
471
|
+
result.current.closeDialog();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Should not reset when resetOnClose is false
|
|
475
|
+
expect(result.current.formData).toEqual(testData);
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
});
|
package/src/hooks/index.ts
CHANGED
|
@@ -39,6 +39,8 @@ export type { OrganisationSecurityHook } from './useOrganisationSecurity';
|
|
|
39
39
|
|
|
40
40
|
// === FORM HOOKS ===
|
|
41
41
|
export { useZodForm } from './useZodForm';
|
|
42
|
+
export { useFormDialog } from './useFormDialog';
|
|
43
|
+
export type { UseFormDialogOptions, UseFormDialogReturn } from './useFormDialog';
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
// === PERFORMANCE HOOKS ===
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useFormDialog Hook
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Hooks
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Generic React hook for managing form dialog state (open/close, form data, reset behavior).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* interface ContactFormData {
|
|
12
|
+
* name: string;
|
|
13
|
+
* email: string;
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* function ContactDialog() {
|
|
17
|
+
* const { isOpen, formData, openDialog, closeDialog, handleOpenChange } =
|
|
18
|
+
* useFormDialog<ContactFormData>();
|
|
19
|
+
*
|
|
20
|
+
* // Use in Dialog component
|
|
21
|
+
* // <Dialog open={isOpen} onOpenChange={handleOpenChange}>...</Dialog>
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { useState, useCallback } from 'react';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Options for configuring the useFormDialog hook behavior
|
|
30
|
+
*/
|
|
31
|
+
export interface UseFormDialogOptions<T = unknown> {
|
|
32
|
+
/**
|
|
33
|
+
* Callback invoked when the dialog open state changes
|
|
34
|
+
* @param open - The new open state
|
|
35
|
+
*/
|
|
36
|
+
onOpenChange?: (open: boolean) => void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Whether to reset form data when the dialog closes
|
|
40
|
+
* @default true
|
|
41
|
+
*/
|
|
42
|
+
resetOnClose?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Return type for the useFormDialog hook
|
|
47
|
+
*/
|
|
48
|
+
export interface UseFormDialogReturn<T = unknown> {
|
|
49
|
+
/**
|
|
50
|
+
* Current open state of the dialog
|
|
51
|
+
*/
|
|
52
|
+
isOpen: boolean;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Current form data (null when dialog is closed or no data provided)
|
|
56
|
+
*/
|
|
57
|
+
formData: T | null;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Open the dialog with optional form data
|
|
61
|
+
* @param data - Optional form data to populate the dialog
|
|
62
|
+
*/
|
|
63
|
+
openDialog: (data?: T | null) => void;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Close the dialog
|
|
67
|
+
*/
|
|
68
|
+
closeDialog: () => void;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Handler for controlled dialog components (e.g., Radix UI Dialog)
|
|
72
|
+
* @param open - The new open state
|
|
73
|
+
*/
|
|
74
|
+
handleOpenChange: (open: boolean) => void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generic React hook for managing form dialog state.
|
|
79
|
+
*
|
|
80
|
+
* Provides state management for form dialogs including:
|
|
81
|
+
* - Open/close state
|
|
82
|
+
* - Form data management
|
|
83
|
+
* - Automatic reset on close (configurable)
|
|
84
|
+
* - Controlled and uncontrolled usage patterns
|
|
85
|
+
*
|
|
86
|
+
* @param options - Configuration options for the hook
|
|
87
|
+
* @returns Object containing dialog state and control functions
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* // Basic usage
|
|
92
|
+
* const { isOpen, openDialog, closeDialog } = useFormDialog();
|
|
93
|
+
*
|
|
94
|
+
* // With form data
|
|
95
|
+
* interface UserData {
|
|
96
|
+
* id: string;
|
|
97
|
+
* name: string;
|
|
98
|
+
* }
|
|
99
|
+
* const { isOpen, formData, openDialog } = useFormDialog<UserData>();
|
|
100
|
+
*
|
|
101
|
+
* // Open with data
|
|
102
|
+
* openDialog({ id: '1', name: 'John' });
|
|
103
|
+
*
|
|
104
|
+
* // Controlled usage with callback
|
|
105
|
+
* const { handleOpenChange, isOpen } = useFormDialog({
|
|
106
|
+
* onOpenChange: (open) => console.log('Dialog state:', open),
|
|
107
|
+
* resetOnClose: false
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export function useFormDialog<T = unknown>({
|
|
112
|
+
onOpenChange,
|
|
113
|
+
resetOnClose = true,
|
|
114
|
+
}: UseFormDialogOptions<T> = {}): UseFormDialogReturn<T> {
|
|
115
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
116
|
+
const [formData, setFormData] = useState<T | null>(null);
|
|
117
|
+
|
|
118
|
+
const openDialog = useCallback((data: T | null = null) => {
|
|
119
|
+
setFormData(data);
|
|
120
|
+
setIsOpen(true);
|
|
121
|
+
onOpenChange?.(true);
|
|
122
|
+
}, [onOpenChange]);
|
|
123
|
+
|
|
124
|
+
const closeDialog = useCallback(() => {
|
|
125
|
+
setIsOpen(false);
|
|
126
|
+
if (resetOnClose) {
|
|
127
|
+
setFormData(null);
|
|
128
|
+
}
|
|
129
|
+
onOpenChange?.(false);
|
|
130
|
+
}, [resetOnClose, onOpenChange]);
|
|
131
|
+
|
|
132
|
+
const handleOpenChange = useCallback((open: boolean) => {
|
|
133
|
+
setIsOpen(open);
|
|
134
|
+
if (!open && resetOnClose) {
|
|
135
|
+
setFormData(null);
|
|
136
|
+
}
|
|
137
|
+
onOpenChange?.(open);
|
|
138
|
+
}, [resetOnClose, onOpenChange]);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
isOpen,
|
|
142
|
+
formData,
|
|
143
|
+
openDialog,
|
|
144
|
+
closeDialog,
|
|
145
|
+
handleOpenChange,
|
|
146
|
+
};
|
|
147
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -189,6 +189,17 @@ export type { FormProps, FormFieldProps } from './components/Form/Form';
|
|
|
189
189
|
export { LoginForm } from './components/LoginForm';
|
|
190
190
|
export type { LoginFormProps } from './components/LoginForm';
|
|
191
191
|
|
|
192
|
+
// FORM HOOKS
|
|
193
|
+
export { useZodForm } from './hooks/useZodForm';
|
|
194
|
+
export { useFormDialog } from './hooks/useFormDialog';
|
|
195
|
+
export type { UseFormDialogOptions, UseFormDialogReturn } from './hooks/useFormDialog';
|
|
196
|
+
|
|
197
|
+
// VALIDATION - Re-export zod for schema creation
|
|
198
|
+
export { z } from 'zod';
|
|
199
|
+
// Common validation schemas
|
|
200
|
+
export { emailSchema, nameSchema, phoneSchema, urlSchema } from './utils/validation/common';
|
|
201
|
+
export { passwordSchema } from './utils/validation/passwordSchema';
|
|
202
|
+
|
|
192
203
|
// LAYOUT COMPONENTS
|
|
193
204
|
export { Header } from './components/Header/Header';
|
|
194
205
|
export { Footer } from './components/Footer/Footer';
|
|
@@ -236,6 +247,10 @@ export { useEventTheme } from './hooks/useEventTheme';
|
|
|
236
247
|
export { cn } from './utils/core/cn';
|
|
237
248
|
export { setAppConfig, getAppConfig, getCurrentAppName, getCurrentAppId } from './utils/app/appConfig';
|
|
238
249
|
|
|
250
|
+
// LOGGING UTILITIES
|
|
251
|
+
export { Logger, logger, createLogger, LogLevel } from './utils/core/logger';
|
|
252
|
+
export type { LoggerConfig } from './utils/core/logger';
|
|
253
|
+
|
|
239
254
|
// FORMATTING UTILITIES
|
|
240
255
|
export {
|
|
241
256
|
formatDate,
|
|
@@ -248,6 +263,18 @@ export {
|
|
|
248
263
|
formatFileSize
|
|
249
264
|
} from './utils/formatting/formatting';
|
|
250
265
|
|
|
266
|
+
// TIMEZONE UTILITIES
|
|
267
|
+
export {
|
|
268
|
+
formatInTimeZone,
|
|
269
|
+
getTimezoneAbbreviation,
|
|
270
|
+
formatTimeInTimeZone,
|
|
271
|
+
getUserTimeZone,
|
|
272
|
+
toZonedTime,
|
|
273
|
+
fromZonedTime,
|
|
274
|
+
roundToNearestMinutes,
|
|
275
|
+
getTimeZoneDifference
|
|
276
|
+
} from './utils/timezone';
|
|
277
|
+
|
|
251
278
|
// STORAGE UTILITIES
|
|
252
279
|
export { FileUpload } from './components/FileUpload';
|
|
253
280
|
export type { FileUploadProps } from './components/FileUpload';
|
|
@@ -49,11 +49,12 @@ export function OrganisationServiceProvider({
|
|
|
49
49
|
// Re-initialize service when user/session changes
|
|
50
50
|
let isMounted = true;
|
|
51
51
|
|
|
52
|
-
organisationService.initialize()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
organisationService.initialize()
|
|
53
|
+
.catch(error => {
|
|
54
|
+
if (isMounted) {
|
|
55
|
+
logger.error('OrganisationServiceProvider', 'Failed to initialize organisation service:', error);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
57
58
|
|
|
58
59
|
return () => {
|
|
59
60
|
isMounted = false;
|