@webitel/ui-sdk 25.6.6 → 25.6.7

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 (31) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/types/enums/WtObject/WtObject.d.ts +2 -0
  3. package/dist/types/modules/AuditForm/components/audit-form-question-read-wrapper.vue.d.ts +16 -15
  4. package/dist/types/modules/AuditForm/components/audit-form-question.vue.d.ts +20 -15
  5. package/dist/types/modules/AuditForm/components/audit-form.vue.d.ts +33 -13
  6. package/dist/types/modules/AuditForm/components/form/form-footer/audit-form-footer.vue.d.ts +13 -0
  7. package/dist/types/modules/AuditForm/components/form-answers/answer-editing-info/audit-form-answer-editing-info.vue.d.ts +9 -0
  8. package/dist/types/modules/AuditForm/components/{questions → form-questions}/options/audit-form-question-options-write-row.vue.d.ts +1 -1
  9. package/dist/types/modules/AuditForm/components/form-questions/options/audit-form-question-options.vue.d.ts +19 -0
  10. package/dist/types/modules/AuditForm/components/{questions → form-questions}/score/audit-form-question-score.vue.d.ts +1 -1
  11. package/dist/types/modules/Userinfo/v2/enums/ScopeClass/ScopeClass.d.ts +2 -0
  12. package/package.json +1 -1
  13. package/src/enums/WtObject/WtObject.ts +2 -0
  14. package/src/locale/en/en.js +4 -0
  15. package/src/locale/ru/ru.js +4 -0
  16. package/src/locale/ua/ua.js +4 -0
  17. package/src/modules/AuditForm/components/audit-form-question-read-wrapper.vue +64 -37
  18. package/src/modules/AuditForm/components/audit-form-question-write-wrapper.vue +2 -2
  19. package/src/modules/AuditForm/components/audit-form-question.vue +43 -43
  20. package/src/modules/AuditForm/components/audit-form.vue +75 -54
  21. package/src/modules/AuditForm/components/form/form-footer/audit-form-footer.vue +65 -0
  22. package/src/modules/AuditForm/components/form-answers/answer-editing-info/audit-form-answer-editing-info.vue +106 -0
  23. package/src/modules/AuditForm/components/{questions → form-questions}/options/audit-form-question-options-write-row.vue +1 -1
  24. package/src/modules/AuditForm/components/{questions → form-questions}/options/audit-form-question-options.vue +32 -26
  25. package/src/modules/Userinfo/v2/enums/ScopeClass/ScopeClass.ts +2 -0
  26. package/src/modules/Userinfo/v2/mappings/mappings.ts +2 -0
  27. package/dist/types/modules/AuditForm/components/questions/options/audit-form-question-options.vue.d.ts +0 -12
  28. /package/src/modules/AuditForm/components/{questions → form-questions}/options/__tests__/audit-form-question-options-write-row.spec.js +0 -0
  29. /package/src/modules/AuditForm/components/{questions → form-questions}/options/__tests__/audit-form-question-options.spec.js +0 -0
  30. /package/src/modules/AuditForm/components/{questions → form-questions}/score/__tests__/audit-form-question-score.spec.js +0 -0
  31. /package/src/modules/AuditForm/components/{questions → form-questions}/score/audit-form-question-score.vue +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [v25.6.6] - 2025-05-07
