@umituz/react-native-design-system 2.6.128 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/package.json +4 -2
  2. package/src/exports/onboarding.ts +6 -0
  3. package/src/index.ts +5 -0
  4. package/src/molecules/navigation/utils/AppNavigation.ts +53 -28
  5. package/src/molecules/splash/hooks/useSplashFlow.ts +33 -7
  6. package/src/onboarding/domain/entities/OnboardingOptions.ts +104 -0
  7. package/src/onboarding/domain/entities/OnboardingQuestion.ts +165 -0
  8. package/src/onboarding/domain/entities/OnboardingSlide.ts +152 -0
  9. package/src/onboarding/domain/entities/OnboardingUserData.ts +43 -0
  10. package/src/onboarding/hooks/useOnboardingFlow.ts +50 -0
  11. package/src/onboarding/index.ts +108 -0
  12. package/src/onboarding/infrastructure/hooks/__tests__/useOnboardingAnswers.test.ts +163 -0
  13. package/src/onboarding/infrastructure/hooks/__tests__/useOnboardingNavigation.test.ts +121 -0
  14. package/src/onboarding/infrastructure/hooks/useOnboardingAnswers.ts +69 -0
  15. package/src/onboarding/infrastructure/hooks/useOnboardingNavigation.ts +75 -0
  16. package/src/onboarding/infrastructure/services/SlideManager.ts +53 -0
  17. package/src/onboarding/infrastructure/services/ValidationManager.ts +127 -0
  18. package/src/onboarding/infrastructure/storage/OnboardingStore.ts +99 -0
  19. package/src/onboarding/infrastructure/storage/OnboardingStoreActions.ts +50 -0
  20. package/src/onboarding/infrastructure/storage/OnboardingStoreSelectors.ts +25 -0
  21. package/src/onboarding/infrastructure/storage/OnboardingStoreState.ts +22 -0
  22. package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +85 -0
  23. package/src/onboarding/infrastructure/storage/actions/answerActions.ts +47 -0
  24. package/src/onboarding/infrastructure/storage/actions/completeAction.ts +45 -0
  25. package/src/onboarding/infrastructure/storage/actions/index.ts +22 -0
  26. package/src/onboarding/infrastructure/storage/actions/initializeAction.ts +40 -0
  27. package/src/onboarding/infrastructure/storage/actions/resetAction.ts +37 -0
  28. package/src/onboarding/infrastructure/storage/actions/skipAction.ts +46 -0
  29. package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +60 -0
  30. package/src/onboarding/infrastructure/utils/arrayUtils.ts +28 -0
  31. package/src/onboarding/infrastructure/utils/backgroundUtils.ts +38 -0
  32. package/src/onboarding/infrastructure/utils/layouts/collageLayout.ts +81 -0
  33. package/src/onboarding/infrastructure/utils/layouts/gridLayouts.ts +78 -0
  34. package/src/onboarding/infrastructure/utils/layouts/honeycombLayout.ts +36 -0
  35. package/src/onboarding/infrastructure/utils/layouts/index.ts +12 -0
  36. package/src/onboarding/infrastructure/utils/layouts/layoutTypes.ts +37 -0
  37. package/src/onboarding/infrastructure/utils/layouts/masonryLayout.ts +37 -0
  38. package/src/onboarding/infrastructure/utils/layouts/scatteredLayout.ts +34 -0
  39. package/src/onboarding/infrastructure/utils/layouts/screenDimensions.ts +11 -0
  40. package/src/onboarding/infrastructure/utils/layouts/tilesLayout.ts +34 -0
  41. package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +90 -0
  42. package/src/onboarding/presentation/components/BackgroundVideo.tsx +24 -0
  43. package/src/onboarding/presentation/components/BaseSlide.tsx +47 -0
  44. package/src/onboarding/presentation/components/OnboardingBackground.tsx +91 -0
  45. package/src/onboarding/presentation/components/OnboardingFooter.tsx +151 -0
  46. package/src/onboarding/presentation/components/OnboardingHeader.tsx +92 -0
  47. package/src/onboarding/presentation/components/OnboardingResetSetting.tsx +70 -0
  48. package/src/onboarding/presentation/components/OnboardingScreenContent.tsx +146 -0
  49. package/src/onboarding/presentation/components/OnboardingSlide.tsx +124 -0
  50. package/src/onboarding/presentation/components/QuestionRenderer.tsx +60 -0
  51. package/src/onboarding/presentation/components/QuestionSlide.tsx +67 -0
  52. package/src/onboarding/presentation/components/QuestionSlideHeader.tsx +75 -0
  53. package/src/onboarding/presentation/components/questions/MultipleChoiceQuestion.tsx +74 -0
  54. package/src/onboarding/presentation/components/questions/QuestionOptionItem.tsx +115 -0
  55. package/src/onboarding/presentation/components/questions/RatingQuestion.tsx +66 -0
  56. package/src/onboarding/presentation/components/questions/SingleChoiceQuestion.tsx +117 -0
  57. package/src/onboarding/presentation/components/questions/TextInputQuestion.tsx +71 -0
  58. package/src/onboarding/presentation/hooks/__tests__/useOnboardingContainerStyle.test.ts +96 -0
  59. package/src/onboarding/presentation/hooks/useOnboardingContainerStyle.ts +37 -0
  60. package/src/onboarding/presentation/hooks/useOnboardingGestures.ts +45 -0
  61. package/src/onboarding/presentation/hooks/useOnboardingScreenHandlers.ts +114 -0
  62. package/src/onboarding/presentation/hooks/useOnboardingScreenState.ts +146 -0
  63. package/src/onboarding/presentation/providers/OnboardingProvider.tsx +51 -0
  64. package/src/onboarding/presentation/screens/OnboardingScreen.tsx +189 -0
  65. package/src/onboarding/presentation/types/OnboardingProps.ts +46 -0
  66. package/src/onboarding/presentation/types/OnboardingTheme.ts +27 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Onboarding Validation Service
