@smartex.com/modules-utils 0.1.1 → 0.2.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/dist/base/index.d.ts +4 -4
- package/dist/survey/index.d.ts +15 -14
- package/dist/survey/index.js +4 -4
- package/dist/survey/index.js.map +1 -1
- package/package.json +13 -2
package/dist/base/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
type ModuleProps<T extends object = object> = {
|
|
5
5
|
token: string;
|
|
6
6
|
lang?: 'ru' | 'kz' | 'en';
|
|
7
7
|
theme?: string;
|
|
@@ -12,11 +12,11 @@ interface ModuleProps<T extends object = object> {
|
|
|
12
12
|
};
|
|
13
13
|
onSuccess?: () => void;
|
|
14
14
|
onError?: (errors: string[]) => void;
|
|
15
|
-
}
|
|
16
|
-
|
|
15
|
+
};
|
|
16
|
+
type ModuleController<T extends object = object> = {
|
|
17
17
|
mount: (targetNodeId: string, props: ModuleProps<T>) => void;
|
|
18
18
|
unmount: () => void;
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
|
|
21
21
|
declare const createModuleController: <E extends object = object>(Module: ComponentType<ModuleProps<E>>) => ModuleController<E>;
|
|
22
22
|
|
package/dist/survey/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ComponentType, ReactNode } from 'react';
|
|
|
2
2
|
import { SurveyResultEntry, CtxStoreWithSurvey } from '@smartex.com/ctx';
|
|
3
3
|
|
|
4
4
|
type Lang = 'ru' | 'kz' | 'en';
|
|
5
|
-
|
|
5
|
+
type SurveyAnswer = {
|
|
6
6
|
code: number;
|
|
7
7
|
text: {
|
|
8
8
|
ru: string | null;
|
|
@@ -24,8 +24,8 @@ interface SurveyAnswer {
|
|
|
24
24
|
isMandatory: number;
|
|
25
25
|
isCustomRouting: boolean;
|
|
26
26
|
isCustomInput: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
27
|
+
};
|
|
28
|
+
type SurveyQuestion = {
|
|
29
29
|
code: number;
|
|
30
30
|
text: {
|
|
31
31
|
ru: string | null;
|
|
@@ -48,44 +48,45 @@ interface SurveyQuestion {
|
|
|
48
48
|
isFirst: boolean;
|
|
49
49
|
isPrevStepDisabled: boolean;
|
|
50
50
|
isModule: boolean;
|
|
51
|
-
}
|
|
52
|
-
|
|
51
|
+
};
|
|
52
|
+
type SurveyMountProps<T extends object = object> = {
|
|
53
53
|
token: string;
|
|
54
54
|
lang?: Lang;
|
|
55
55
|
theme?: string;
|
|
56
|
-
externalData?: T;
|
|
57
56
|
question: SurveyQuestion;
|
|
58
57
|
surveyCode: number;
|
|
59
58
|
fusCode?: number;
|
|
60
59
|
visit?: number;
|
|
61
60
|
parentVisit?: number;
|
|
61
|
+
externalData?: T;
|
|
62
62
|
apiConfig: {
|
|
63
63
|
services: string;
|
|
64
64
|
externalApi: string;
|
|
65
65
|
};
|
|
66
66
|
routeToQuestion: (questionCode: number) => void;
|
|
67
|
-
onSurveyFinish: () => void;
|
|
68
67
|
onError: (errors: string[]) => void;
|
|
69
|
-
|
|
68
|
+
onComplete: () => void;
|
|
69
|
+
onExit: () => void;
|
|
70
|
+
};
|
|
70
71
|
type SurveyComponentProps = {
|
|
71
72
|
question: SurveyMountProps['question'];
|
|
72
73
|
getSettings: <T = unknown>(key?: string) => [T | null, string | null];
|
|
73
74
|
onSetAnswer: (answer: number | number[], value: SurveyResultEntry['result']) => void;
|
|
74
75
|
goNext: SurveyMountProps['routeToQuestion'];
|
|
75
76
|
goPrev: () => void;
|
|
76
|
-
|
|
77
|
+
proceed: () => void;
|
|
77
78
|
onError: (errors: string[]) => void;
|
|
78
|
-
|
|
79
|
+
onComplete: SurveyMountProps['onComplete'];
|
|
79
80
|
};
|
|
80
|
-
|
|
81
|
+
type SurveyModuleController<E extends object = object> = {
|
|
81
82
|
mount: (targetNodeId: string, props: SurveyMountProps<E>) => void;
|
|
82
83
|
unmount: () => void;
|
|
83
|
-
}
|
|
84
|
-
|
|
84
|
+
};
|
|
85
|
+
type SurveyModuleWrapperConfig = {
|
|
85
86
|
schema: Record<string, unknown>;
|
|
86
87
|
defaultLang: Lang;
|
|
87
88
|
logicScreenAliases: string[];
|
|
88
|
-
}
|
|
89
|
+
};
|
|
89
90
|
type SurveyCtxStore<S extends Record<string, unknown>> = CtxStoreWithSurvey<S>;
|
|
90
91
|
|
|
91
92
|
type SurveyLayout = ComponentType<{
|
package/dist/survey/index.js
CHANGED
|
@@ -64,7 +64,7 @@ var SurveyModuleWrapper = ({ children, config, ...props }) => {
|
|
|
64
64
|
const prevQuestionType = prevQuestionCode ? ctx.survey.getResult(prevQuestionCode)?.questionType : null;
|
|
65
65
|
if (prevQuestionCode === null) {
|
|
66
66
|
ctx.destroy();
|
|
67
|
-
props.
|
|
67
|
+
props.onExit();
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
ctx.survey.removeData(currentQuestionCode);
|
|
@@ -102,7 +102,7 @@ var SurveyModuleWrapper = ({ children, config, ...props }) => {
|
|
|
102
102
|
},
|
|
103
103
|
[]
|
|
104
104
|
);
|
|
105
|
-
const
|
|
105
|
+
const proceed = useCallback(() => {
|
|
106
106
|
const baseAnswers = props.question.answers.filter((answer) => !answer.isCustomRouting);
|
|
107
107
|
onSetAnswer(baseAnswers[0].code, null);
|
|
108
108
|
props.routeToQuestion(baseAnswers[0].nextQuestion);
|
|
@@ -128,11 +128,11 @@ var SurveyModuleWrapper = ({ children, config, ...props }) => {
|
|
|
128
128
|
return children({
|
|
129
129
|
getSettings,
|
|
130
130
|
onSetAnswer,
|
|
131
|
-
|
|
131
|
+
proceed,
|
|
132
132
|
onError,
|
|
133
133
|
goPrev,
|
|
134
134
|
goNext: props.routeToQuestion,
|
|
135
|
-
|
|
135
|
+
onComplete: props.onComplete,
|
|
136
136
|
question: props.question
|
|
137
137
|
});
|
|
138
138
|
};
|
package/dist/survey/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/survey/survey-module-controller-factory.tsx","../../src/survey/survey-module-wrapper.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { SurveyModuleWrapper } from './survey-module-wrapper';\nimport type {\n SurveyMountProps,\n SurveyComponentProps,\n SurveyModuleController,\n SurveyModuleWrapperConfig,\n} from './types.ts';\n\ntype SurveyLayout = ComponentType<{\n mountProps: SurveyMountProps;\n surveyProps: SurveyComponentProps;\n children: ReactNode;\n}>;\n\nexport const createSurveyModuleControllerFactory = <E extends object = object>(\n Layout: SurveyLayout,\n config: SurveyModuleWrapperConfig\n) => {\n return (Module: ComponentType<SurveyComponentProps>): SurveyModuleController<E> => {\n let root: ReturnType<typeof createRoot> | null = null;\n\n const mount = (targetNodeId: string, props: SurveyMountProps<E>) => {\n const el = document.getElementById(targetNodeId);\n\n if (!el) {\n throw new Error(`DOM элемент \"${targetNodeId}\" не найден.`);\n }\n\n root = createRoot(el);\n root.render(\n <SurveyModuleWrapper {...(props as SurveyMountProps)} config={config}>\n {(injectedProps: SurveyComponentProps) => (\n <Layout mountProps={props as SurveyMountProps} surveyProps={injectedProps}>\n <Module {...injectedProps} />\n </Layout>\n )}\n </SurveyModuleWrapper>\n );\n };\n\n const unmount = () => {\n if (root !== null) {\n root.unmount();\n root = null;\n }\n };\n\n return { mount, unmount };\n };\n};\n","import { useState, useMemo, useLayoutEffect, useCallback, type ReactNode } from 'react';\nimport dayjs from 'dayjs';\nimport { create, type CtxStoreWithSurvey, type SurveyResultEntry } from '@smartex.com/ctx';\nimport type { SurveyMountProps, SurveyComponentProps, SurveyModuleWrapperConfig } from './types.ts';\n\ntype AnyCtxStore = CtxStoreWithSurvey<Record<string, unknown>>;\n\n/**\n * Internal accessor for the global ctx store.\n * Avoids declaring `ctx` on `Window` in the library — that declaration\n * belongs to the consuming project, which knows the actual schema type.\n */\nconst getCtx = (): AnyCtxStore => {\n const store = (window as any).ctx as AnyCtxStore | undefined;\n\n if (!store) {\n throw new Error('ctx is not initialized.');\n }\n\n return store;\n};\n\nconst hasCtx = (): boolean => !!(window as any).ctx;\n\ntype SurveyWrapperProps = SurveyMountProps & {\n config: SurveyModuleWrapperConfig;\n children: (injectedProps: SurveyComponentProps) => ReactNode;\n};\n\nexport const SurveyModuleWrapper = ({ children, config, ...props }: SurveyWrapperProps) => {\n const [ctxReady, setCtxReady] = useState(false);\n const startTime = useMemo(() => dayjs().format('DD.MM.YYYY HH:mm:ss'), []);\n\n const initSurveyCtx = () => {\n create({\n name: 'ctx',\n schema: config.schema,\n survey: { code: props.surveyCode },\n });\n\n const ctx = getCtx();\n ctx.set('token', props.token);\n ctx.set('lang', props.lang ?? config.defaultLang);\n ctx.set('theme', props.theme ?? null);\n\n if (props.externalData) {\n for (const [key, value] of Object.entries(props.externalData)) {\n ctx.set(key, value);\n }\n }\n };\n\n const getPrevQuestionCode = () => {\n const history = getCtx().survey.getHistory();\n return history.length > 0 ? history[history.length - 1].code : null;\n };\n\n const getPrevQuestionData = () => {\n const prevQuestion = getPrevQuestionCode();\n return prevQuestion ? getCtx().survey.getData(prevQuestion) : {};\n };\n\n const getSettings = useCallback(<T = unknown,>(key?: string): [T | null, string | null] => {\n if (!props.question.settings) {\n return [null, null];\n }\n\n let settings: Record<string, unknown>;\n\n try {\n settings = JSON.parse(props.question.settings);\n } catch {\n return [null, 'Ошибка парсинга настроек вопроса'];\n }\n\n if (Object.keys(settings).length === 0) return [null, null];\n if (!key) return [settings as T, null];\n if (!Object.prototype.hasOwnProperty.call(settings, key)) {\n return [null, `В настройках вопроса нет параметра \"${key}\"`];\n }\n\n return [settings[key] as T, null];\n }, []);\n\n const routeToPrevQuestion = (questionCode?: number) => {\n const ctx = getCtx();\n const currentQuestionCode = questionCode ?? props.question.code;\n const prevQuestionCode = getPrevQuestionCode();\n const prevQuestionType = prevQuestionCode\n ? ctx.survey.getResult(prevQuestionCode)?.questionType\n : null;\n\n if (prevQuestionCode === null) {\n ctx.destroy();\n props.onSurveyFinish(); // TODO: РЕШИТЬ ЧТО ТУТ ДЕЛАТЬ !!!!!!!!!!!!!!!\n return;\n }\n\n ctx.survey.removeData(currentQuestionCode);\n ctx.survey.removeResult(currentQuestionCode);\n ctx.survey.undoHistory();\n\n if (config.logicScreenAliases.includes(prevQuestionType as string)) {\n routeToPrevQuestion(prevQuestionCode);\n return;\n }\n\n props.routeToQuestion(prevQuestionCode);\n };\n\n const goPrev = useCallback(() => routeToPrevQuestion(), []);\n\n const onSetAnswer = useCallback(\n (answer: number | number[], value: SurveyResultEntry['result']) => {\n const ctx = getCtx();\n let weight = 0;\n\n if (Array.isArray(answer)) {\n weight = props.question.answers\n .filter((item) => answer.includes(item.code))\n .reduce((sum, item) => sum + (item.weight ?? 0), 0);\n } else {\n weight = props.question.answers.find((item) => item.code === answer)?.weight ?? 0;\n }\n\n ctx.survey.setAnswerWeight(props.question.code, weight);\n ctx.survey.addHistory({\n code: props.question.code,\n answers: Array.isArray(answer) ? [...answer] : [answer],\n stime: startTime,\n etime: dayjs().format('DD.MM.YYYY HH:mm:ss'),\n });\n ctx.survey.addResult({\n questionCode: props.question.code,\n questionType: props.question.typeCode,\n answerCode: Array.isArray(answer) ? null : answer,\n result: value,\n });\n },\n []\n );\n\n const onSuccess = useCallback(() => {\n const baseAnswers = props.question.answers.filter((answer) => !answer.isCustomRouting);\n onSetAnswer(baseAnswers[0].code, null);\n props.routeToQuestion(baseAnswers[0].nextQuestion as number);\n }, []);\n\n const onError = useCallback((errors: string[]) => {\n // SEND LOGS via API\n props.onError(errors);\n }, []);\n\n useLayoutEffect(() => {\n if (props.question.isFirst) {\n if (hasCtx()) getCtx().destroy();\n initSurveyCtx();\n }\n\n if (!hasCtx()) {\n throw new Error('Отсутствует контекст для работы с модулями!');\n }\n\n const ctx = getCtx();\n // Results and data cleanup on module init\n ctx.survey.removeData(props.question.code);\n ctx.survey.removeResult(props.question.code);\n ctx.survey.addData(props.question.code, getPrevQuestionData(), true);\n setCtxReady(true); // eslint-disable-line react-hooks/set-state-in-effect\n }, []);\n\n if (!ctxReady) return null;\n\n return children({\n getSettings,\n onSetAnswer,\n onSuccess,\n onError,\n goPrev,\n goNext: props.routeToQuestion,\n onSurveyFinish: props.onSurveyFinish,\n question: props.question,\n });\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/survey/survey-module-controller-factory.tsx","../../src/survey/survey-module-wrapper.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { SurveyModuleWrapper } from './survey-module-wrapper';\nimport type {\n SurveyMountProps,\n SurveyComponentProps,\n SurveyModuleController,\n SurveyModuleWrapperConfig,\n} from './types.ts';\n\ntype SurveyLayout = ComponentType<{\n mountProps: SurveyMountProps;\n surveyProps: SurveyComponentProps;\n children: ReactNode;\n}>;\n\nexport const createSurveyModuleControllerFactory = <E extends object = object>(\n Layout: SurveyLayout,\n config: SurveyModuleWrapperConfig\n) => {\n return (Module: ComponentType<SurveyComponentProps>): SurveyModuleController<E> => {\n let root: ReturnType<typeof createRoot> | null = null;\n\n const mount = (targetNodeId: string, props: SurveyMountProps<E>) => {\n const el = document.getElementById(targetNodeId);\n\n if (!el) {\n throw new Error(`DOM элемент \"${targetNodeId}\" не найден.`);\n }\n\n root = createRoot(el);\n root.render(\n <SurveyModuleWrapper {...(props as SurveyMountProps)} config={config}>\n {(injectedProps: SurveyComponentProps) => (\n <Layout mountProps={props as SurveyMountProps} surveyProps={injectedProps}>\n <Module {...injectedProps} />\n </Layout>\n )}\n </SurveyModuleWrapper>\n );\n };\n\n const unmount = () => {\n if (root !== null) {\n root.unmount();\n root = null;\n }\n };\n\n return { mount, unmount };\n };\n};\n","import { useState, useMemo, useLayoutEffect, useCallback, type ReactNode } from 'react';\nimport dayjs from 'dayjs';\nimport { create, type CtxStoreWithSurvey, type SurveyResultEntry } from '@smartex.com/ctx';\nimport type { SurveyMountProps, SurveyComponentProps, SurveyModuleWrapperConfig } from './types.ts';\n\ntype AnyCtxStore = CtxStoreWithSurvey<Record<string, unknown>>;\n\n/**\n * Internal accessor for the global ctx store.\n * Avoids declaring `ctx` on `Window` in the library — that declaration\n * belongs to the consuming project, which knows the actual schema type.\n */\nconst getCtx = (): AnyCtxStore => {\n const store = (window as any).ctx as AnyCtxStore | undefined;\n\n if (!store) {\n throw new Error('ctx is not initialized.');\n }\n\n return store;\n};\n\nconst hasCtx = (): boolean => !!(window as any).ctx;\n\ntype SurveyWrapperProps = SurveyMountProps & {\n config: SurveyModuleWrapperConfig;\n children: (injectedProps: SurveyComponentProps) => ReactNode;\n};\n\nexport const SurveyModuleWrapper = ({ children, config, ...props }: SurveyWrapperProps) => {\n const [ctxReady, setCtxReady] = useState(false);\n const startTime = useMemo(() => dayjs().format('DD.MM.YYYY HH:mm:ss'), []);\n\n const initSurveyCtx = () => {\n create({\n name: 'ctx',\n schema: config.schema,\n survey: { code: props.surveyCode },\n });\n\n const ctx = getCtx();\n ctx.set('token', props.token);\n ctx.set('lang', props.lang ?? config.defaultLang);\n ctx.set('theme', props.theme ?? null);\n\n if (props.externalData) {\n for (const [key, value] of Object.entries(props.externalData)) {\n ctx.set(key, value);\n }\n }\n };\n\n const getPrevQuestionCode = () => {\n const history = getCtx().survey.getHistory();\n return history.length > 0 ? history[history.length - 1].code : null;\n };\n\n const getPrevQuestionData = () => {\n const prevQuestion = getPrevQuestionCode();\n return prevQuestion ? getCtx().survey.getData(prevQuestion) : {};\n };\n\n const getSettings = useCallback(<T = unknown,>(key?: string): [T | null, string | null] => {\n if (!props.question.settings) {\n return [null, null];\n }\n\n let settings: Record<string, unknown>;\n\n try {\n settings = JSON.parse(props.question.settings);\n } catch {\n return [null, 'Ошибка парсинга настроек вопроса'];\n }\n\n if (Object.keys(settings).length === 0) return [null, null];\n if (!key) return [settings as T, null];\n if (!Object.prototype.hasOwnProperty.call(settings, key)) {\n return [null, `В настройках вопроса нет параметра \"${key}\"`];\n }\n\n return [settings[key] as T, null];\n }, []);\n\n const routeToPrevQuestion = (questionCode?: number) => {\n const ctx = getCtx();\n const currentQuestionCode = questionCode ?? props.question.code;\n const prevQuestionCode = getPrevQuestionCode();\n const prevQuestionType = prevQuestionCode\n ? ctx.survey.getResult(prevQuestionCode)?.questionType\n : null;\n\n if (prevQuestionCode === null) {\n ctx.destroy();\n props.onExit();\n return;\n }\n\n ctx.survey.removeData(currentQuestionCode);\n ctx.survey.removeResult(currentQuestionCode);\n ctx.survey.undoHistory();\n\n if (config.logicScreenAliases.includes(prevQuestionType as string)) {\n routeToPrevQuestion(prevQuestionCode);\n return;\n }\n\n props.routeToQuestion(prevQuestionCode);\n };\n\n const goPrev = useCallback(() => routeToPrevQuestion(), []);\n\n const onSetAnswer = useCallback(\n (answer: number | number[], value: SurveyResultEntry['result']) => {\n const ctx = getCtx();\n let weight = 0;\n\n if (Array.isArray(answer)) {\n weight = props.question.answers\n .filter((item) => answer.includes(item.code))\n .reduce((sum, item) => sum + (item.weight ?? 0), 0);\n } else {\n weight = props.question.answers.find((item) => item.code === answer)?.weight ?? 0;\n }\n\n ctx.survey.setAnswerWeight(props.question.code, weight);\n ctx.survey.addHistory({\n code: props.question.code,\n answers: Array.isArray(answer) ? [...answer] : [answer],\n stime: startTime,\n etime: dayjs().format('DD.MM.YYYY HH:mm:ss'),\n });\n ctx.survey.addResult({\n questionCode: props.question.code,\n questionType: props.question.typeCode,\n answerCode: Array.isArray(answer) ? null : answer,\n result: value,\n });\n },\n []\n );\n\n const proceed = useCallback(() => {\n const baseAnswers = props.question.answers.filter((answer) => !answer.isCustomRouting);\n onSetAnswer(baseAnswers[0].code, null);\n props.routeToQuestion(baseAnswers[0].nextQuestion as number);\n }, []);\n\n const onError = useCallback((errors: string[]) => {\n // SEND LOGS via API\n props.onError(errors);\n }, []);\n\n useLayoutEffect(() => {\n if (props.question.isFirst) {\n if (hasCtx()) getCtx().destroy();\n initSurveyCtx();\n }\n\n if (!hasCtx()) {\n throw new Error('Отсутствует контекст для работы с модулями!');\n }\n\n const ctx = getCtx();\n // Results and data cleanup on module init\n ctx.survey.removeData(props.question.code);\n ctx.survey.removeResult(props.question.code);\n ctx.survey.addData(props.question.code, getPrevQuestionData(), true);\n setCtxReady(true); // eslint-disable-line react-hooks/set-state-in-effect\n }, []);\n\n if (!ctxReady) return null;\n\n return children({\n getSettings,\n onSetAnswer,\n proceed,\n onError,\n goPrev,\n goNext: props.routeToQuestion,\n onComplete: props.onComplete,\n question: props.question,\n });\n};\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartex.com/modules-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -15,8 +15,19 @@
|
|
|
15
15
|
"import": "./dist/survey/index.js"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
|
+
"typesVersions": {
|
|
19
|
+
"*": {
|
|
20
|
+
"base": [
|
|
21
|
+
"dist/base/index.d.ts"
|
|
22
|
+
],
|
|
23
|
+
"survey": [
|
|
24
|
+
"dist/survey/index.d.ts"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
18
28
|
"scripts": {
|
|
19
|
-
"
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"build": "npm run typecheck && tsup",
|
|
20
31
|
"prepublishOnly": "npm run build"
|
|
21
32
|
},
|
|
22
33
|
"peerDependencies": {
|