2
+ ### :bug: Bug Fixes
3
+ - [`0c370cd`](https://github.com/webitel/webitel-ui-sdk/commit/0c370cdfe2543d6fc41c38650bbd4455b263b942) - fixed search for get sys type records list [WTEL-6851](https://webitel.atlassian.net/browse/WTEL-6851) *(commit by [@plnnsimon](https://github.com/plnnsimon))*
4
+
5
+
1
6
  ## [v25.6.5] - 2025-05-05
2
7
  ### :bug: Bug Fixes
3
8
  - [`39d99ac`](https://github.com/webitel/webitel-ui-sdk/commit/39d99ac3629658df76536b9d8e6026cb5747d9e1) - fixed styles for select tags line-height [WTEL-6360](https://webitel.atlassian.net/browse/WTEL-6360) *(commit by [@plnnsimon](https://github.com/plnnsimon))*
@@ -2086,3 +2091,4 @@
2086
2091
  [v25.6.3]: https://github.com/webitel/webitel-ui-sdk/compare/v25.6.2...v25.6.3
2087
2092
  [v25.6.4]: https://github.com/webitel/webitel-ui-sdk/compare/v25.6.3...v25.6.4
2088
2093
  [v25.6.5]: https://github.com/webitel/webitel-ui-sdk/compare/v25.6.4...v25.6.5
2094
+ [v25.6.6]: https://github.com/webitel/webitel-ui-sdk/compare/v25.6.5...v25.6.6
@@ -56,5 +56,7 @@ export declare const WtObject: {
56
56
  readonly Status: "status";
57
57
  readonly Source: "source";
58
58
  readonly CustomLookup: "customLookup";
59
+ readonly AuditForm: "auditForm";
60
+ readonly AuditRating: "auditRating";
59
61
  };
60
62
  export type WtObject = (typeof WtObject)[keyof typeof WtObject];
@@ -1,16 +1,17 @@
1
- declare const _default: import("vue").DefineComponent<{}, {
2
- $emit: (event: "change:result" | "activate", ...args: any[]) => void;
3
- question: Record<string, any>;
4
- result: Record<string, any>;
5
- disableDragging: boolean;
6
- readonly: boolean;
7
- first: boolean;
8
- $props: {
9
- readonly question?: Record<string, any>;
10
- readonly result?: Record<string, any>;
11
- readonly disableDragging?: boolean;
12
- readonly readonly?: boolean;
13
- readonly first?: boolean;
14
- };
15
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ import { EngineQuestion, EngineQuestionAnswer } from 'webitel-sdk';
2
+ type __VLS_Props = {
3
+ question: EngineQuestion;
4
+ disableDragging?: boolean;
5
+ first?: boolean;
6
+ };
7
+ type __VLS_PublicProps = __VLS_Props & {
8
+ 'answer'?: EngineQuestionAnswer | null;
9
+ };
10
+ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ "update:answer": (value: EngineQuestionAnswer) => any;
12
+ activate: () => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
14
+ "onUpdate:answer"?: (value: EngineQuestionAnswer) => any;
15
+ onActivate?: () => any;
16
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
17
  export default _default;
@@ -1,18 +1,23 @@
1
- declare const _default: import("vue").DefineComponent<{}, {
1
+ import { EngineQuestion, EngineQuestionAnswer } from "webitel-sdk";
2
+ type __VLS_Props = {
3
+ question: EngineQuestion;
4
+ answer: EngineQuestionAnswer | null;
5
+ first?: boolean;
6
+ };
7
+ declare function activateQuestion(): void;
8
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {
2
9
  activateQuestion: typeof activateQuestion;
3
- $emit: (event: "delete" | "copy" | "update:question" | "update:result", ...args: any[]) => void;
4
- question: Record<string, any>;
5
- readonly: boolean;
10
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ delete: () => any;
12
+ copy: () => any;
13
+ "update:question": (args_0: unknown) => any;
14
+ "update:answer": (args_0: unknown) => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onDelete?: () => any;
17
+ onCopy?: () => any;
18
+ "onUpdate:question"?: (args_0: unknown) => any;
19
+ "onUpdate:answer"?: (args_0: unknown) => any;
20
+ }>, {
6
21
  first: boolean;
7
- mode?: string;
8
- result?: Record<string, any>;
9
- $props: {
10
- readonly question?: Record<string, any>;
11
- readonly readonly?: boolean;
12
- readonly first?: boolean;
13
- readonly mode?: string;
14
- readonly result?: Record<string, any>;
15
- };
16
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
22
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
23
  export default _default;
18
- declare function activateQuestion(): void;
@@ -1,14 +1,34 @@
1
- declare const _default: import("vue").DefineComponent<{}, {
2
- $emit: (event: "update:result" | "update:questions" | "update:validation", ...args: any[]) => void;
3
- mode: string;
4
- readonly: boolean;
5
- questions: unknown[];
6
- result?: unknown[];
7
- $props: {
8
- readonly mode?: string;
9
- readonly readonly?: boolean;
10
- readonly questions?: unknown[];
11
- readonly result?: unknown[];
12
- };
13
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ import { EngineQuestion, EngineQuestionAnswer } from 'webitel-sdk';
2
+ declare const AuditFormMode: {
3
+ readonly Create: "create";
4
+ readonly Fill: "fill";
5
+ };
6
+ type AuditFormMode = typeof AuditFormMode[keyof typeof AuditFormMode];
7
+ type __VLS_Props = {
8
+ mode: AuditFormMode;
9
+ readonly?: boolean;
10
+ };
11
+ type __VLS_PublicProps = __VLS_Props & {
12
+ 'answers'?: EngineQuestionAnswer[];
13
+ /**
14
+ * todo: rename to questionsModel and use instead of 'update:questions' event
15
+ */
16
+ 'questions'?: EngineQuestion[];
17
+ 'resultComment'?: string;
18
+ };
19
+ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
+ "update:questions": (...args: any[]) => void;
21
+ "update:validation": (...args: any[]) => void;
22
+ "save:evaluation": (...args: any[]) => void;
23
+ "cancel:evaluation": (...args: any[]) => void;
24
+ "update:answers": (value: EngineQuestionAnswer[]) => void;
25
+ "update:resultComment": (value: string) => void;
26
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
27
+ "onUpdate:questions"?: (...args: any[]) => any;
28
+ "onUpdate:validation"?: (...args: any[]) => any;
29
+ "onSave:evaluation"?: (...args: any[]) => any;
30
+ "onCancel:evaluation"?: (...args: any[]) => any;
31
+ "onUpdate:answers"?: (value: EngineQuestionAnswer[]) => any;
32
+ "onUpdate:resultComment"?: (value: string) => any;
33
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
34
  export default _default;
@@ -0,0 +1,13 @@
1
+ type __VLS_Props = {
2
+ invalid: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "fill:save": () => any;
6
+ "fill:cancel": () => any;
7
+ "create:add": () => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ "onFill:save"?: () => any;
10
+ "onFill:cancel"?: () => any;
11
+ "onCreate:add"?: () => any;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import { EngineQuestionAnswer } from "webitel-sdk";
2
+ type __VLS_Props = {
3
+ answer: EngineQuestionAnswer;
4
+ collapsible?: boolean;
5
+ };
6
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
7
+ collapsible: boolean;
8
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ export default _default;
@@ -1,5 +1,5 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
2
- $emit: (event: "delete" | "change:option", ...args: any[]) => void;
2
+ $emit: (event: "delete" | "update:option", ...args: any[]) => void;
3
3
  option: Record<string, any>;
4
4
  first: boolean;
5
5
  $props: {
@@ -0,0 +1,19 @@
1
+ import { EngineQuestion, EngineQuestionAnswer } from "webitel-sdk";
2
+ type __VLS_Props = {
3
+ /**
4
+ * question mode, NOT audit form mode
5
+ */
6
+ mode: 'read' | 'write';
7
+ };
8
+ type __VLS_PublicProps = __VLS_Props & {
9
+ 'question'?: EngineQuestion;
10
+ 'answer'?: EngineQuestionAnswer | null;
11
+ };
12
+ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
+ "update:question": (value: EngineQuestion) => any;
14
+ "update:answer": (value: EngineQuestionAnswer) => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
16
+ "onUpdate:question"?: (value: EngineQuestion) => any;
17
+ "onUpdate:answer"?: (value: EngineQuestionAnswer) => any;
18
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ export default _default;
@@ -1,5 +1,5 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
2
- $emit: (event: "change:result" | "change:question", ...args: any[]) => void;
2
+ $emit: (event: "change:question" | "change:result", ...args: any[]) => void;
3
3
  mode: string;
4
4
  question: Record<string, any>;
5
5
  result?: Record<string, any>;
@@ -41,5 +41,7 @@ export declare const ScopeClass: {
41
41
  readonly ChatBots: "chat_bots";
42
42
  readonly Cases: "cases";
43
43
  readonly CaseComments: "case_comments";
44
+ readonly AuditForm: "cc_audit_form";
45
+ readonly AuditRating: "rating";
44
46
  };
45
47
  export type ScopeClass = (typeof ScopeClass)[keyof typeof ScopeClass];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "25.6.6",
3
+ "version": "25.6.7",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "npm run docs:dev",
@@ -56,6 +56,8 @@ export const WtObject = {
56
56
  Status: 'status',
57
57
  Source: 'source',
58
58
  CustomLookup: 'customLookup',
59
+ AuditForm: 'auditForm',
60
+ AuditRating: 'auditRating',
59
61
  } as const;
60
62
 
61
63
  export type WtObject = (typeof WtObject)[keyof typeof WtObject];
@@ -23,6 +23,7 @@ import { snakeToCamel } from '../../scripts';
23
23
  export default {
24
24
  // describes reusable buttons, actions, default titles, and other ui elements
25
25
  reusable: {
26
+ comment: 'Comment',
26
27
  replace: 'Replace',
27
28
  download: 'Download',
28
29
  history: 'History',
@@ -88,6 +89,9 @@ export default {
88
89
  unassigned: 'Unassigned',
89
90
  showUnassigned: 'Show unassigned',
90
91
  group: 'Group',
92
+ updatedBy: (/*{ named }*/) => {
93
+ return 'Edited';
94
+ },
91
95
  },
92
96
  // yak zhe ya zaebalsya povtoriaty odni i ti sami slova!!!!
93
97
  vocabulary: {
@@ -23,6 +23,7 @@ import { snakeToCamel } from '../../scripts';
23
23
  export default {
24
24
  // describes reusable buttons, actions, default titles, and other ui elements
25
25
  reusable: {
26
+ comment: 'Коментарий',
26
27
  replace: 'Заменить',
27
28
  download: 'Скачать',
28
29
  history: 'История',
@@ -88,6 +89,9 @@ export default {
88
89
  unassigned: 'Неназначенные',
89
90
  showUnassigned: 'Показать неназначенные',
90
91
  group: 'Группа',
92
+ updatedBy: (/*{ named }*/) => {
93
+ return 'Редактировано';
94
+ },
91
95
  },
92
96
  vocabulary: {
93
97
  apply: 'Применить',
@@ -23,6 +23,7 @@ import { snakeToCamel } from '../../scripts';
23
23
  export default {
24
24
  // describes reusable buttons, actions, default titles, and other ui elements
25
25
  reusable: {
26
+ comment: 'Коментар',
26
27
  replace: 'Замінити',
27
28
  download: 'Завантажити',
28
29
  history: 'Історія',
@@ -89,6 +90,9 @@ export default {
89
90
  unassigned: 'Непризначені',
90
91
  showUnassigned: 'Показати непризначені',
91
92
  group: 'Група',
93
+ updatedBy: (/*{ named }*/) => {
94
+ return 'Редаговано';
95
+ },
92
96
  },
93
97
  vocabulary: {
94
98
  apply: 'Застосувати',
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <article
3
3
  :class="{
4
- 'audit-form-question-read--filled': isResult && result.score != null,
4
+ 'audit-form-question-read--filled': isAnswer && answerModel.score != null,
5
5
  'audit-form-question-read--readonly': readonly,
6
6
  }"
7
7
  class="audit-form-question-read"
@@ -26,55 +26,61 @@
26
26
  <section class="audit-form-question-read-content">
27
27
  <component
28
28
  :is="QuestionTypeComponent"
29
+ v-model:answer="answerModel"
29
30
  :question="question"
30
- :result="result"
31
+ :result="answerModel /* compat, should use 'answer' */"
31
32
  mode="read"
32
- @change:result="!readonly && emit('change:result', $event)"
33
+ @change:result="updateAnswer /* compat, should use 'answer' */"
33
34
  />
34
35
  <div
35
- v-show="isResult"
36
+ v-show="isAnswer"
36
37
  class="audit-form-question--clear"
37
- @click="emit('change:result', {})"
38
+ @click="resetAnswer"
38
39
  >
39
- {{ $t('webitelUI.auditForm.clearSelection') }}
40
+ {{ t('webitelUI.auditForm.clearSelection') }}
40
41
  </div>
42
+ <wt-input
43
+ v-if="answerModel?.createdAt"
44
+ v-model="answerModel.comment"
45
+ :label="t('reusable.comment')"
46
+ />
41
47
  </section>
48
+
49
+ <template v-if="answerModel?.updatedAt">
50
+ <wt-divider />
51
+ <audit-form-answer-editing-info
52
+ :answer="answerModel"
53
+ />
54
+ </template>
42
55
  </article>
43
56
  </template>
44
57
 
45
- <script setup>
46
- import { computed } from 'vue';
47
- import { EngineAuditQuestionType } from 'webitel-sdk';
58
+ <script lang="ts" setup>
59
+ import { computed, inject } from 'vue';
60
+ import {useI18n} from "vue-i18n";
61
+ import {EngineAuditQuestionType, EngineQuestion, EngineQuestionAnswer} from 'webitel-sdk';
48
62
 
49
- import WtIcon from '../../../components/wt-icon/wt-icon.vue';
63
+ import { WtDivider,WtIcon, WtInput } from '../../../components';
50
64
  import isEmpty from '../../../scripts/isEmpty.js';
51
- import AuditFormQuestionOptions from './questions/options/audit-form-question-options.vue';
52
- import AuditFormQuestionScore from './questions/score/audit-form-question-score.vue';
53
-
54
- const props = defineProps({
55
- question: {
56
- type: Object,
57
- required: true,
58
- },
59
- result: {
60
- type: [Object, null],
61
- required: true,
62
- },
63
- disableDragging: {
64
- type: Boolean,
65
- default: false,
66
- },
67
- readonly: {
68
- type: Boolean,
69
- default: false,
70
- },
71
- first: {
72
- type: Boolean,
73
- default: false,
74
- },
75
- });
65
+ import AuditFormAnswerEditingInfo from "./form-answers/answer-editing-info/audit-form-answer-editing-info.vue";
66
+ import AuditFormQuestionOptions from './form-questions/options/audit-form-question-options.vue';
67
+ import AuditFormQuestionScore from './form-questions/score/audit-form-question-score.vue';
68
+
69
+ const readonly = inject('readonly');
70
+
71
+ const answerModel = defineModel<EngineQuestionAnswer | null>('answer');
72
+
73
+ const props = defineProps<{
74
+ question: EngineQuestion;
75
+ disableDragging?: boolean;
76
+ first?: boolean;
77
+ }>();
78
+
79
+ const emit = defineEmits<{
80
+ 'activate': [];
81
+ }>();
76
82
 
77
- const emit = defineEmits(['change:result', 'activate']);
83
+ const { t } = useI18n();
78
84
 
79
85
  const QuestionTypeComponent = computed(() => {
80
86
  if (props.question.type === EngineAuditQuestionType.Option)
@@ -84,7 +90,22 @@ const QuestionTypeComponent = computed(() => {
84
90
  return null;
85
91
  });
86
92
 
87
- const isResult = computed(() => !isEmpty(props.result));
93
+ const isAnswer = computed(() => !isEmpty(answerModel.value));
94
+
95
+ const updateAnswer = (value: EngineQuestionAnswer) => {
96
+ if (readonly.value) return; // if ... then in preview mode
97
+
98
+ // coz only some properties of answer may be patched
99
+ const newAnswer = Object.assign(answerModel.value, value);
100
+ answerModel.value = newAnswer;
101
+ };
102
+
103
+ const resetAnswer = () => {
104
+ answerModel.value = {
105
+ ...answerModel.value,
106
+ score: null, // reset only score field
107
+ };
108
+ };
88
109
  </script>
89
110
 
90
111
  <style lang="scss" scoped>
@@ -128,6 +149,12 @@ const isResult = computed(() => !isEmpty(props.result));
128
149
  }
129
150
  }
130
151
 
152
+ .audit-form-question-read-content {
153
+ display: flex;
154
+ flex-direction: column;
155
+ gap: var(--spacing-sm);
156
+ }
157
+
131
158
  .audit-form-question--clear {
132
159
  cursor: pointer;
133
160
  margin-top: var(--spacing-sm);
@@ -71,8 +71,8 @@ import WtSwitcher from '../../../components/wt-switcher/wt-switcher.vue';
71
71
  import WtTooltip from '../../../components/wt-tooltip/wt-tooltip.vue';
72
72
  import { generateQuestionOptionsSchema } from '../schemas/AuditFormQuestionOptionsSchema.js';
73
73
  import { generateQuestionScoreSchema } from '../schemas/AuditFormQuestionScoreSchema.js';
74
- import AuditFormQuestionOptions from './questions/options/audit-form-question-options.vue';
75
- import AuditFormQuestionScore from './questions/score/audit-form-question-score.vue';
74
+ import AuditFormQuestionOptions from './form-questions/options/audit-form-question-options.vue';
75
+ import AuditFormQuestionScore from './form-questions/score/audit-form-question-score.vue';
76
76
 
77
77
  const props = defineProps({
78
78
  question: {
@@ -5,7 +5,7 @@
5
5
  :class="[
6
6
  `audit-form-question--mode-${mode}`,
7
7
  {
8
- 'audit-form-question--answered': isResult,
8
+ 'audit-form-question--answered': isAnswer,
9
9
  },
10
10
  {
11
11
  'audit-form-question--sort-ignore': first && mode === 'fill',
@@ -15,54 +15,48 @@
15
15
  :first="first"
16
16
  :question="question"
17
17
  :readonly="readonly"
18
- :result="result"
18
+ :result="answer /* compat, should be ':answer' */"
19
+ :answer="answer"
19
20
  :v="v$"
20
21
  class="audit-form-question"
21
22
  @activate="activateQuestion"
22
- @copy="emits('copy')"
23
- @delete="emits('delete')"
24
- @change:question="emits('update:question', $event)"
25
- @change:result="emits('update:result', $event)"
23
+ @copy="emit('copy')"
24
+ @delete="emit('delete')"
25
+ @change:question="emit('update:question', $event) /* compat, should be removed */"
26
+ @update:question="emit('update:question', $event)"
27
+ @change:result="emit('update:answer', $event) /* compat, should be ':answer' */"
28
+ @update:answer="emit('update:answer', $event)"
26
29
  />
27
30
  </template>
28
31
 
29
- <script setup>
32
+ <script lang="ts" setup>
30
33
  import { useVuelidate } from '@vuelidate/core';
31
34
  import { required } from '@vuelidate/validators';
32
- import { computed, onMounted, ref, toRefs } from 'vue';
35
+ import { computed, inject,onMounted, ref, toRefs } from 'vue';
36
+ import { EngineQuestion, EngineQuestionAnswer } from "webitel-sdk";
33
37
 
34
38
  import vClickaway from '../../../directives/clickaway/clickaway.js';
35
39
  import isEmpty from '../../../scripts/isEmpty.js';
36
40
  import QuestionRead from './audit-form-question-read-wrapper.vue';
37
41
  import QuestionWrite from './audit-form-question-write-wrapper.vue';
38
42
 
39
- const props = defineProps({
40
- question: {
41
- type: Object,
42
- required: true,
43
- },
44
- result: {
45
- type: Object,
46
- },
47
- mode: {
48
- type: String,
49
- },
50
- first: {
51
- type: Boolean,
52
- default: false,
53
- },
54
- readonly: {
55
- type: Boolean,
56
- default: false,
57
- },
43
+ const mode = inject('mode');
44
+ const readonly = inject('readonly');
45
+
46
+ const props = withDefaults(defineProps<{
47
+ question: EngineQuestion;
48
+ answer: EngineQuestionAnswer | null;
49
+ first?: boolean;
50
+ }>(), {
51
+ first: false,
58
52
  });
59
53
 
60
- const emits = defineEmits([
61
- 'copy',
62
- 'delete',
63
- 'update:question',
64
- 'update:result',
65
- ]);
54
+ const emit = defineEmits<{
55
+ 'copy': [];
56
+ 'delete': [];
57
+ 'update:question': [unknown]; // todo
58
+ 'update:answer': [unknown]; // todo
59
+ }>();
66
60
 
67
61
  const QuestionState = {
68
62
  SAVED: 'saved',
@@ -72,44 +66,50 @@ const QuestionState = {
72
66
  const state = ref(QuestionState.SAVED);
73
67
 
74
68
  // is needed for useVuelidate, because props.question/props.result isn't reactive
75
- const { question, result } = toRefs(props);
69
+ const { question, answer } = toRefs(props);
76
70
 
77
71
  const v$ = useVuelidate(
78
72
  computed(() =>
79
- props.mode === 'create'
73
+ mode === 'create'
80
74
  ? {
81
75
  question: {
82
76
  question: { required },
83
77
  },
84
78
  }
85
79
  : {
86
- result: {
87
- required: (value) =>
88
- question.value.required ? !isEmpty(value) : true,
80
+ answer: {
81
+ required: (value) => {
82
+ // if not required, no need to validate
83
+ if (!props.question.required) return true;
84
+
85
+ if (value && value?.score != null) {
86
+ return true;
87
+ }
88
+ },
89
89
  },
90
90
  },
91
91
  ),
92
- { question, result },
92
+ { question, answer },
93
93
  { $autoDirty: true },
94
94
  );
95
95
 
96
96
  const component = computed(() => {
97
- if (props.readonly) return QuestionRead;
98
- if (props.mode === 'create') {
97
+ if (readonly) return QuestionRead;
98
+ if (mode === 'create') {
99
99
  if (state.value === QuestionState.SAVED) return QuestionRead;
100
100
  return QuestionWrite;
101
101
  }
102
102
  return QuestionRead;
103
103
  });
104
104
 
105
- const isResult = computed(() => !isEmpty(props.result));
105
+ const isAnswer = computed(() => !isEmpty(props.answer));
106
106
 
107
107
  function saveQuestion() {
108
108
  state.value = QuestionState.SAVED;
109
109
  }
110
110
 
111
111
  function activateQuestion() {
112
- if (props.mode !== 'create') return;
112
+ if (mode !== 'create') return;
113
113
  state.value = QuestionState.EDIT;
114
114
  }
115
115
 
@@ -10,78 +10,90 @@
10
10
  :key="key"
11
11
  ref="auditQuestions"
12
12
  :first="key === 0"
13
- :mode="mode"
14
13
  :question="question"
15
- :readonly="readonly"
16
- :result="result && result[key] ? result[key] : null"
14
+ :answer="answers && answers[key] ? answers[key] : null"
17
15
  @copy="copyQuestion({ question, key })"
18
16
  @delete="deleteQuestion({ question, key })"
19
17
  @update:question="handleQuestionUpdate({ key, value: $event })"
20
- @update:result="handleResultUpdate({ key, value: $event })"
18
+ @update:answer="handleAnswerUpdate({ key, value: $event })"
21
19
  />
22
20
  </div>
23
- <wt-button
24
- v-if="mode === 'create' && !readonly"
25
- :disabled="isInvalidForm"
26
- class="audit-form__add-button"
27
- @click="addQuestion"
28
- >
29
- {{ $t('webitelUI.auditForm.addQuestion') }}
30
- </wt-button>
21
+
22
+ <wt-textarea
23
+ v-model="resultCommentModel"
24
+ class="call-evaluation-form__comment"
25
+ :label="$t('reusable.comment')"
26
+ />
27
+
28
+ <audit-form-footer
29
+ :invalid="isInvalidForm"
30
+ @fill:save="saveEvaluation"
31
+ @fill:cancel="cancelEvaluation"
32
+ @create:add="addQuestion"
33
+ />
31
34
  </section>
32
35
  </template>
33
36
 
34
- <script setup>
37
+ <script lang="ts" setup>
35
38
  import { useVuelidate } from '@vuelidate/core';
36
39
  import cloneDeep from 'lodash/cloneDeep.js';
37
40
  import {
38
41
  computed,
39
42
  nextTick,
40
43
  onMounted,
44
+ provide,
41
45
  reactive,
42
- ref,
46
+ useTemplateRef,
43
47
  watch,
44
- watchEffect,
45
48
  } from 'vue';
49
+ import {EngineQuestion, EngineQuestionAnswer} from 'webitel-sdk';
46
50
 
47
- import WtButton from '../../../components/wt-button/wt-button.vue';
51
+ import { WtTextarea } from '../../../components';
48
52
  import { useDestroyableSortable } from '../../../composables/useDestroyableSortable/useDestroyableSortable.js';
49
53
  import { generateQuestionSchema } from '../schemas/AuditFormQuestionSchema.js';
50
54
  import AuditFormQuestion from './audit-form-question.vue';
55
+ import AuditFormFooter from "./form/form-footer/audit-form-footer.vue";
51
56
 
52
- const props = defineProps({
53
- mode: {
54
- type: String,
55
- required: true,
56
- /*
57
- * Available options: ['create', 'fill']
58
- * */
59
- },
60
- questions: {
61
- type: Array,
62
- required: true,
63
- },
64
- result: {
65
- type: Array,
66
- },
67
- readonly: {
68
- type: Boolean,
69
- default: false,
70
- },
71
- });
57
+ const answersModel = defineModel<EngineQuestionAnswer[]>('answers');
58
+
59
+ /**
60
+ * todo: rename to questionsModel and use instead of 'update:questions' event
61
+ */
62
+ const questions = defineModel<EngineQuestion[]>('questions', {});
63
+ const resultCommentModel = defineModel<string>('resultComment');
64
+
65
+ const AuditFormMode = {
66
+ Create: 'create',
67
+ Fill: 'fill',
68
+ } as const;
69
+
70
+ type AuditFormMode = typeof AuditFormMode[keyof typeof AuditFormMode];
71
+
72
+ const props = defineProps<{
73
+ mode: AuditFormMode;
74
+ readonly?: boolean;
75
+ }>();
72
76
 
73
77
  const emit = defineEmits([
78
+ /**
79
+ * todo: remove and use questions model
80
+ */
74
81
  'update:questions',
75
- 'update:result',
76
82
  'update:validation',
83
+ 'save:evaluation',
84
+ 'cancel:evaluation',
77
85
  ]);
78
86
 
79
87
  const v$ = useVuelidate();
80
88
 
81
- const isInvalidForm = computed(() => !!v$.value.$errors.length);
82
- const auditQuestions = ref(null);
89
+ const auditQuestions = useTemplateRef('auditQuestions');
83
90
  const isQuestionAdded = reactive({ value: false, index: null });
84
91
 
92
+ provide('readonly', props.readonly);
93
+ provide('mode', props.mode);
94
+
95
+ const isInvalidForm = computed(() => !!v$.value.$errors.length);
96
+
85
97
  async function addQuestion({ index, question } = {}) {
86
98
  const questions = [...(props.questions || [])];
87
99
  const newQuestion = question || generateQuestionSchema();
@@ -118,24 +130,33 @@ function changeQuestionsOrder({ oldIndex, newIndex }) {
118
130
  emit('update:questions', questions);
119
131
  }
120
132
 
121
- function handleResultUpdate({ key, value }) {
122
- const result = [...props.result];
123
- result[key] = value;
124
- emit('update:result', result);
133
+ function handleAnswerUpdate({ key, value }) {
134
+ const answer = [...answersModel.value];
135
+ answer[key] = value;
136
+ answersModel.value = answer;
125
137
  }
126
138
 
127
- function initResult() {
128
- const result = props.questions?.map(() => ({}));
129
- emit('update:result', result);
139
+ function initAnswers() {
140
+ if (!answersModel.value || !answersModel.value.length) {
141
+ answersModel.value = props.questions.map(() => ({}));
142
+ }
130
143
  }
131
144
 
132
145
  function initQuestions() {
133
- if (props.mode === 'create' && !props.questions?.length) {
146
+ if (!props.questions?.length) {
134
147
  addQuestion({ question: generateQuestionSchema({ required: true }) });
135
148
  } else if (props.questions.length)
136
149
  auditQuestions.value.at(0).activateQuestion();
137
150
  }
138
151
 
152
+ function saveEvaluation() {
153
+ emit('save:evaluation');
154
+ }
155
+
156
+ function cancelEvaluation() {
157
+ emit('cancel:evaluation');
158
+ }
159
+
139
160
  // https://my.webitel.com/browse/WTEL-3451, https://my.webitel.com/browse/WTEL-3436
140
161
  // at new question added, activate this question
141
162
  async function atQuestionAdded() {
@@ -151,7 +172,7 @@ async function atQuestionAdded() {
151
172
  isQuestionAdded.index = null;
152
173
  }
153
174
 
154
- const sortableWrapper = ref(null);
175
+ const sortableWrapper = useTemplateRef('sortableWrapper');
155
176
 
156
177
  const { reloadSortable } = useDestroyableSortable(sortableWrapper, {
157
178
  handle: '.audit-form-question-read__drag-icon',
@@ -166,7 +187,7 @@ const { reloadSortable } = useDestroyableSortable(sortableWrapper, {
166
187
  watch(v$, () =>
167
188
  emit('update:validation', { invalid: isInvalidForm.value, v$: v$.value }),
168
189
  );
169
- watchEffect(initResult);
190
+
170
191
  watch(
171
192
  () => props.questions,
172
193
  () => {
@@ -176,7 +197,11 @@ watch(
176
197
  );
177
198
 
178
199
  onMounted(() => {
179
- initQuestions();
200
+ if (props.mode === AuditFormMode.Create) {
201
+ initQuestions();
202
+ } else if (props.mode === AuditFormMode.Fill) {
203
+ initAnswers();
204
+ }
180
205
  });
181
206
  </script>
182
207
 
@@ -190,8 +215,4 @@ onMounted(() => {
190
215
  display: contents;
191
216
  }
192
217
  }
193
-
194
- .audit-form__add-button {
195
- align-self: flex-end;
196
- }
197
218
  </style>
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <footer class="audit-form-footer">
3
+ <template
4
+ v-if="mode === 'fill'"
5
+ >
6
+ <wt-button
7
+ :disabled="props.invalid"
8
+ @click="emit('fill:save')"
9
+ >
10
+ {{ t('reusable.save') }}
11
+ </wt-button>
12
+ <wt-button
13
+ color="secondary"
14
+ @click="emit('fill:cancel')"
15
+ >
16
+ {{ t('reusable.cancel') }}
17
+ </wt-button>
18
+ </template>
19
+
20
+
21
+ <wt-button
22
+ v-else-if="mode === 'create' && !readonly"
23
+ class="audit-form-footer-create-question-action"
24
+ :disabled="props.invalid"
25
+ @click="emit('create:add')"
26
+ >
27
+ {{ t('webitelUI.auditForm.addQuestion') }}
28
+ </wt-button>
29
+ </footer>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import { inject } from "vue";
34
+ import { useI18n } from "vue-i18n";
35
+
36
+ import WtButton from "../../../../../components/wt-button/wt-button.vue";
37
+
38
+ const mode = inject('mode');
39
+ const readonly = inject('readonly');
40
+
41
+ const props = defineProps<{
42
+ invalid: boolean;
43
+ }>();
44
+
45
+ const emit = defineEmits<{
46
+ 'fill:save': [];
47
+ 'fill:cancel': [];
48
+ 'create:add': [];
49
+ }>();
50
+
51
+ const { t } = useI18n();
52
+
53
+ </script>
54
+
55
+ <style scoped lang="scss">
56
+ .audit-form-footer {
57
+ display: flex;
58
+ justify-content: center;
59
+ gap: var(--spacing-sm);
60
+ }
61
+
62
+ .audit-form-footer-create-question-action {
63
+ align-self: flex-end;
64
+ }
65
+ </style>
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <section class="audit-form-answer-editing-info">
3
+ <header class="audit-form-answer-editing-info-header">
4
+ <span>{{ t('reusable.updatedBy') }}</span>
5
+ <span>{{ answer.updatedBy.name }}</span>
6
+ <span>{{ updateTime }}</span>
7
+ </header>
8
+ <p
9
+ v-if="initialComment"
10
+ ref="answer-editing-comment"
11
+ class="audit-form-answer-editing-info-comment"
12
+ :class="{
13
+ 'audit-form-answer-editing-info-comment--collapsed': collapsed,
14
+ }"
15
+ >
16
+ {{ initialComment }}
17
+ </p>
18
+ <footer
19
+ v-if="collapsed && isCommentClamped"
20
+ class="audit-form-answer-editing-info-footer"
21
+ >
22
+ <button
23
+ class="audit-form-answer-editing-info-expand-action"
24
+ type="button"
25
+ @click="collapsed = false"
26
+ >
27
+ {{ t('reusable.more') }}
28
+ </button>
29
+ </footer>
30
+ </section>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ import {computed, ref, useTemplateRef } from "vue";
35
+ import { useI18n } from 'vue-i18n';
36
+ import {EngineQuestionAnswer} from "webitel-sdk";
37
+
38
+ const props = withDefaults(defineProps<{
39
+ answer: EngineQuestionAnswer;
40
+ collapsible?: boolean;
41
+ }>(), {
42
+ collapsible: false,
43
+ });
44
+
45
+ const commentElRef = useTemplateRef('answer-editing-comment');
46
+
47
+ const collapsed = ref(props.collapsible);
48
+
49
+ const { t } = useI18n();
50
+
51
+ const initialComment = props.answer.comment; /* prevent editing-info change if comment is changing */
52
+
53
+ const updateTime = computed(() => {
54
+ return new Date(+props.answer.updatedAt).toLocaleString();
55
+ });
56
+
57
+ const isCommentClamped = props.collapsible && computed(() => {
58
+ // https://stackoverflow.com/a/67455839
59
+ return commentElRef.value?.clientHeight !== commentElRef.value?.scrollHeight;
60
+ });
61
+
62
+ </script>
63
+
64
+ <style scoped lang="scss">
65
+ @use '@webitel/styleguide/typography' as *;
66
+
67
+ .audit-form-answer-editing-info {
68
+ display: flex;
69
+ flex-direction: column;
70
+ gap: var(--spacing-xs);
71
+ }
72
+
73
+ .audit-form-answer-editing-info-header {
74
+ @extend %typo-caption;
75
+ display: flex;
76
+ gap: var(--spacing-xs);
77
+ }
78
+
79
+ .audit-form-answer-editing-info-comment {
80
+ @extend %typo-body-1;
81
+
82
+ &--collapsed {
83
+ line-clamp: 2;
84
+ -webkit-line-clamp: 2; /* coz autoprefixer wont write "-webkit" for us https://github.com/postcss/autoprefixer/issues/1322 */
85
+ -webkit-box-orient: vertical; /* is needed for line-clamp */
86
+ display: -webkit-box;
87
+ overflow: hidden;
88
+ }
89
+ }
90
+
91
+ .audit-form-answer-editing-info-footer {
92
+ display: flex;
93
+ justify-content: flex-end;
94
+ }
95
+
96
+ .audit-form-answer-editing-info-expand-action {
97
+ @extend %typo-body-1;
98
+ color: var(--text-link-color);
99
+ cursor: pointer;
100
+ transition: var(--transition);
101
+
102
+ &:hover {
103
+ text-decoration: underline;
104
+ }
105
+ }
106
+ </style>
@@ -48,7 +48,7 @@ const props = defineProps({
48
48
  },
49
49
  });
50
50
 
51
- const emit = defineEmits(['change:option', 'delete']);
51
+ const emit = defineEmits(['update:option', 'delete']);
52
52
 
53
53
  // is needed for useVuelidate, because props.question/props.result isn't reactive
54
54
  const { option } = toRefs(props);
@@ -5,7 +5,7 @@
5
5
  class="audit-form-question-options-write"
6
6
  >
7
7
  <options-write-row
8
- v-for="(option, key) of question.options"
8
+ v-for="(option, key) of questionModel.options"
9
9
  :key="key"
10
10
  :first="key === 0"
11
11
  :option="option"
@@ -26,56 +26,62 @@
26
26
  class="audit-form-question-options-read"
27
27
  >
28
28
  <wt-radio
29
- v-for="opt of question.options"
29
+ v-for="opt of questionModel.options"
30
30
  :key="opt.score + opt.name"
31
31
  :label="opt.name"
32
- :selected="result"
33
- :value="opt"
34
- @input="emit('change:result', $event)"
32
+ :selected="answerModel?.score"
33
+ :value="opt.score"
34
+ @input="updateAnswer"
35
35
  />
36
36
  </div>
37
37
  <div v-else>Unknown mode: {{ mode }}</div>
38
38
  </article>
39
39
  </template>
40
40
 
41
- <script setup>
42
- import WtButton from '../../../../../components/wt-button/wt-button.vue';
43
- import WtRadio from '../../../../../components/wt-radio/wt-radio.vue';
41
+ <script lang="ts" setup>
42
+ import {EngineQuestion, EngineQuestionAnswer} from "webitel-sdk";
43
+
44
+ import { WtButton, WtRadio } from '../../../../../components';
44
45
  import updateObject from '../../../../../scripts/updateObject.js';
45
46
  import { generateOption } from '../../../schemas/AuditFormQuestionOptionsSchema.js';
46
47
  import OptionsWriteRow from './audit-form-question-options-write-row.vue';
47
48
 
48
- const props = defineProps({
49
- question: {
50
- type: Object,
51
- required: true,
52
- },
53
- result: {
54
- type: Object,
55
- },
56
- mode: {
57
- // options: ['read', 'write']
58
- type: String,
59
- default: 'read',
60
- },
61
- });
49
+ const questionModel = defineModel<EngineQuestion>('question');
50
+ const answerModel = defineModel<EngineQuestionAnswer | null>('answer');
62
51
 
63
- const emit = defineEmits(['change:question', 'change:result']);
52
+ const props = defineProps<{
53
+ /**
54
+ * question mode, NOT audit form mode
55
+ */
56
+ mode: 'read' | 'write';
57
+ }>();
64
58
 
65
59
  function updateQuestion({ path, value }) {
66
- emit('change:question', updateObject({ obj: props.question, path, value }));
60
+ questionModel.value = updateObject({
61
+ obj: questionModel.value,
62
+ path,
63
+ value,
64
+ });
67
65
  }
68
66
 
69
67
  function addQuestionOption() {
70
- const options = [...props.question.options, generateOption()];
68
+ const options = [...questionModel.value.options, generateOption()];
71
69
  return updateQuestion({ path: 'options', value: options });
72
70
  }
73
71
 
74
72
  function deleteQuestionOption({ key }) {
75
- const options = [...props.question.options];
73
+ const options = [...questionModel.value.options];
76
74
  options.splice(key, 1);
77
75
  return updateQuestion({ path: 'options', value: options });
78
76
  }
77
+
78
+ function updateAnswer(score) {
79
+ answerModel.value = answerModel.value ? {
80
+ ...answerModel.value,
81
+ score,
82
+ } : { score };
83
+ }
84
+
79
85
  </script>
80
86
 
81
87
  <style lang="scss" scoped>
@@ -42,6 +42,8 @@ export const ScopeClass = {
42
42
  ChatBots: 'chat_bots',
43
43
  Cases: 'cases',
44
44
  CaseComments: 'case_comments',
45
+ AuditForm: 'cc_audit_form',
46
+ AuditRating: 'rating',
45
47
  } as const;
46
48
 
47
49
  export type ScopeClass = (typeof ScopeClass)[keyof typeof ScopeClass];
@@ -65,6 +65,8 @@ export const mapScopeClassToWtObjects: Record<ScopeClass, WtObject[]> = {
65
65
  [ScopeClass.ChatBots]: [WtObject.ChatBot], // routing cht_gateway
66
66
  [ScopeClass.Cases]: [WtObject.Case], // CRM
67
67
  [ScopeClass.CaseComments]: [WtObject.CaseComment],
68
+ [ScopeClass.AuditForm]: [WtObject.AuditForm],
69
+ [ScopeClass.AuditRating]: [WtObject.AuditRating],
68
70
  };
69
71
 
70
72
  export const mapScopeClassAccessTokenToCrudAction = {
@@ -1,12 +0,0 @@
1
- declare const _default: import("vue").DefineComponent<{}, {
2
- $emit: (event: "change:result" | "change:question", ...args: any[]) => void;
3
- mode: string;
4
- question: Record<string, any>;
5
- result?: Record<string, any>;
6
- $props: {
7
- readonly mode?: string;
8
- readonly question?: Record<string, any>;
9
- readonly result?: Record<string, any>;
10
- };
11
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
12
- export default _default;