3
+ *
4
+ * Business logic for validating onboarding question answers
5
+ * Follows Single Responsibility Principle
6
+ */
7
+
8
+ import type { OnboardingQuestion } from "../../domain/entities/OnboardingQuestion";
9
+
10
+ /**
11
+ * ValidationManager
12
+ */
13
+ export class ValidationManager {
14
+ /**
15
+ * Validate answer against question validation rules
16
+ * @param question - The question to validate against
17
+ * @param answer - The answer to validate
18
+ * @returns true if valid, false otherwise
19
+ */
20
+ static validateAnswer(
21
+ question: OnboardingQuestion,
22
+ answer: any,
23
+ ): boolean {
24
+ const { validation } = question;
25
+ if (!validation) {
26
+ return true;
27
+ }
28
+
29
+ // Required validation
30
+ if (validation.required && !answer) {
31
+ return false;
32
+ }
33
+
34
+ // Type-specific validations
35
+ switch (question.type) {
36
+ case "multiple_choice":
37
+ return this.validateMultipleChoice(answer, validation);
38
+ case "text_input":
39
+ return this.validateTextInput(answer, validation);
40
+ case "rating":
41
+ return this.validateNumeric(answer, validation);
42
+ default:
43
+ break;
44
+ }
45
+
46
+ // Custom validator
47
+ if (validation.customValidator) {
48
+ const customResult = validation.customValidator(answer);
49
+ return customResult === true;
50
+ }
51
+
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Validate multiple choice answer
57
+ */
58
+ private static validateMultipleChoice(
59
+ answer: any,
60
+ validation: OnboardingQuestion["validation"],
61
+ ): boolean {
62
+ if (!validation) return true;
63
+
64
+ if (validation.minSelections) {
65
+ if (!answer || !Array.isArray(answer) || answer.length < validation.minSelections) {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ if (validation.maxSelections) {
71
+ if (Array.isArray(answer) && answer.length > validation.maxSelections) {
72
+ return false;
73
+ }
74
+ }
75
+
76
+ return true;
77
+ }
78
+
79
+ /**
80
+ * Validate text input answer
81
+ */
82
+ private static validateTextInput(
83
+ answer: any,
84
+ validation: OnboardingQuestion["validation"],
85
+ ): boolean {
86
+ if (!validation) return true;
87
+
88
+ if (typeof answer !== "string") {
89
+ return false;
90
+ }
91
+
92
+ if (validation.minLength && answer.length < validation.minLength) {
93
+ return false;
94
+ }
95
+
96
+ if (validation.maxLength && answer.length > validation.maxLength) {
97
+ return false;
98
+ }
99
+
100
+ return true;
101
+ }
102
+
103
+ /**
104
+ * Validate numeric answer (rating)
105
+ */
106
+ private static validateNumeric(
107
+ answer: any,
108
+ validation: OnboardingQuestion["validation"],
109
+ ): boolean {
110
+ if (!validation) return true;
111
+
112
+ if (typeof answer !== "number") {
113
+ return false;
114
+ }
115
+
116
+ if (validation.min !== undefined && answer < validation.min) {
117
+ return false;
118
+ }
119
+
120
+ if (validation.max !== undefined && answer > validation.max) {
121
+ return false;
122
+ }
123
+
124
+ return true;
125
+ }
126
+ }
127
+
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Onboarding Store
3
+ *
4
+ * Zustand store for managing onboarding completion state
5
+ * Uses @umituz/react-native-storage for persistence
6
+ */
7
+
8
+ import { useMemo } from "react";
9
+ import { createStore } from "@umituz/react-native-storage";
10
+ import type { OnboardingStoreState } from "./OnboardingStoreState";
11
+ import { initialOnboardingState } from "./OnboardingStoreState";
12
+ import { createOnboardingStoreActions } from "./OnboardingStoreActions";
13
+ import type { OnboardingUserData } from "../../domain/entities/OnboardingUserData";
14
+ import { createOnboardingStoreSelectors } from "./OnboardingStoreSelectors";
15
+
16
+ interface OnboardingActions {
17
+ // Simple actions
18
+ setCurrentStep: (step: number) => void;
19
+ setLoading: (loading: boolean) => void;
20
+ setError: (error: string | null) => void;
21
+ setState: (state: Partial<OnboardingStoreState>) => void;
22
+ getState: () => OnboardingStoreState;
23
+ // Async actions for initialization (match OnboardingStoreActions signatures)
24
+ initialize: (storageKey?: string) => Promise<void>;
25
+ complete: (storageKey?: string) => Promise<void>;
26
+ skip: (storageKey?: string) => Promise<void>;
27
+ reset: (storageKey?: string) => Promise<void>;
28
+ saveAnswer: (questionId: string, answer: unknown) => Promise<void>;
29
+ setUserData: (data: OnboardingUserData) => Promise<void>;
30
+ }
31
+
32
+ export const useOnboardingStore = createStore<
33
+ OnboardingStoreState,
34
+ OnboardingActions
35
+ >({
36
+ name: "onboarding-store",
37
+ initialState: initialOnboardingState,
38
+ persist: false,
39
+ actions: (
40
+ set: (state: Partial<OnboardingStoreState>) => void,
41
+ get: () => OnboardingStoreState
42
+ ): OnboardingActions => {
43
+ const actions = createOnboardingStoreActions(set, get);
44
+
45
+ return {
46
+ setCurrentStep: (step: number) => set({ currentStep: step }),
47
+ setLoading: (loading: boolean) => set({ loading }),
48
+ setError: (error: string | null) => set({ error }),
49
+ setState: set,
50
+ getState: get,
51
+
52
+ // Async actions from actions module
53
+ initialize: actions.initialize,
54
+ complete: actions.complete,
55
+ skip: actions.skip,
56
+ reset: actions.reset,
57
+ saveAnswer: actions.saveAnswer,
58
+ setUserData: actions.setUserData,
59
+ };
60
+ },
61
+ });
62
+
63
+ /**
64
+ * Hook for accessing onboarding state
65
+ * Memoized to prevent unnecessary re-renders in consumer components
66
+ */
67
+ export const useOnboarding = () => {
68
+ const store = useOnboardingStore();
69
+ const setState = store.setState;
70
+ const getState = store.getState;
71
+
72
+ const actions = useMemo(() => createOnboardingStoreActions(setState, getState), [setState, getState]);
73
+ const selectors = useMemo(() => createOnboardingStoreSelectors(getState), [getState]);
74
+
75
+ return useMemo(() => ({
76
+ // State
77
+ isOnboardingComplete: store.isOnboardingComplete,
78
+ currentStep: store.currentStep,
79
+ loading: store.loading,
80
+ error: store.error,
81
+ userData: store.userData,
82
+
83
+ // Actions
84
+ initialize: actions.initialize,
85
+ complete: actions.complete,
86
+ skip: actions.skip,
87
+ setCurrentStep: store.setCurrentStep,
88
+ reset: actions.reset,
89
+ setLoading: store.setLoading,
90
+ setError: store.setError,
91
+ saveAnswer: actions.saveAnswer,
92
+ setUserData: actions.setUserData,
93
+
94
+ // Selectors
95
+ getAnswer: selectors.getAnswer,
96
+ getUserData: selectors.getUserData,
97
+ }), [store, actions, selectors]);
98
+ };
99
+
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Onboarding Store Actions
3
+ * Single Responsibility: Async store actions interface and factory
4
+ */
5
+
6
+ import type { OnboardingUserData } from "../../domain/entities/OnboardingUserData";
7
+ import type { OnboardingStoreState } from "./OnboardingStoreState";
8
+ import {
9
+ initializeAction,
10
+ completeAction,
11
+ skipAction,
12
+ resetAction,
13
+ saveAnswerAction,
14
+ setUserDataAction,
15
+ DEFAULT_STORAGE_KEY,
16
+ } from "./actions";
17
+
18
+ export interface OnboardingStoreActions {
19
+ initialize: (storageKey?: string) => Promise<void>;
20
+ complete: (storageKey?: string) => Promise<void>;
21
+ skip: (storageKey?: string) => Promise<void>;
22
+ reset: (storageKey?: string) => Promise<void>;
23
+ saveAnswer: (questionId: string, answer: unknown) => Promise<void>;
24
+ setUserData: (data: OnboardingUserData) => Promise<void>;
25
+ }
26
+
27
+ export function createOnboardingStoreActions(
28
+ set: (state: Partial<OnboardingStoreState>) => void,
29
+ get: () => OnboardingStoreState
30
+ ): OnboardingStoreActions {
31
+ return {
32
+ initialize: (storageKey = DEFAULT_STORAGE_KEY) =>
33
+ initializeAction(set, storageKey),
34
+
35
+ complete: (storageKey = DEFAULT_STORAGE_KEY) =>
36
+ completeAction(set, get, storageKey),
37
+
38
+ skip: (storageKey = DEFAULT_STORAGE_KEY) =>
39
+ skipAction(set, get, storageKey),
40
+
41
+ reset: (storageKey = DEFAULT_STORAGE_KEY) =>
42
+ resetAction(set, storageKey),
43
+
44
+ saveAnswer: (questionId: string, answer: unknown) =>
45
+ saveAnswerAction(set, get, questionId, answer),
46
+
47
+ setUserData: (data: OnboardingUserData) =>
48
+ setUserDataAction(set, data),
49
+ };
50
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Onboarding Store Selectors
3
+ * Single Responsibility: Store state selectors
4
+ */
5
+
6
+ import type { OnboardingStoreState } from "./OnboardingStoreState";
7
+
8
+ export interface OnboardingStoreSelectors {
9
+ getAnswer: (questionId: string) => any;
10
+ getUserData: () => OnboardingStoreState['userData'];
11
+ }
12
+
13
+ export function createOnboardingStoreSelectors(
14
+ get: () => OnboardingStoreState
15
+ ): OnboardingStoreSelectors {
16
+ return {
17
+ getAnswer: (questionId: string) => {
18
+ return get().userData.answers[questionId];
19
+ },
20
+
21
+ getUserData: () => {
22
+ return get().userData;
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Onboarding Store State
3
+ * Single Responsibility: Store state interface and initial state
4
+ */
5
+
6
+ import type { OnboardingUserData } from "../../domain/entities/OnboardingUserData";
7
+
8
+ export interface OnboardingStoreState {
9
+ isOnboardingComplete: boolean;
10
+ currentStep: number;
11
+ loading: boolean;
12
+ error: string | null;
13
+ userData: OnboardingUserData;
14
+ }
15
+
16
+ export const initialOnboardingState: OnboardingStoreState = {
17
+ isOnboardingComplete: false,
18
+ currentStep: 0,
19
+ loading: true,
20
+ error: null,
21
+ userData: { answers: {} },
22
+ };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * OnboardingStore Tests
3
+ */
4
+
5
+ import { renderHook, act } from '@testing-library/react-native';
6
+ import { useOnboardingStore, useOnboarding } from '../OnboardingStore';
7
+
8
+ // Mock storage repository
9
+ jest.mock('@umituz/react-native-storage', () => ({
10
+ storageRepository: {
11
+ getString: jest.fn(),
12
+ setString: jest.fn(),
13
+ getObject: jest.fn(),
14
+ setObject: jest.fn(),
15
+ removeItem: jest.fn(),
16
+ },
17
+ StorageKey: {
18
+ ONBOARDING_COMPLETED: '@onboarding_completed',
19
+ },
20
+ unwrap: jest.fn((result, defaultValue) => result.success ? result.data : defaultValue),
21
+ }));
22
+
23
+ describe('OnboardingStore', () => {
24
+ beforeEach(() => {
25
+ jest.clearAllMocks();
26
+ });
27
+
28
+ describe('useOnboardingStore', () => {
29
+ it('should have initial state', () => {
30
+ const { result } = renderHook(() => useOnboardingStore());
31
+
32
+ expect(result.current.isOnboardingComplete).toBe(false);
33
+ expect(result.current.currentStep).toBe(0);
34
+ expect(result.current.loading).toBe(true);
35
+ expect(result.current.error).toBe(null);
36
+ expect(result.current.userData).toEqual({ answers: {} });
37
+ });
38
+
39
+ it('should set current step', () => {
40
+ const { result } = renderHook(() => useOnboardingStore());
41
+
42
+ act(() => {
43
+ result.current.setCurrentStep(5);
44
+ });
45
+
46
+ expect(result.current.currentStep).toBe(5);
47
+ });
48
+
49
+ it('should set loading state', () => {
50
+ const { result } = renderHook(() => useOnboardingStore());
51
+
52
+ act(() => {
53
+ result.current.setLoading(false);
54
+ });
55
+
56
+ expect(result.current.loading).toBe(false);
57
+ });
58
+
59
+ it('should set error state', () => {
60
+ const { result } = renderHook(() => useOnboardingStore());
61
+ const errorMessage = 'Test error';
62
+
63
+ act(() => {
64
+ result.current.setError(errorMessage);
65
+ });
66
+
67
+ expect(result.current.error).toBe(errorMessage);
68
+ });
69
+ });
70
+
71
+ describe('useOnboarding', () => {
72
+ it('should return all store properties and methods', () => {
73
+ const { result } = renderHook(() => useOnboarding());
74
+
75
+ expect(typeof result.current.initialize).toBe('function');
76
+ expect(typeof result.current.complete).toBe('function');
77
+ expect(typeof result.current.skip).toBe('function');
78
+ expect(typeof result.current.reset).toBe('function');
79
+ expect(typeof result.current.saveAnswer).toBe('function');
80
+ expect(typeof result.current.getAnswer).toBe('function');
81
+ expect(typeof result.current.getUserData).toBe('function');
82
+ expect(typeof result.current.setUserData).toBe('function');
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Answer Actions
3
+ * Single Responsibility: Save and update user answers
4
+ */
5
+
6
+ import { storageRepository } from "@umituz/react-native-storage";
7
+ import type { OnboardingUserData } from "../../../domain/entities/OnboardingUserData";
8
+ import type { OnboardingStoreState } from "../OnboardingStoreState";
9
+ import { USER_DATA_STORAGE_KEY, handleError, logSuccess } from "./storageHelpers";
10
+
11
+ export async function saveAnswerAction(
12
+ set: (state: Partial<OnboardingStoreState>) => void,
13
+ get: () => OnboardingStoreState,
14
+ questionId: string,
15
+ answer: unknown
16
+ ): Promise<void> {
17
+ try {
18
+ const userData: OnboardingUserData = {
19
+ ...get().userData,
20
+ answers: {
21
+ ...get().userData.answers,
22
+ [questionId]: answer,
23
+ },
24
+ };
25
+
26
+ await storageRepository.setItem(USER_DATA_STORAGE_KEY, userData);
27
+ set({ userData });
28
+
29
+ logSuccess(`Answer saved for question: ${questionId}`);
30
+ } catch (error) {
31
+ handleError(error, "save answer");
32
+ }
33
+ }
34
+
35
+ export async function setUserDataAction(
36
+ set: (state: Partial<OnboardingStoreState>) => void,
37
+ data: OnboardingUserData
38
+ ): Promise<void> {
39
+ try {
40
+ await storageRepository.setItem(USER_DATA_STORAGE_KEY, data);
41
+ set({ userData: data });
42
+
43
+ logSuccess("User data updated successfully");
44
+ } catch (error) {
45
+ handleError(error, "set user data");
46
+ }
47
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Complete Action
3
+ * Single Responsibility: Mark onboarding as completed
4
+ */
5
+
6
+ import type { OnboardingUserData } from "../../../domain/entities/OnboardingUserData";
7
+ import type { OnboardingStoreState } from "../OnboardingStoreState";
8
+ import {
9
+ saveCompletionStatus,
10
+ saveUserData,
11
+ handleError,
12
+ logSuccess,
13
+ } from "./storageHelpers";
14
+
15
+ export async function completeAction(
16
+ set: (state: Partial<OnboardingStoreState>) => void,
17
+ get: () => OnboardingStoreState,
18
+ storageKey: string
19
+ ): Promise<void> {
20
+ try {
21
+ set({ loading: true, error: null });
22
+
23
+ await saveCompletionStatus(storageKey);
24
+
25
+ const userData: OnboardingUserData = {
26
+ ...get().userData,
27
+ completedAt: new Date().toISOString(),
28
+ };
29
+
30
+ await saveUserData(userData);
31
+
32
+ set({
33
+ isOnboardingComplete: true,
34
+ userData,
35
+ loading: false,
36
+ error: null,
37
+ });
38
+
39
+ logSuccess("Onboarding completed and persisted successfully");
40
+ } catch (error) {
41
+ const errorMessage = handleError(error, "complete onboarding");
42
+ set({ loading: false, error: errorMessage });
43
+ throw error;
44
+ }
45
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Actions Index
3
+ * Single Responsibility: Export all action functions
4
+ */
5
+
6
+ export {
7
+ loadCompletionStatus,
8
+ loadUserData,
9
+ saveCompletionStatus,
10
+ saveUserData,
11
+ removeStorageKeys,
12
+ handleError,
13
+ logSuccess,
14
+ DEFAULT_STORAGE_KEY,
15
+ USER_DATA_STORAGE_KEY,
16
+ } from "./storageHelpers";
17
+
18
+ export { initializeAction } from "./initializeAction";
19
+ export { completeAction } from "./completeAction";
20
+ export { skipAction } from "./skipAction";
21
+ export { resetAction } from "./resetAction";
22
+ export { saveAnswerAction, setUserDataAction } from "./answerActions";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Initialize Action
3
+ * Single Responsibility: Load initial onboarding state from storage
4
+ */
5
+
6
+ import type { OnboardingUserData } from "../../../domain/entities/OnboardingUserData";
7
+ import type { OnboardingStoreState } from "../OnboardingStoreState";
8
+ import {
9
+ loadCompletionStatus,
10
+ loadUserData,
11
+ handleError,
12
+ logSuccess,
13
+ } from "./storageHelpers";
14
+
15
+ export async function initializeAction(
16
+ set: (state: Partial<OnboardingStoreState>) => void,
17
+ storageKey: string
18
+ ): Promise<void> {
19
+ try {
20
+ set({ loading: true, error: null });
21
+
22
+ const isComplete = await loadCompletionStatus(storageKey);
23
+ const defaultData: OnboardingUserData = { answers: {} };
24
+ const userData = await loadUserData(defaultData);
25
+
26
+ set({
27
+ isOnboardingComplete: isComplete,
28
+ userData,
29
+ loading: false,
30
+ error: null,
31
+ });
32
+
33
+ logSuccess(`Initialized with completion status: ${isComplete}`);
34
+ } catch (error) {
35
+ set({
36
+ loading: false,
37
+ error: handleError(error, "initialize onboarding"),
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Reset Action
3
+ * Single Responsibility: Reset onboarding state
4
+ */
5
+
6
+ import type { OnboardingStoreState } from "../OnboardingStoreState";
7
+ import {
8
+ removeStorageKeys,
9
+ handleError,
10
+ logSuccess,
11
+ } from "./storageHelpers";
12
+
13
+ export async function resetAction(
14
+ set: (state: Partial<OnboardingStoreState>) => void,
15
+ storageKey: string
16
+ ): Promise<void> {
17
+ try {
18
+ set({ loading: true, error: null });
19
+
20
+ await removeStorageKeys(storageKey);
21
+
22
+ set({
23
+ isOnboardingComplete: false,
24
+ currentStep: 0,
25
+ userData: { answers: {} },
26
+ loading: false,
27
+ error: null,
28
+ });
29
+
30
+ logSuccess("Onboarding reset successfully");
31
+ } catch (error) {
32
+ set({
33
+ loading: false,
34
+ error: handleError(error, "reset onboarding"),
35
+ });
36
+ }
37
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Skip Action
3
+ * Single Responsibility: Mark onboarding as skipped
4
+ */
5
+
6
+ import type { OnboardingUserData } from "../../../domain/entities/OnboardingUserData";
7
+ import type { OnboardingStoreState } from "../OnboardingStoreState";
8
+ import {
9
+ saveCompletionStatus,
10
+ saveUserData,
11
+ handleError,
12
+ logSuccess,
13
+ } from "./storageHelpers";
14
+
15
+ export async function skipAction(
16
+ set: (state: Partial<OnboardingStoreState>) => void,
17
+ get: () => OnboardingStoreState,
18
+ storageKey: string
19
+ ): Promise<void> {
20
+ try {
21
+ set({ loading: true, error: null });
22
+
23
+ await saveCompletionStatus(storageKey);
24
+
25
+ const userData: OnboardingUserData = {
26
+ ...get().userData,
27
+ skipped: true,
28
+ completedAt: new Date().toISOString(),
29
+ };
30
+
31
+ await saveUserData(userData);
32
+
33
+ set({
34
+ isOnboardingComplete: true,
35
+ userData,
36
+ loading: false,
37
+ error: null,
38
+ });
39
+
40
+ logSuccess("Onboarding skipped and persisted successfully");
41
+ } catch (error) {
42
+ const errorMessage = handleError(error, "skip onboarding");
43
+ set({ loading: false, error: errorMessage });
44
+ throw error;
45
+ }
46
+ }