@constructor-io/constructorio-ui-quizzes 1.2.3 → 1.3.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.
- package/README.md +1 -1
- package/dist/constructorio-ui-quizzes-bundled.js +17 -17
- package/lib/cjs/components/CioQuiz/actions.js +11 -1
- package/lib/cjs/components/CioQuiz/index.js +15 -103
- package/lib/cjs/components/CioQuiz/quizApiReducer.js +32 -0
- package/lib/cjs/components/CioQuiz/{reducer.js → quizLocalReducer.js} +2 -2
- package/lib/cjs/components/CoverTypeQuestion/CoverTypeQuestion.js +6 -12
- package/lib/cjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +12 -18
- package/lib/cjs/components/QuizQuestions/index.js +6 -3
- package/lib/cjs/components/ResultCard/ResultCard.js +10 -30
- package/lib/cjs/components/ResultContainer/ResultContainer.js +8 -31
- package/lib/cjs/components/ResultCtaButton/ResultCtaButton.js +7 -26
- package/lib/cjs/components/ResultFilters/ResultFilters.js +6 -23
- package/lib/cjs/components/Results/Results.js +5 -5
- package/lib/cjs/components/SelectTypeQuestion/SelectTypeQuestion.js +16 -23
- package/lib/cjs/components/ZeroResults/ZeroResults.js +2 -2
- package/lib/cjs/constants.js +3 -2
- package/lib/cjs/hooks/useCioClient.js +4 -3
- package/lib/cjs/hooks/useConsoleErrors.js +20 -0
- package/lib/cjs/hooks/useQuiz.js +48 -0
- package/lib/cjs/hooks/useQuizApiState.js +91 -0
- package/lib/cjs/hooks/useQuizEvents/index.js +36 -0
- package/lib/cjs/hooks/useQuizEvents/useQuizAddToCart.js +20 -0
- package/lib/cjs/hooks/useQuizEvents/useQuizBackClick.js +13 -0
- package/lib/cjs/hooks/useQuizEvents/useQuizNextClick.js +48 -0
- package/lib/cjs/hooks/useQuizEvents/useQuizResultClick.js +19 -0
- package/lib/cjs/hooks/useQuizEvents/useQuizResultsLoaded.js +22 -0
- package/lib/cjs/hooks/useQuizLocalState.js +20 -0
- package/lib/cjs/services/index.js +72 -0
- package/lib/cjs/utils.js +42 -21
- package/lib/mjs/components/CioQuiz/actions.js +10 -0
- package/lib/mjs/components/CioQuiz/index.js +15 -103
- package/lib/mjs/components/CioQuiz/quizApiReducer.js +49 -0
- package/lib/mjs/components/CioQuiz/{reducer.js → quizLocalReducer.js} +1 -1
- package/lib/mjs/components/CoverTypeQuestion/CoverTypeQuestion.js +6 -12
- package/lib/mjs/components/OpenTextTypeQuestion/OpenTextTypeQuestion.js +11 -17
- package/lib/mjs/components/QuizQuestions/index.js +5 -3
- package/lib/mjs/components/ResultCard/ResultCard.js +10 -29
- package/lib/mjs/components/ResultContainer/ResultContainer.js +8 -31
- package/lib/mjs/components/ResultCtaButton/ResultCtaButton.js +7 -25
- package/lib/mjs/components/ResultFilters/ResultFilters.js +5 -23
- package/lib/mjs/components/Results/Results.js +3 -3
- package/lib/mjs/components/SelectTypeQuestion/SelectTypeQuestion.js +13 -20
- package/lib/mjs/components/ZeroResults/ZeroResults.js +2 -2
- package/lib/mjs/constants.js +3 -2
- package/lib/mjs/hooks/useCioClient.js +4 -3
- package/lib/mjs/hooks/useConsoleErrors.js +18 -0
- package/lib/mjs/hooks/useQuiz.js +47 -0
- package/lib/mjs/hooks/useQuizApiState.js +87 -0
- package/lib/mjs/hooks/useQuizEvents/index.js +33 -0
- package/lib/mjs/hooks/useQuizEvents/useQuizAddToCart.js +18 -0
- package/lib/mjs/hooks/useQuizEvents/useQuizBackClick.js +11 -0
- package/lib/mjs/hooks/useQuizEvents/useQuizNextClick.js +45 -0
- package/lib/mjs/hooks/useQuizEvents/useQuizResultClick.js +17 -0
- package/lib/mjs/hooks/useQuizEvents/useQuizResultsLoaded.js +19 -0
- package/lib/mjs/hooks/useQuizLocalState.js +17 -0
- package/lib/mjs/services/index.js +60 -0
- package/lib/mjs/utils.js +39 -17
- package/lib/types/components/CioQuiz/actions.d.ts +20 -0
- package/lib/types/components/CioQuiz/context.d.ts +10 -14
- package/lib/types/components/CioQuiz/index.d.ts +1 -12
- package/lib/types/components/CioQuiz/quizApiReducer.d.ts +14 -0
- package/lib/types/components/CioQuiz/{reducer.d.ts → quizLocalReducer.d.ts} +3 -3
- package/lib/types/components/QuizQuestions/index.d.ts +1 -4
- package/lib/types/components/ResultCard/ResultCard.d.ts +2 -4
- package/lib/types/components/ResultContainer/ResultContainer.d.ts +1 -2
- package/lib/types/components/ResultCtaButton/ResultCtaButton.d.ts +2 -3
- package/lib/types/components/ResultFilters/ResultFilters.d.ts +2 -5
- package/lib/types/components/Results/Results.d.ts +1 -7
- package/lib/types/components/ZeroResults/ZeroResults.d.ts +1 -1
- package/lib/types/constants.d.ts +1 -1
- package/lib/types/hooks/useCioClient.d.ts +1 -1
- package/lib/types/hooks/useConsoleErrors.d.ts +3 -0
- package/lib/types/hooks/useQuiz.d.ts +3 -0
- package/lib/types/hooks/useQuizApiState.d.ts +10 -0
- package/lib/types/hooks/useQuizEvents/index.d.ts +15 -0
- package/lib/types/hooks/useQuizEvents/useQuizAddToCart.d.ts +5 -0
- package/lib/types/hooks/useQuizEvents/useQuizBackClick.d.ts +4 -0
- package/lib/types/hooks/useQuizEvents/useQuizNextClick.d.ts +5 -0
- package/lib/types/hooks/useQuizEvents/useQuizResultClick.d.ts +5 -0
- package/lib/types/hooks/useQuizEvents/useQuizResultsLoaded.d.ts +5 -0
- package/lib/types/hooks/useQuizLocalState.d.ts +6 -0
- package/lib/types/services/index.d.ts +8 -0
- package/lib/types/types.d.ts +63 -0
- package/lib/types/utils.d.ts +12 -7
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
const useConsoleErrors = (quizId, resultsPageOptions) => {
|
|
3
|
+
useEffect(() => {
|
|
4
|
+
if (!quizId) {
|
|
5
|
+
// eslint-disable-next-line no-console
|
|
6
|
+
console.error('quizId is a required field of type string');
|
|
7
|
+
}
|
|
8
|
+
if (!resultsPageOptions || Object.keys(resultsPageOptions).length === 0) {
|
|
9
|
+
// eslint-disable-next-line no-console
|
|
10
|
+
console.error('resultsPageOptions is a required field of type object');
|
|
11
|
+
}
|
|
12
|
+
if (resultsPageOptions && !resultsPageOptions?.onAddToCartClick) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.error('resultsPageOptions.onAddToCartClick is a required field of type function');
|
|
15
|
+
}
|
|
16
|
+
}, [quizId, resultsPageOptions, resultsPageOptions?.onAddToCartClick]);
|
|
17
|
+
};
|
|
18
|
+
export default useConsoleErrors;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import useCioClient from './useCioClient';
|
|
2
|
+
import useConsoleErrors from './useConsoleErrors';
|
|
3
|
+
import useQuizApiState from './useQuizApiState';
|
|
4
|
+
import useQuizEvents from './useQuizEvents';
|
|
5
|
+
import useQuizLocalState from './useQuizLocalState';
|
|
6
|
+
const useQuiz = ({ quizId, apiKey, cioJsClient, quizVersionId, resultsPageOptions }) => {
|
|
7
|
+
// Log console errors for required parameters quizId and resultsPageOptions
|
|
8
|
+
useConsoleErrors(quizId, resultsPageOptions);
|
|
9
|
+
// Quiz Local state
|
|
10
|
+
const { quizLocalState, resetQuizLocalState, dispatchLocalState } = useQuizLocalState();
|
|
11
|
+
// Quiz Cio Client
|
|
12
|
+
const cioClient = useCioClient({ apiKey, cioJsClient });
|
|
13
|
+
// Quiz API state
|
|
14
|
+
const { isFirstQuestion, quizApiState, resetQuizApiState } = useQuizApiState(quizId, quizLocalState, resultsPageOptions, quizVersionId, cioClient);
|
|
15
|
+
// Quiz callback events
|
|
16
|
+
const quizEvents = useQuizEvents({
|
|
17
|
+
cioClient,
|
|
18
|
+
quizApiState,
|
|
19
|
+
resultsPageOptions,
|
|
20
|
+
dispatchLocalState,
|
|
21
|
+
resetQuizApiState,
|
|
22
|
+
resetQuizLocalState,
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
cioClient,
|
|
26
|
+
state: {
|
|
27
|
+
answers: {
|
|
28
|
+
inputs: quizLocalState.answerInputs,
|
|
29
|
+
isLastAnswer: quizLocalState.isLastAnswer,
|
|
30
|
+
},
|
|
31
|
+
quiz: {
|
|
32
|
+
requestState: quizApiState.quizRequestState,
|
|
33
|
+
versionId: quizApiState.quizVersionId,
|
|
34
|
+
sessionId: quizApiState.quizSessionId,
|
|
35
|
+
firstQuestion: quizApiState.quizFirstQuestion,
|
|
36
|
+
currentQuestion: quizApiState.quizCurrentQuestion,
|
|
37
|
+
results: quizApiState.quizResults,
|
|
38
|
+
resultsFilters: quizApiState.quizResultsFilters,
|
|
39
|
+
isFirstQuestion,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
events: {
|
|
43
|
+
...quizEvents,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export default useQuiz;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useEffect, useReducer } from 'react';
|
|
2
|
+
import { QuizAPIActionTypes } from '../components/CioQuiz/actions';
|
|
3
|
+
import apiReducer, { initialState } from '../components/CioQuiz/quizApiReducer';
|
|
4
|
+
import { nextQuestion, getQuizResults } from '../services';
|
|
5
|
+
const useFetchQuiz = (quizId, quizLocalState, resultsPageOptions, quizVersionIdProp, cioClient) => {
|
|
6
|
+
const [quizApiState, dispatch] = useReducer(apiReducer, initialState);
|
|
7
|
+
const firstQuestionId = quizApiState.quizFirstQuestion?.next_question.id;
|
|
8
|
+
const currentQuestionId = quizApiState.quizCurrentQuestion?.next_question.id;
|
|
9
|
+
const isFirstQuestion = firstQuestionId === currentQuestionId;
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
(async () => {
|
|
12
|
+
dispatch({
|
|
13
|
+
type: QuizAPIActionTypes.SET_IS_LOADING,
|
|
14
|
+
});
|
|
15
|
+
if (quizLocalState.isLastAnswer) {
|
|
16
|
+
try {
|
|
17
|
+
const quizResults = await getQuizResults(cioClient, quizId, {
|
|
18
|
+
answers: quizLocalState.answers,
|
|
19
|
+
resultsPerPage: resultsPageOptions?.numResultsToDisplay,
|
|
20
|
+
quizVersionId: quizApiState.quizVersionId,
|
|
21
|
+
quizSessionId: quizApiState.quizSessionId,
|
|
22
|
+
});
|
|
23
|
+
// Set quiz results state
|
|
24
|
+
dispatch({
|
|
25
|
+
type: QuizAPIActionTypes.SET_QUIZ_RESULTS,
|
|
26
|
+
payload: {
|
|
27
|
+
quizResults,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
dispatch({
|
|
33
|
+
type: QuizAPIActionTypes.SET_IS_ERROR,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
try {
|
|
39
|
+
let quizVersionId = quizApiState.quizVersionId || quizVersionIdProp;
|
|
40
|
+
let { quizSessionId } = quizApiState;
|
|
41
|
+
const questionResult = await nextQuestion(cioClient, quizId, {
|
|
42
|
+
answers: quizLocalState.answers,
|
|
43
|
+
quizVersionId,
|
|
44
|
+
quizSessionId,
|
|
45
|
+
});
|
|
46
|
+
if (!quizVersionId && questionResult?.quiz_version_id) {
|
|
47
|
+
quizVersionId = questionResult.quiz_version_id;
|
|
48
|
+
}
|
|
49
|
+
if (!quizSessionId && questionResult?.quiz_session_id) {
|
|
50
|
+
quizSessionId = questionResult.quiz_session_id;
|
|
51
|
+
}
|
|
52
|
+
// Set current question state
|
|
53
|
+
dispatch({
|
|
54
|
+
type: QuizAPIActionTypes.SET_CURRENT_QUESTION,
|
|
55
|
+
payload: {
|
|
56
|
+
quizSessionId,
|
|
57
|
+
quizVersionId,
|
|
58
|
+
quizCurrentQuestion: questionResult,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
dispatch({
|
|
64
|
+
type: QuizAPIActionTypes.SET_IS_ERROR,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
+
}, [
|
|
71
|
+
cioClient,
|
|
72
|
+
quizId,
|
|
73
|
+
quizLocalState,
|
|
74
|
+
quizLocalState.isLastAnswer,
|
|
75
|
+
resultsPageOptions?.numResultsToDisplay,
|
|
76
|
+
]);
|
|
77
|
+
const resetQuizApiState = () => {
|
|
78
|
+
dispatch({ type: QuizAPIActionTypes.RESET_QUIZ });
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
cioClient,
|
|
82
|
+
quizApiState,
|
|
83
|
+
isFirstQuestion,
|
|
84
|
+
resetQuizApiState,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
export default useFetchQuiz;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import useQuizResultsLoaded from './useQuizResultsLoaded';
|
|
2
|
+
import useQuizResultClick from './useQuizResultClick';
|
|
3
|
+
import useQuizAddToCart from './useQuizAddToCart';
|
|
4
|
+
import useQuizNextClick from './useQuizNextClick';
|
|
5
|
+
import useQuizBackClick from './useQuizBackClick';
|
|
6
|
+
const useQuizEvents = (options) => {
|
|
7
|
+
const { cioClient, quizApiState, resultsPageOptions, dispatchLocalState, resetQuizApiState, resetQuizLocalState, } = options;
|
|
8
|
+
const { onAddToCartClick, onQuizResultClick, onQuizResultsLoaded } = resultsPageOptions;
|
|
9
|
+
// Quiz Next button click
|
|
10
|
+
const nextQuestion = useQuizNextClick(quizApiState, dispatchLocalState);
|
|
11
|
+
// Quiz Back button click callback
|
|
12
|
+
const previousQuestion = useQuizBackClick(dispatchLocalState);
|
|
13
|
+
// Quiz result add to cart callback
|
|
14
|
+
const addToCart = useQuizAddToCart(cioClient, quizApiState, onAddToCartClick);
|
|
15
|
+
// Quiz result click callback
|
|
16
|
+
const resultClick = useQuizResultClick(cioClient, quizApiState, onQuizResultClick);
|
|
17
|
+
// Quiz results loaded event
|
|
18
|
+
useQuizResultsLoaded(cioClient, quizApiState, onQuizResultsLoaded);
|
|
19
|
+
const resetQuiz = () => {
|
|
20
|
+
if (quizApiState.quizResults) {
|
|
21
|
+
resetQuizApiState();
|
|
22
|
+
resetQuizLocalState();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
addToCart,
|
|
27
|
+
resultClick,
|
|
28
|
+
nextQuestion,
|
|
29
|
+
previousQuestion,
|
|
30
|
+
resetQuiz,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export default useQuizEvents;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { trackQuizConversion } from '../../services';
|
|
3
|
+
import { isFunction } from '../../utils';
|
|
4
|
+
const useQuizAddToCart = (cioClient, quizApiState, onAddToCartClick) => {
|
|
5
|
+
const addToCartClickHandler = useCallback((e, result, price) => {
|
|
6
|
+
e.preventDefault();
|
|
7
|
+
if (quizApiState.quizResults) {
|
|
8
|
+
// Tracking call
|
|
9
|
+
trackQuizConversion(cioClient, quizApiState.quizResults, result, price);
|
|
10
|
+
// User custom callback function
|
|
11
|
+
if (isFunction(onAddToCartClick)) {
|
|
12
|
+
onAddToCartClick(result);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}, [quizApiState, cioClient, onAddToCartClick]);
|
|
16
|
+
return addToCartClickHandler;
|
|
17
|
+
};
|
|
18
|
+
export default useQuizAddToCart;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { QuestionTypes } from '../../components/CioQuiz/actions';
|
|
3
|
+
const useQuizBackClick = (dispatchLocalState) => {
|
|
4
|
+
const quizBackHandler = useCallback(() => {
|
|
5
|
+
if (dispatchLocalState) {
|
|
6
|
+
dispatchLocalState({ type: QuestionTypes.Back });
|
|
7
|
+
}
|
|
8
|
+
}, [dispatchLocalState]);
|
|
9
|
+
return quizBackHandler;
|
|
10
|
+
};
|
|
11
|
+
export default useQuizBackClick;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { QuestionTypes } from '../../components/CioQuiz/actions';
|
|
3
|
+
const useQuizNextClick = (quizApiState, dispatchLocalState) => {
|
|
4
|
+
const quizNextHandler = useCallback((payload) => {
|
|
5
|
+
const questionType = quizApiState.quizCurrentQuestion?.next_question.type;
|
|
6
|
+
const currentQuestion = quizApiState.quizCurrentQuestion;
|
|
7
|
+
switch (questionType) {
|
|
8
|
+
case QuestionTypes.Cover:
|
|
9
|
+
dispatchLocalState({
|
|
10
|
+
type: QuestionTypes.Cover,
|
|
11
|
+
payload: {
|
|
12
|
+
isLastQuestion: currentQuestion.is_last_question,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
break;
|
|
16
|
+
case QuestionTypes.OpenText:
|
|
17
|
+
dispatchLocalState({
|
|
18
|
+
type: QuestionTypes.OpenText,
|
|
19
|
+
payload: {
|
|
20
|
+
questionId: currentQuestion.next_question.id,
|
|
21
|
+
input: payload,
|
|
22
|
+
isLastQuestion: currentQuestion.is_last_question,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
break;
|
|
26
|
+
case QuestionTypes.SingleSelect:
|
|
27
|
+
case QuestionTypes.MultipleSelect:
|
|
28
|
+
dispatchLocalState({
|
|
29
|
+
type: currentQuestion.next_question.type === QuestionTypes.SingleSelect
|
|
30
|
+
? QuestionTypes.SingleSelect
|
|
31
|
+
: QuestionTypes.MultipleSelect,
|
|
32
|
+
payload: {
|
|
33
|
+
questionId: currentQuestion.next_question.id,
|
|
34
|
+
input: payload,
|
|
35
|
+
isLastQuestion: currentQuestion.is_last_question,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}, [quizApiState, dispatchLocalState]);
|
|
43
|
+
return quizNextHandler;
|
|
44
|
+
};
|
|
45
|
+
export default useQuizNextClick;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { trackQuizResultClick } from '../../services';
|
|
3
|
+
import { isFunction } from '../../utils';
|
|
4
|
+
const useQuizResultClick = (cioClient, quizApiState, onQuizResultClick) => {
|
|
5
|
+
const resultClickHandler = useCallback((result, position) => {
|
|
6
|
+
if (quizApiState.quizResults) {
|
|
7
|
+
// Tracking call
|
|
8
|
+
trackQuizResultClick(cioClient, quizApiState.quizResults, result, position);
|
|
9
|
+
// User custom callback function
|
|
10
|
+
if (isFunction(onQuizResultClick)) {
|
|
11
|
+
onQuizResultClick(result, position);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}, [quizApiState, cioClient, onQuizResultClick]);
|
|
15
|
+
return resultClickHandler;
|
|
16
|
+
};
|
|
17
|
+
export default useQuizResultClick;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { trackQuizResultsLoaded } from '../../services';
|
|
3
|
+
import { isFunction } from '../../utils';
|
|
4
|
+
const useQuizResultsLoaded = (cioClient, quizApiState, onQuizResultsLoaded) => {
|
|
5
|
+
// Quiz results loaded
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (quizApiState.quizResults) {
|
|
8
|
+
// Tracking call
|
|
9
|
+
trackQuizResultsLoaded(cioClient, quizApiState.quizResults);
|
|
10
|
+
// User custom callback function
|
|
11
|
+
if (onQuizResultsLoaded &&
|
|
12
|
+
isFunction(onQuizResultsLoaded) &&
|
|
13
|
+
quizApiState.quizResults.response?.results) {
|
|
14
|
+
onQuizResultsLoaded(quizApiState.quizResults.response.results);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}, [quizApiState, cioClient, onQuizResultsLoaded]);
|
|
18
|
+
};
|
|
19
|
+
export default useQuizResultsLoaded;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useReducer } from 'react';
|
|
2
|
+
import { QuestionTypes } from '../components/CioQuiz/actions';
|
|
3
|
+
import quizLocalReducer, { initialState } from '../components/CioQuiz/quizLocalReducer';
|
|
4
|
+
const useQuizLocalState = () => {
|
|
5
|
+
const [quizLocalState, dispatch] = useReducer(quizLocalReducer, initialState);
|
|
6
|
+
const resetQuizLocalState = () => {
|
|
7
|
+
dispatch({
|
|
8
|
+
type: QuestionTypes.Reset,
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
return {
|
|
12
|
+
quizLocalState,
|
|
13
|
+
resetQuizLocalState,
|
|
14
|
+
dispatchLocalState: dispatch,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export default useQuizLocalState;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
3
|
+
export const getCioClient = (apiKey) => {
|
|
4
|
+
if (apiKey) {
|
|
5
|
+
return new ConstructorIOClient({
|
|
6
|
+
apiKey,
|
|
7
|
+
sendTrackingEvents: true,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
};
|
|
12
|
+
export const nextQuestion = (cioClient, quizId, parameters) => cioClient?.quizzes.getQuizNextQuestion(quizId, parameters);
|
|
13
|
+
export const getQuizResults = async (cioClient, quizId, parameters) => cioClient?.quizzes.getQuizResults(quizId, parameters);
|
|
14
|
+
// Tracking requests
|
|
15
|
+
export const trackQuizResultsLoaded = (cioClient, quizResults) => {
|
|
16
|
+
const { quiz_id, quiz_session_id, quiz_version_id, result_id, request, response } = quizResults;
|
|
17
|
+
cioClient?.tracker.trackQuizResultsLoaded({
|
|
18
|
+
quiz_id,
|
|
19
|
+
quiz_version_id,
|
|
20
|
+
quiz_session_id,
|
|
21
|
+
url: window.location.href,
|
|
22
|
+
section: request?.section,
|
|
23
|
+
result_count: response?.total_num_results,
|
|
24
|
+
result_page: request?.page,
|
|
25
|
+
result_id,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
export const trackQuizResultClick = (cioClient, quizResults, result, position) => {
|
|
29
|
+
if (quizResults.request && quizResults.response) {
|
|
30
|
+
const { quiz_id, quiz_session_id, quiz_version_id, result_id, request: { section, page, num_results_per_page }, response: { total_num_results }, } = quizResults;
|
|
31
|
+
cioClient?.tracker.trackQuizResultClick({
|
|
32
|
+
quiz_id,
|
|
33
|
+
quiz_version_id,
|
|
34
|
+
quiz_session_id,
|
|
35
|
+
item_id: result.data?.id,
|
|
36
|
+
item_name: result?.value,
|
|
37
|
+
section,
|
|
38
|
+
result_count: total_num_results,
|
|
39
|
+
result_page: page,
|
|
40
|
+
result_id,
|
|
41
|
+
result_position_on_page: position,
|
|
42
|
+
num_results_per_page,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
export const trackQuizConversion = (cioClient, quizResults, result, price) => {
|
|
47
|
+
if (quizResults.request) {
|
|
48
|
+
const { quiz_id, quiz_session_id, quiz_version_id, request: { section }, } = quizResults;
|
|
49
|
+
cioClient?.tracker.trackQuizConversion({
|
|
50
|
+
quiz_id,
|
|
51
|
+
quiz_version_id,
|
|
52
|
+
quiz_session_id,
|
|
53
|
+
item_id: result.data?.id,
|
|
54
|
+
item_name: result.value,
|
|
55
|
+
section,
|
|
56
|
+
variation_id: result.data?.variation_id,
|
|
57
|
+
revenue: (price && String(price)) || undefined,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
package/lib/mjs/utils.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
3
2
|
import { QuestionTypes } from './components/CioQuiz/actions';
|
|
4
3
|
export const renderImages = (images, cssClasses) => {
|
|
5
4
|
const { primary_url: primaryUrl, primary_alt: primaryAlt, secondary_url: secondaryUrl, secondary_alt: secondaryAlt, } = images;
|
|
@@ -36,21 +35,32 @@ ${templateCode}
|
|
|
36
35
|
},
|
|
37
36
|
};
|
|
38
37
|
};
|
|
39
|
-
export const
|
|
38
|
+
export const defaultOnAddToCartClickCode = `"onAddToCartClick": (item) => console.dir(item)`;
|
|
39
|
+
export const defaultOnQuizResultClickCode = `"onQuizResultClick": (result, position) => console.dir(result, position)`;
|
|
40
|
+
export const defaultOnQuizResultsLoadedCode = `"onQuizResultsLoaded": (results) => console.dir(results)`;
|
|
40
41
|
export const stringifyWithDefaults = (obj) => {
|
|
41
|
-
const {
|
|
42
|
-
|
|
42
|
+
const { resultsPageOptions, cioJsClient, ...rest } = obj;
|
|
43
|
+
const { onAddToCartClick, onQuizResultsLoaded, onQuizResultClick } = resultsPageOptions;
|
|
44
|
+
let res = JSON.stringify({ ...rest, resultsPageOptions }, null, ' ');
|
|
43
45
|
if (cioJsClient) {
|
|
44
46
|
res = res.replace('"resultsPageOptions": {', `"cioJsClient": cioJsClient,
|
|
45
47
|
"resultsPageOptions": {`);
|
|
46
48
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
if (onQuizResultsLoaded) {
|
|
50
|
+
res = res.replace('"resultsPageOptions": {', `"resultsPageOptions": {
|
|
51
|
+
${defaultOnQuizResultsLoadedCode},`);
|
|
52
|
+
}
|
|
53
|
+
if (onQuizResultClick) {
|
|
54
|
+
res = res.replace('"resultsPageOptions": {', `"resultsPageOptions": {
|
|
55
|
+
${defaultOnQuizResultClickCode},`);
|
|
56
|
+
}
|
|
57
|
+
if (onAddToCartClick) {
|
|
58
|
+
res = res.replace('"resultsPageOptions": {', `"resultsPageOptions": {
|
|
59
|
+
${defaultOnAddToCartClickCode},`);
|
|
60
|
+
}
|
|
49
61
|
return res;
|
|
50
62
|
};
|
|
51
63
|
export const stringify = (obj) => JSON.stringify(obj, null, ' ');
|
|
52
|
-
export const getNextQuestion = (cioClient, quizId, parameters) => cioClient?.quizzes.getQuizNextQuestion(quizId, parameters);
|
|
53
|
-
export const getQuizResults = async (cioClient, quizId, parameters) => cioClient?.quizzes.getQuizResults(quizId, parameters);
|
|
54
64
|
export const getQuestionTypes = (questionType) => {
|
|
55
65
|
const isOpenQuestion = questionType === QuestionTypes.OpenText;
|
|
56
66
|
const isCoverQuestion = questionType === QuestionTypes.Cover;
|
|
@@ -65,15 +75,6 @@ export const getQuestionTypes = (questionType) => {
|
|
|
65
75
|
isSelectQuestion,
|
|
66
76
|
};
|
|
67
77
|
};
|
|
68
|
-
export const getCioClient = (apiKey) => {
|
|
69
|
-
if (apiKey) {
|
|
70
|
-
return new ConstructorIOClient({
|
|
71
|
-
apiKey,
|
|
72
|
-
sendTrackingEvents: true,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
return undefined;
|
|
76
|
-
};
|
|
77
78
|
export function getPreferredColorScheme() {
|
|
78
79
|
let colorScheme = 'light';
|
|
79
80
|
// Check if the dark-mode Media-Query matches
|
|
@@ -82,3 +83,24 @@ export function getPreferredColorScheme() {
|
|
|
82
83
|
}
|
|
83
84
|
return colorScheme;
|
|
84
85
|
}
|
|
86
|
+
export function isFunction(fn) {
|
|
87
|
+
return fn && typeof fn === 'function';
|
|
88
|
+
}
|
|
89
|
+
const isValueExpression = (exp) => 'name' in exp && 'value' in exp;
|
|
90
|
+
const isAndFilter = (exp) => 'and' in exp;
|
|
91
|
+
const isOrFilter = (exp) => 'or' in exp;
|
|
92
|
+
export const getFilterValuesFromExpression = (exp) => {
|
|
93
|
+
if (!exp) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
if (isAndFilter(exp)) {
|
|
97
|
+
return exp.and.flatMap((innerExpression) => getFilterValuesFromExpression(innerExpression));
|
|
98
|
+
}
|
|
99
|
+
if (isOrFilter(exp)) {
|
|
100
|
+
return exp.or.flatMap((innerExpression) => getFilterValuesFromExpression(innerExpression));
|
|
101
|
+
}
|
|
102
|
+
if (isValueExpression(exp)) {
|
|
103
|
+
return [exp.value];
|
|
104
|
+
}
|
|
105
|
+
return [];
|
|
106
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NextQuestionResponse, QuizResultsResponse } from '../../types';
|
|
1
2
|
export declare enum QuestionTypes {
|
|
2
3
|
OpenText = "open",
|
|
3
4
|
Cover = "cover",
|
|
@@ -22,4 +23,23 @@ interface Action<Type, Payload = {}> {
|
|
|
22
23
|
}
|
|
23
24
|
export type ActionAnswerInputQuestion = Action<QuestionTypes.OpenText, OpenTextQuestionPayload> | Action<QuestionTypes.SingleSelect, SelectQuestionPayload> | Action<QuestionTypes.MultipleSelect, SelectQuestionPayload>;
|
|
24
25
|
export type ActionAnswerQuestion = ActionAnswerInputQuestion | Action<QuestionTypes.Cover, CoverQuestionPayload> | Action<QuestionTypes.Back> | Action<QuestionTypes.Reset>;
|
|
26
|
+
export declare enum QuizAPIActionTypes {
|
|
27
|
+
SET_IS_LOADING = 0,
|
|
28
|
+
SET_IS_ERROR = 1,
|
|
29
|
+
SET_QUIZ_RESULTS = 2,
|
|
30
|
+
SET_CURRENT_QUESTION = 3,
|
|
31
|
+
RESET_QUIZ = 4
|
|
32
|
+
}
|
|
33
|
+
export type ActionSetIsLoading = Action<QuizAPIActionTypes.SET_IS_LOADING>;
|
|
34
|
+
export type ActionSetIsError = Action<QuizAPIActionTypes.SET_IS_ERROR>;
|
|
35
|
+
export type ActionSetQuizResults = Action<QuizAPIActionTypes.SET_QUIZ_RESULTS, {
|
|
36
|
+
quizResults: QuizResultsResponse;
|
|
37
|
+
}>;
|
|
38
|
+
export type ActionSetCurrentQuestion = Action<QuizAPIActionTypes.SET_CURRENT_QUESTION, {
|
|
39
|
+
quizCurrentQuestion: NextQuestionResponse;
|
|
40
|
+
quizSessionId?: string;
|
|
41
|
+
quizVersionId?: string;
|
|
42
|
+
}>;
|
|
43
|
+
export type ActionResetQuiz = Action<QuizAPIActionTypes.RESET_QUIZ>;
|
|
44
|
+
export type ActionQuizAPI = ActionSetIsLoading | ActionSetIsError | ActionSetQuizResults | ActionSetCurrentQuestion | ActionResetQuiz;
|
|
25
45
|
export {};
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
quizNextHandler: (action?: ActionAnswerQuestion) => void;
|
|
14
|
-
quizBackHandler: () => void;
|
|
15
|
-
dispatch: React.Dispatch<ActionAnswerQuestion>;
|
|
16
|
-
cioClient: ConstructorIOClient;
|
|
3
|
+
import { QuizEventsReturn, QuizReturnState } from '../../types';
|
|
4
|
+
export interface QuizContextValue {
|
|
5
|
+
cioClient?: ConstructorIOClient;
|
|
6
|
+
state?: QuizReturnState;
|
|
7
|
+
resetQuiz: QuizEventsReturn.ResetQuiz;
|
|
8
|
+
nextQuestion?: QuizEventsReturn.NextQuestion;
|
|
9
|
+
previousQuestion?: QuizEventsReturn.PreviousQuestion;
|
|
10
|
+
addToCart: QuizEventsReturn.AddToCart;
|
|
11
|
+
resultClick: QuizEventsReturn.ResultClick;
|
|
12
|
+
customClickItemCallback: boolean;
|
|
17
13
|
}
|
|
18
14
|
declare const _default: React.Context<Partial<QuizContextValue>>;
|
|
19
15
|
export default _default;
|
|
@@ -1,14 +1,3 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import
|
|
3
|
-
import { ResultsProps } from '../Results/Results';
|
|
4
|
-
export interface ResultsPageOptions extends ResultsProps {
|
|
5
|
-
numResultsToDisplay?: number;
|
|
6
|
-
}
|
|
7
|
-
export interface IQuizProps {
|
|
8
|
-
quizId: string;
|
|
9
|
-
apiKey?: string;
|
|
10
|
-
cioJsClient?: ConstructorIOClient;
|
|
11
|
-
resultsPageOptions: ResultsPageOptions;
|
|
12
|
-
quizVersionId?: string;
|
|
13
|
-
}
|
|
2
|
+
import { IQuizProps } from '../../types';
|
|
14
3
|
export default function CioQuiz(props: IQuizProps): JSX.Element | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RequestStates } from '../../constants';
|
|
2
|
+
import { NextQuestionResponse, QuizResultsResponse } from '../../types';
|
|
3
|
+
import { ActionQuizAPI } from './actions';
|
|
4
|
+
export type QuizAPIReducerState = {
|
|
5
|
+
quizRequestState: RequestStates;
|
|
6
|
+
quizVersionId?: string;
|
|
7
|
+
quizSessionId?: string;
|
|
8
|
+
quizFirstQuestion?: NextQuestionResponse;
|
|
9
|
+
quizCurrentQuestion?: NextQuestionResponse;
|
|
10
|
+
quizResults?: QuizResultsResponse;
|
|
11
|
+
quizResultsFilters?: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare const initialState: QuizAPIReducerState;
|
|
14
|
+
export default function apiReducer(state: QuizAPIReducerState, action: ActionQuizAPI): QuizAPIReducerState;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ActionAnswerQuestion, OpenTextQuestionPayload, SelectQuestionPayload } from './actions';
|
|
2
2
|
export type Answers = string[][];
|
|
3
|
-
export type
|
|
3
|
+
export type QuizLocalReducerState = {
|
|
4
4
|
answers: Answers;
|
|
5
5
|
answerInputs: {};
|
|
6
6
|
isLastAnswer: boolean;
|
|
@@ -8,5 +8,5 @@ export type QuizReducerState = {
|
|
|
8
8
|
export type AnswerInputState = {
|
|
9
9
|
[key: string]: OpenTextQuestionPayload | SelectQuestionPayload;
|
|
10
10
|
};
|
|
11
|
-
export declare const initialState:
|
|
12
|
-
export default function
|
|
11
|
+
export declare const initialState: QuizLocalReducerState;
|
|
12
|
+
export default function quizLocalReducer(state: QuizLocalReducerState, action: ActionAnswerQuestion): QuizLocalReducerState;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import {
|
|
2
|
+
import { QuizResultDataPartial } from '../../types';
|
|
3
3
|
interface ResultCardProps {
|
|
4
|
-
result:
|
|
5
|
-
addToCartCallback: (clickedResult: Partial<BrowseResultData>) => any;
|
|
6
|
-
clickItemCallback?: (clickedResult: Partial<BrowseResultData>) => any;
|
|
4
|
+
result: QuizResultDataPartial;
|
|
7
5
|
salePriceKey?: string;
|
|
8
6
|
regularPriceKey?: string;
|
|
9
7
|
resultPosition: number;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { ResultsProps } from '
|
|
2
|
+
import { ResultsProps } from '../../types';
|
|
3
3
|
export interface IResultContainerProps {
|
|
4
4
|
options: ResultsProps;
|
|
5
|
-
resetQuizSessionId: () => void;
|
|
6
5
|
}
|
|
7
6
|
export default function ResultContainer(props: IResultContainerProps): JSX.Element;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import {
|
|
2
|
+
import { QuizResultDataPartial } from '../../types';
|
|
3
3
|
interface ResultCtaButtonProps {
|
|
4
|
-
item:
|
|
4
|
+
item: QuizResultDataPartial;
|
|
5
5
|
className?: string;
|
|
6
|
-
callback?: (item: Partial<BrowseResultData>) => any;
|
|
7
6
|
price?: number;
|
|
8
7
|
}
|
|
9
8
|
export default function ResultCtaButton(props: ResultCtaButtonProps): JSX.Element;
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
filters?: any;
|
|
4
|
-
}
|
|
5
|
-
declare function ResultFilters(props: ResultFiltersProps): JSX.Element;
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
declare function ResultFilters(): JSX.Element;
|
|
6
3
|
export default ResultFilters;
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import {
|
|
3
|
-
export interface ResultsProps {
|
|
4
|
-
addToCartCallback: (item: Partial<BrowseResultData>) => any;
|
|
5
|
-
clickItemCallback?: (item: Partial<BrowseResultData>) => any;
|
|
6
|
-
resultCardSalePriceKey?: string;
|
|
7
|
-
resultCardRegularPriceKey?: string;
|
|
8
|
-
}
|
|
2
|
+
import { ResultsProps } from '../../types';
|
|
9
3
|
declare function Results(props: ResultsProps): JSX.Element;
|
|
10
4
|
export default Results;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
interface ZeroResultsProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
3
|
-
|
|
3
|
+
resetQuizClickHandler?: () => {};
|
|
4
4
|
}
|
|
5
5
|
declare function ZeroResults(props: ZeroResultsProps): JSX.Element;
|
|
6
6
|
export default ZeroResults;
|