@encatch/web-sdk 0.0.35-beta.8 → 1.0.0-beta.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 (33) hide show
  1. package/README.md +140 -638
  2. package/dist/encatch.es.js +2 -0
  3. package/dist/encatch.es.js.map +1 -0
  4. package/dist/encatch.iife.js +2 -0
  5. package/dist/encatch.iife.js.map +1 -0
  6. package/dist/index.d.ts +191 -0
  7. package/package.json +32 -50
  8. package/dist-sdk/plugin/sdk/core-wrapper-BMvOyc0u.js +0 -22926
  9. package/dist-sdk/plugin/sdk/module-DC2Edddk.js +0 -481
  10. package/dist-sdk/plugin/sdk/module.js +0 -5
  11. package/dist-sdk/plugin/sdk/preview-sdk.html +0 -1182
  12. package/dist-sdk/plugin/sdk/vite.svg +0 -15
  13. package/dist-sdk/plugin/sdk/web-form-engine-core.css +0 -1
  14. package/index.d.ts +0 -207
  15. package/src/@types/encatch-type.ts +0 -111
  16. package/src/encatch-instance.ts +0 -161
  17. package/src/feedback-api-types.ts +0 -18
  18. package/src/hooks/useDevice.ts +0 -30
  19. package/src/hooks/useFeedbackInterval.ts +0 -71
  20. package/src/hooks/useFeedbackTriggers.ts +0 -342
  21. package/src/hooks/useFetchElligibleFeedbackConfiguration.ts +0 -363
  22. package/src/hooks/useFetchFeedbackConfigurationDetails.ts +0 -92
  23. package/src/hooks/usePageChangeTracker.ts +0 -88
  24. package/src/hooks/usePrepopulatedAnswers.ts +0 -123
  25. package/src/hooks/useRefineTextForm.ts +0 -55
  26. package/src/hooks/useSubmitFeedbackForm.ts +0 -134
  27. package/src/hooks/useUserSession.ts +0 -53
  28. package/src/module.tsx +0 -428
  29. package/src/store/formResponses.ts +0 -211
  30. package/src/utils/browser-details.ts +0 -36
  31. package/src/utils/duration-utils.ts +0 -158
  32. package/src/utils/feedback-frequency-storage.ts +0 -214
  33. package/src/utils/feedback-storage.ts +0 -166
@@ -1,363 +0,0 @@
1
- import { useState, useEffect, useRef } from "preact/hooks";
2
- import {
3
- FetchConfigurationListRequest,
4
- DeviceInfo,
5
- FeedbackConfigurationItem,
6
- MasterProperties,
7
- } from "@encatch/schema";
8
- import { EncatchApiSDK } from "@encatch/api-sdk";
9
- import { setPendingFeedbacks, getPendingFeedbacks } from "../utils/feedback-storage";
10
- import {
11
- getFrequencyTracking,
12
- initializeOrUpdateTracking,
13
- } from "../utils/feedback-frequency-storage";
14
- import { isEligibleByFrequency } from "../utils/duration-utils";
15
-
16
- /**
17
- * Filter feedbacks based on frequency rules
18
- */
19
- const filterByFrequencyRules = (
20
- feedbacks: FeedbackConfigurationItem[]
21
- ): FeedbackConfigurationItem[] => {
22
- const frequencyTracking = getFrequencyTracking();
23
- const currentTime = Date.now();
24
-
25
- const filtered = feedbacks.filter((fb) => {
26
- const fbId = fb.feedbackConfigurationId;
27
- const isManual = fb.triggerProperties?.isManual === true;
28
-
29
- // Check frequency rules
30
- const tracking = frequencyTracking[fbId];
31
-
32
- if (!tracking) {
33
- // No tracking data yet
34
- if (isManual && fb.frequencyAndScheduling) {
35
- // For manual feedbacks without tracking, check date range directly
36
- const startDate = new Date(fb.frequencyAndScheduling.startDate).getTime();
37
- const stopDate = new Date(fb.frequencyAndScheduling.stopDate).getTime();
38
- return currentTime >= startDate && currentTime <= stopDate;
39
- }
40
- // Never interacted before, always eligible for non-manual
41
- return true;
42
- }
43
-
44
- // Pass isManual flag to eligibility check
45
- // Manual feedbacks: Only check date range, bypass all other frequency rules
46
- // Non-manual feedbacks: Apply all frequency rules
47
- const eligible = isEligibleByFrequency(tracking, currentTime, isManual);
48
- return eligible;
49
- });
50
-
51
- return filtered;
52
- };
53
-
54
- interface StoredConfiguration {
55
- feedbackConfiguration: FeedbackConfigurationItem[];
56
- expiry: number;
57
- feedbackInterval: number;
58
- masterProperties: MasterProperties;
59
- }
60
-
61
- // MD5 hash function
62
- const md5 = (str: string): string => {
63
- const crypto = window.crypto || (window as any).msCrypto;
64
- if (!crypto) {
65
- // Fallback for older browsers
66
- return btoa(str)
67
- .replace(/[^a-zA-Z0-9]/g, "")
68
- .substring(0, 16);
69
- }
70
-
71
- // Simple hash implementation for browser compatibility
72
- let hash = 0;
73
- if (str.length === 0) return hash.toString();
74
-
75
- for (let i = 0; i < str.length; i++) {
76
- const char = str.charCodeAt(i);
77
- hash = (hash << 5) - hash + char;
78
- hash = hash & hash; // Convert to 32-bit integer
79
- }
80
-
81
- return Math.abs(hash).toString(16);
82
- };
83
-
84
- // Function to check if device has changed
85
- const isNewDevice = (deviceInfo: DeviceInfo): boolean => {
86
- const deviceHashKey = "encatch_device_hash";
87
-
88
- // Safety check for deviceInfo
89
- if (!deviceInfo || typeof deviceInfo !== "object") {
90
- console.warn(
91
- "isNewDevice: deviceInfo is null, undefined, or not an object:",
92
- deviceInfo
93
- );
94
- return true; // Treat as new device if deviceInfo is invalid
95
- }
96
-
97
- // Helper function to create consistent device string with sorted keys
98
- const createDeviceString = (device: DeviceInfo): string => {
99
- const deviceKeys = [
100
- "$deviceType",
101
- "$timezone",
102
- "$theme",
103
- "$os",
104
- "$osVersion",
105
- "$appVersion",
106
- "$app",
107
- "$language",
108
- "$deviceId",
109
- ];
110
-
111
- const orderedDevice: Record<string, any> = {};
112
- deviceKeys.forEach((key) => {
113
- const value = device[key as keyof DeviceInfo];
114
- if (value !== undefined && value !== null) {
115
- orderedDevice[key] = value;
116
- }
117
- });
118
-
119
- return JSON.stringify(orderedDevice);
120
- };
121
-
122
- // Check if device_hash flag exists in localStorage
123
- const storedHash = localStorage.getItem(deviceHashKey);
124
- if (!storedHash) {
125
- // No hash found, this is a new device
126
- const newHash = md5(createDeviceString(deviceInfo));
127
- localStorage.setItem(deviceHashKey, newHash);
128
- return true;
129
- }
130
-
131
- const currentHash = md5(createDeviceString(deviceInfo));
132
-
133
- // Compare hashes
134
- if (currentHash !== storedHash) {
135
- // Device has changed, update stored hash
136
- localStorage.setItem(deviceHashKey, currentHash);
137
- return true;
138
- }
139
-
140
- // Device is the same
141
- return false;
142
- };
143
-
144
- export const useFetchElligibleFeedbackConfiguration = ({
145
- apiKey,
146
- hostUrl,
147
- }: {
148
- apiKey: string;
149
- hostUrl: string;
150
- }) => {
151
- const [loading, setLoading] = useState(false);
152
- const [error, setError] = useState<string | null>(null);
153
- const [data, setData] = useState<any>(null);
154
- const [allFeedbacks, setAllFeedbacks] = useState<FeedbackConfigurationItem[]>([]); // Store all feedbacks including manual
155
- const [lastFetchParams, setLastFetchParams] =
156
- useState<FetchConfigurationListRequest | null>(null);
157
- const lastFetchParamsRef = useRef<FetchConfigurationListRequest | null>(null);
158
- const [feedbackInterval, setFeedbackInterval] = useState<number>(0);
159
- const [configSyncInterval, setConfigSyncInterval] = useState<number>(15 * 60); // Default 15 minutes
160
- const intervalIdRef = useRef<number | null>(null);
161
-
162
- const getStoredConfiguration = (): StoredConfiguration | null => {
163
- const stored = sessionStorage.getItem("encatch-configuration");
164
- if (!stored) return null;
165
-
166
- const config = JSON.parse(stored) as StoredConfiguration;
167
- if (Date.now() > config.expiry) {
168
- sessionStorage.removeItem("encatch-configuration");
169
- return null;
170
- }
171
-
172
- // Always use pending feedbacks as the source of truth
173
- const pendingFeedbacks = getPendingFeedbacks();
174
- // Filter by frequency rules (includes session views and frequency eligibility)
175
- const filteredFeedbacks = filterByFrequencyRules(pendingFeedbacks);
176
- config.feedbackConfiguration = filteredFeedbacks;
177
-
178
- return config;
179
- };
180
-
181
- const storeConfiguration = (config: StoredConfiguration) => {
182
- const syncInterval =
183
- config?.masterProperties?.configSyncIntervalSeconds || 15 * 60;
184
- const expiry = Date.now() + syncInterval * 1000;
185
- const disableAllSurvey =
186
- config?.masterProperties?.disableAllSurvey || false;
187
- const feedbackIntervalConfig =
188
- config?.masterProperties?.feedbackIntervalDuration || 1 * 60;
189
- const feedbackConfigurations = disableAllSurvey
190
- ? []
191
- : config?.feedbackConfiguration || [];
192
-
193
- // Store ALL feedbacks (including manual) for lookup by openFeedbackById/openFeedbackByName
194
- setAllFeedbacks(feedbackConfigurations);
195
-
196
- // Include all feedbacks in pending list
197
- // Manual feedbacks will respect frequency rules but bypass showOnce
198
- const pendingConfigurations = feedbackConfigurations || [];
199
-
200
- const storedConfig: StoredConfiguration = {
201
- feedbackConfiguration: pendingConfigurations,
202
- expiry,
203
- feedbackInterval: feedbackIntervalConfig,
204
- masterProperties: config?.masterProperties,
205
- };
206
-
207
- setFeedbackInterval(feedbackIntervalConfig);
208
- setConfigSyncInterval(syncInterval);
209
- sessionStorage.setItem(
210
- "encatch-configuration",
211
- JSON.stringify(storedConfig)
212
- );
213
-
214
- // Persist all configurations (including manual)
215
- setPendingFeedbacks(pendingConfigurations);
216
-
217
- // Initialize tracking only for non-manual feedbacks
218
- // Manual feedbacks don't need tracking as they only check date range
219
- pendingConfigurations.forEach((fb) => {
220
- const isManual = fb.triggerProperties?.isManual === true;
221
- if (fb.frequencyAndScheduling && !isManual) {
222
- initializeOrUpdateTracking(
223
- fb.feedbackConfigurationId,
224
- fb.frequencyAndScheduling as any
225
- );
226
- }
227
- });
228
- };
229
-
230
- const sdk = new EncatchApiSDK({
231
- apiKey,
232
- hostUrl,
233
- appPackageName: window.location.hostname, // Or your app's package name
234
- enableLogging: true,
235
- });
236
- const fetchConfiguration = async (params: FetchConfigurationListRequest) => {
237
- // Prevent API calls if apiKey is blank
238
- if (!apiKey || apiKey.trim() === '') {
239
- console.warn('fetchConfiguration: API key is required');
240
- setError('API key is required');
241
- return null;
242
- }
243
-
244
- setLastFetchParams(params);
245
- lastFetchParamsRef.current = params;
246
- setLoading(true);
247
- setError(null);
248
-
249
- // Check if we should use stored configuration
250
- if (!params.force) {
251
- const storedConfig = getStoredConfiguration();
252
- if (storedConfig) {
253
- const pendingFeedbacks = getPendingFeedbacks();
254
- const filteredFeedbacks = filterByFrequencyRules(pendingFeedbacks);
255
- setData(filteredFeedbacks);
256
- setAllFeedbacks(pendingFeedbacks); // Populate allFeedbacks for manual feedback lookup
257
- setFeedbackInterval(storedConfig.feedbackInterval);
258
- const syncInterval = storedConfig.masterProperties?.configSyncIntervalSeconds || 15 * 60;
259
- setConfigSyncInterval(syncInterval);
260
- setLoading(false);
261
- return filteredFeedbacks;
262
- }
263
- } else {
264
- sessionStorage.removeItem("encatch-configuration");
265
- }
266
-
267
- try {
268
- const isNew = isNewDevice(params.deviceInfo);
269
- let result;
270
- if (isNew) {
271
- result = await sdk.fetchNewDeviceConfiguration(params);
272
- } else {
273
- result = await sdk.fetchConfiguration(params);
274
- }
275
-
276
- storeConfiguration(result);
277
-
278
- const pendingFeedbacks = getPendingFeedbacks();
279
- setData(pendingFeedbacks);
280
- return result;
281
- } catch (err) {
282
- const errorMessage =
283
- err instanceof Error ? err.message : "An error occurred";
284
- setError(errorMessage);
285
- throw new Error(errorMessage);
286
- } finally {
287
- setLoading(false);
288
- }
289
- };
290
-
291
- const checkAndRefetchIfNeeded = () => {
292
- const storedConfig = getStoredConfiguration();
293
- const params = lastFetchParamsRef.current;
294
-
295
- if (!storedConfig && params) {
296
- // Configuration has expired, refetch
297
- fetchConfiguration({ ...params, force: true });
298
- }
299
- };
300
-
301
- const refreshPendingFeedbacks = () => {
302
- const pendingFeedbacks = getPendingFeedbacks();
303
- const filteredFeedbacks = filterByFrequencyRules(pendingFeedbacks);
304
- setData(filteredFeedbacks);
305
- };
306
-
307
- const startInterval = () => {
308
- if (intervalIdRef.current === null && configSyncInterval > 0) {
309
- const intervalMs = configSyncInterval * 1000;
310
- intervalIdRef.current = window.setInterval(
311
- checkAndRefetchIfNeeded,
312
- intervalMs
313
- );
314
- }
315
- };
316
-
317
- const stopInterval = () => {
318
- if (intervalIdRef.current !== null) {
319
- window.clearInterval(intervalIdRef.current);
320
- intervalIdRef.current = null;
321
- }
322
- };
323
-
324
- useEffect(() => {
325
- if (document.visibilityState === "visible") {
326
- startInterval();
327
- }
328
-
329
- const handleVisibilityChange = () => {
330
- if (document.visibilityState === "visible") {
331
- checkAndRefetchIfNeeded();
332
- startInterval();
333
- } else {
334
- stopInterval();
335
- }
336
- };
337
-
338
- document.addEventListener("visibilitychange", handleVisibilityChange);
339
-
340
- return () => {
341
- stopInterval();
342
- document.removeEventListener("visibilitychange", handleVisibilityChange);
343
- };
344
- }, []);
345
-
346
- // Restart interval when configSyncInterval changes
347
- useEffect(() => {
348
- if (document.visibilityState === "visible" && configSyncInterval > 0) {
349
- stopInterval();
350
- startInterval();
351
- }
352
- }, [configSyncInterval]);
353
-
354
- return {
355
- data,
356
- allFeedbacks, // Export all feedbacks including manual ones
357
- loading,
358
- error,
359
- fetchConfiguration,
360
- feedbackInterval,
361
- refreshPendingFeedbacks,
362
- };
363
- };
@@ -1,92 +0,0 @@
1
- import { EncatchApiSDK } from "@encatch/api-sdk";
2
- import { FetchFeedbackDetailsRequest, FetchFeedbackDetailsResponse} from "@encatch/schema";
3
- import { useState } from "preact/hooks";
4
-
5
- export const useFetchFeedbackConfigurationDetails = (
6
- {apiKey, hostUrl}:{apiKey: string, hostUrl:string}
7
- ) => {
8
- const [loadingFetchDetails, setLoadingFetchDetails] = useState(false);
9
- const [errorFetchDetails, setErrorFetchDetails] = useState<string | null>(null);
10
- const [successFetchDetails, setSuccessFetchDetails] = useState(false);
11
- // Instantiate the SDK once
12
- const sdk = new EncatchApiSDK({
13
- apiKey,
14
- hostUrl,
15
- appPackageName: window.location.hostname,
16
- enableLogging: true,
17
- });
18
-
19
- const fetchDetails = async (params: FetchFeedbackDetailsRequest): Promise<(FetchFeedbackDetailsResponse) | null> => {
20
- // Prevent API calls if apiKey is blank
21
- if (!apiKey || apiKey.trim() === '') {
22
- console.warn('fetchDetails: API key is required');
23
- setErrorFetchDetails('API key is required');
24
- return null;
25
- }
26
-
27
- setLoadingFetchDetails(true);
28
- setErrorFetchDetails(null);
29
- setSuccessFetchDetails(false);
30
-
31
- try {
32
- // Prepare request in the shape expected by the SDK
33
- const sdkParams: FetchFeedbackDetailsRequest = {
34
- formConfig: {
35
- feedbackConfigurationId: params.formConfig.feedbackConfigurationId,
36
- responseLanguageCode: params.formConfig.responseLanguageCode,
37
- },
38
- deviceInfo: params.deviceInfo,
39
- sessionInfo: {
40
- $sessionId: params.sessionInfo.$sessionId,
41
- },
42
- userInfo: params.userInfo,
43
- };
44
-
45
- const result: FetchFeedbackDetailsResponse = await sdk.fetchConfigurationDetails(sdkParams);
46
-
47
- // Validate that response has the required properties of ConfigurationResponse
48
- const isValidResponse = result &&
49
- typeof result === 'object' &&
50
- 'feedbackConfigurationId' in result &&
51
- 'formConfiguration' in result &&
52
- 'questionnaireFields' in result &&
53
- 'otherConfigurationProperties' in result &&
54
- 'welcomeScreenProperties' in result &&
55
- 'endScreenProperties' in result &&
56
- 'appearanceProperties' in result;
57
-
58
- if (!isValidResponse) {
59
- throw new Error('Invalid response format from server');
60
- }
61
-
62
- setSuccessFetchDetails(true);
63
-
64
- // Map camelCase SDK response to your expected ConfigurationResponse shape if needed
65
- return {
66
- feedbackConfigurationId: result.feedbackConfigurationId,
67
- feedbackIdentifier: result.feedbackIdentifier,
68
- formConfiguration: result.formConfiguration,
69
- questionnaireFields: result.questionnaireFields,
70
- otherConfigurationProperties: result.otherConfigurationProperties,
71
- welcomeScreenProperties: result.welcomeScreenProperties,
72
- endScreenProperties: result.endScreenProperties,
73
- // Include appearanceProperties if available in the response
74
- appearanceProperties: result.appearanceProperties || null,
75
- };
76
- } catch (err) {
77
- const errorMessage = err instanceof Error ? err.message : "An error occurred while fetching configuration details";
78
- setErrorFetchDetails(errorMessage);
79
- return null;
80
- } finally {
81
- setLoadingFetchDetails(false);
82
- }
83
- };
84
-
85
-
86
- return {
87
- loadingFetchDetails,
88
- errorFetchDetails,
89
- successFetchDetails,
90
- fetchDetails,
91
- };
92
- };
@@ -1,88 +0,0 @@
1
- import { useEffect } from "preact/hooks";
2
-
3
- interface LocationChangeProps {
4
- eventName?: string;
5
- trackEvent: (eventName: string, properties?: Record<string, any>) => void;
6
- }
7
-
8
- // Utility function to extract path from href, handling hash-based routing
9
- const extractPathFromHref = (href: string): string => {
10
- try {
11
- const url = new URL(href);
12
- // For hash-based routing, we want the hash part as the path
13
- if (url.hash && url.hash.length > 1) {
14
- // Remove the leading '#' and return the hash path
15
- return url.hash.substring(1);
16
- }
17
- // For regular routing, return the pathname
18
- return url.pathname;
19
- } catch (error) {
20
- // Fallback: try to extract path manually
21
- const urlObj = new URL(href, window.location.origin);
22
- if (urlObj.hash && urlObj.hash.length > 1) {
23
- return urlObj.hash.substring(1);
24
- }
25
- return urlObj.pathname;
26
- }
27
- };
28
-
29
- export const usePageChangeTracker = (props: LocationChangeProps) => {
30
- const trackLocationChange = () => {
31
- const href = window.location.href;
32
- const path = extractPathFromHref(href);
33
-
34
- props.trackEvent(props.eventName || "pageLoad", {
35
- url: href,
36
- path: path,
37
- });
38
- };
39
-
40
- useEffect(() => {
41
- // Track initial page load
42
- trackLocationChange();
43
-
44
- // Track subsequent navigation changes
45
- const observer = new MutationObserver(() => {
46
- trackLocationChange();
47
- });
48
-
49
- // Observe the document title for changes
50
- observer.observe(document.querySelector("title") || document.head, {
51
- subtree: true,
52
- characterData: true,
53
- childList: true,
54
- });
55
-
56
- // Listen for popstate events (browser back/forward)
57
- window.addEventListener("popstate", trackLocationChange);
58
-
59
- // Listen for pushstate events (React Router navigation)
60
- const originalPushState = window.history.pushState;
61
- window.history.pushState = function (
62
- data: any,
63
- unused: string,
64
- url?: string | URL | null
65
- ) {
66
- originalPushState.call(this, data, unused, url);
67
- trackLocationChange();
68
- };
69
-
70
- // Listen for React Router navigation events if available
71
- if (window.encatch?.router) {
72
- window.encatch.router.subscribe((location: { pathname: string }) => {
73
- trackLocationChange();
74
- });
75
- }
76
-
77
- return () => {
78
- observer.disconnect();
79
- window.removeEventListener("popstate", trackLocationChange);
80
- // Restore original pushState
81
- window.history.pushState = originalPushState;
82
- };
83
- }, []);
84
-
85
- return {
86
- trackLocationChange,
87
- };
88
- };
@@ -1,123 +0,0 @@
1
- // import { QuestionnaireFields } from "src/element/types";
2
- import { QuestionnaireFieldsResponse } from "@encatch/schema";
3
- import { PrepopulatedAnswer } from "../feedback-api-types";
4
- import { updateQuestionResponse } from "../store/formResponses";
5
-
6
- // Helper function to extract the actual value from AnswerFormat
7
- const extractValueFromAnswerFormat = (value: any, questionId: string): any => {
8
- // If value is not an object, return it as-is (primitive value)
9
- if (typeof value !== "object" || value === null) {
10
- return value;
11
- }
12
-
13
- // Define the mapping of answer format properties to their values
14
- const answerFormatMap = {
15
- nps: value.nps,
16
- rating: value.rating,
17
- long_text: value.long_text,
18
- short_answer: value.short_answer,
19
- single_choice: value.single_choice,
20
- multiple_choice_multiple: value.multiple_choice_multiple,
21
- nested_selection: value.nested_selection,
22
- annotation: value.annotation,
23
- };
24
-
25
- // Find the first property that exists in the value object
26
- for (const [property, propertyValue] of Object.entries(answerFormatMap)) {
27
- if (property in value) {
28
- return propertyValue;
29
- }
30
- }
31
-
32
- // If no known property is found, log a warning and return null
33
- console.warn(`Unknown answer format for questionId ${questionId}:`, value);
34
- return null;
35
- };
36
-
37
- // Function to process prepopulated answers and update the form responses store
38
- const processPrepopulatedAnswers = (
39
- prepopulatedAnswers: PrepopulatedAnswer[],
40
- questionnaireFields: QuestionnaireFieldsResponse
41
- ) => {
42
- try {
43
- const { sections, questions } = questionnaireFields;
44
-
45
- if (!sections || !questions) {
46
- console.warn(
47
- "Invalid questionnaire fields structure for prepopulated answers"
48
- );
49
- return;
50
- }
51
-
52
- prepopulatedAnswers.forEach((answer) => {
53
- try {
54
- const { sectionIndex, questionIndex, value } = answer;
55
-
56
- // Validate indices
57
- if (sectionIndex < 0 || sectionIndex >= sections.length) {
58
- console.warn(
59
- `Invalid sectionIndex ${sectionIndex} for prepopulated answer`
60
- );
61
- return;
62
- }
63
-
64
- const section = sections[sectionIndex];
65
- if (
66
- !section?.questionIds ||
67
- questionIndex < 0 ||
68
- questionIndex >= section.questionIds.length
69
- ) {
70
- console.warn(
71
- `Invalid questionIndex ${questionIndex} for section ${sectionIndex}`
72
- );
73
- return;
74
- }
75
-
76
- // Get the question ID
77
- const questionId = section.questionIds[questionIndex];
78
- if (!questionId) {
79
- console.warn(
80
- `No questionId found at section ${sectionIndex}, question ${questionIndex}`
81
- );
82
- return;
83
- }
84
-
85
- // Get the question details to determine the type
86
- const question = questions[questionId];
87
- if (!question) {
88
- console.warn(
89
- `Question details not found for questionId ${questionId}`
90
- );
91
- return;
92
- }
93
-
94
- // Update the form response using the store's helper function
95
- // The value is already in AnswerFormat, so we extract the actual value
96
- const questionType = question.type;
97
- const actualValue = extractValueFromAnswerFormat(value, questionId);
98
-
99
- if (actualValue === null) {
100
- return; // Skip if unknown format
101
- }
102
-
103
- // Update the question response
104
- updateQuestionResponse(questionId, actualValue, questionType);
105
- } catch (error) {
106
- console.error(
107
- "Error processing individual prepopulated answer:",
108
- error,
109
- answer
110
- );
111
- }
112
- });
113
- } catch (error) {
114
- console.error("Error processing prepopulated answers:", error);
115
- }
116
- };
117
-
118
- export const usePrepopulatedAnswers = () => {
119
- return {
120
- processPrepopulatedAnswers,
121
- extractValueFromAnswerFormat,
122
- };
123
- };