@windward/core 0.0.1

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 (113) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintrc.js +11 -0
  3. package/.prettierrc +4 -0
  4. package/README.md +86 -0
  5. package/babel.config.js +1 -0
  6. package/components/Content/Blocks/Accordion.vue +133 -0
  7. package/components/Content/Blocks/ClickableIcons.vue +98 -0
  8. package/components/Content/Blocks/Feedback.vue +323 -0
  9. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.vue +95 -0
  10. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.vue +45 -0
  11. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.vue +55 -0
  12. package/components/Content/Blocks/Image.vue +116 -0
  13. package/components/Content/Blocks/Math.vue +87 -0
  14. package/components/Content/Blocks/Tab.vue +89 -0
  15. package/components/Content/Blocks/Table.vue +68 -0
  16. package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +157 -0
  17. package/components/Content/Blocks/UserUpload/ManageDataTableUserFiles.vue +68 -0
  18. package/components/Content/Blocks/UserUpload.vue +240 -0
  19. package/components/Content/Blocks/Video.vue +245 -0
  20. package/components/Navigation/Items/CourseGlossaryToolNav.vue +39 -0
  21. package/components/Navigation/Items/GlossaryNav.vue +32 -0
  22. package/components/Navigation/Items/UserUploadNav.vue +35 -0
  23. package/components/Settings/AccordionSettings.vue +153 -0
  24. package/components/Settings/ClickableIconsSettings.vue +189 -0
  25. package/components/Settings/FeedbackSettings.vue +92 -0
  26. package/components/Settings/ImageSettings.vue +124 -0
  27. package/components/Settings/MathSettings.vue +81 -0
  28. package/components/Settings/TabSettings.vue +139 -0
  29. package/components/Settings/TextEditorSettings.vue +249 -0
  30. package/components/Settings/UserUploadSettings.vue +151 -0
  31. package/components/Settings/VideoSettings.vue +699 -0
  32. package/components/utils/ContentViewer.vue +69 -0
  33. package/components/utils/MathExpressionEditor.vue +294 -0
  34. package/components/utils/MathLiveWrapper.vue +86 -0
  35. package/components/utils/TinyMCEWrapper.vue +103 -0
  36. package/components/utils/glossary/CourseGlossary.vue +284 -0
  37. package/components/utils/glossary/CourseGlossaryForm.vue +106 -0
  38. package/components/utils/glossary/GlossaryToolTip.vue +87 -0
  39. package/config/tinymce.config.js +136 -0
  40. package/helpers/GlossaryHelper.ts +137 -0
  41. package/helpers/GlossaryTerm.ts +31 -0
  42. package/helpers/MathHelper.ts +236 -0
  43. package/helpers/tinymce/plugin.ts +83 -0
  44. package/i18n/en-US/components/content/blocks/feedback.ts +28 -0
  45. package/i18n/en-US/components/content/blocks/image.ts +5 -0
  46. package/i18n/en-US/components/content/blocks/index.ts +15 -0
  47. package/i18n/en-US/components/content/blocks/tab.ts +4 -0
  48. package/i18n/en-US/components/content/blocks/table.ts +4 -0
  49. package/i18n/en-US/components/content/blocks/user_upload.ts +12 -0
  50. package/i18n/en-US/components/content/blocks/video.ts +53 -0
  51. package/i18n/en-US/components/content/index.ts +5 -0
  52. package/i18n/en-US/components/index.ts +12 -0
  53. package/i18n/en-US/components/navigation/image.ts +4 -0
  54. package/i18n/en-US/components/navigation/index.ts +7 -0
  55. package/i18n/en-US/components/navigation/user_upload.ts +3 -0
  56. package/i18n/en-US/components/settings/clickable_icon.ts +9 -0
  57. package/i18n/en-US/components/settings/image.ts +1 -0
  58. package/i18n/en-US/components/settings/index.ts +13 -0
  59. package/i18n/en-US/components/settings/text_editor.ts +7 -0
  60. package/i18n/en-US/components/settings/user_upload.ts +11 -0
  61. package/i18n/en-US/components/settings/video.ts +13 -0
  62. package/i18n/en-US/components/utils/index.ts +5 -0
  63. package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +7 -0
  64. package/i18n/en-US/index.ts +16 -0
  65. package/i18n/en-US/modules/index.ts +5 -0
  66. package/i18n/en-US/pages/glossary.ts +7 -0
  67. package/i18n/en-US/pages/index.ts +7 -0
  68. package/i18n/en-US/pages/user_upload.ts +3 -0
  69. package/i18n/en-US/shared/content_blocks.ts +20 -0
  70. package/i18n/en-US/shared/index.ts +11 -0
  71. package/i18n/en-US/shared/menu.ts +3 -0
  72. package/i18n/en-US/shared/permission.ts +15 -0
  73. package/i18n/en-US/shared/settings.ts +15 -0
  74. package/index.js +15 -0
  75. package/jest.config.js +15 -0
  76. package/models/SurveyResult.ts +8 -0
  77. package/models/SurveyTemplate.ts +8 -0
  78. package/models/UserFileAsset.ts +12 -0
  79. package/package.json +48 -0
  80. package/pages/glossary.vue +31 -0
  81. package/pages/userUpload.vue +204 -0
  82. package/plugin.js +299 -0
  83. package/test/Components/Content/Blocks/Accordion.spec.js +21 -0
  84. package/test/Components/Content/Blocks/ClickableIcons.spec.js +21 -0
  85. package/test/Components/Content/Blocks/Feedback.spec.js +31 -0
  86. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.spec.js +23 -0
  87. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.spec.js +23 -0
  88. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.spec.js +23 -0
  89. package/test/Components/Content/Blocks/Image.spec.js +34 -0
  90. package/test/Components/Content/Blocks/Math.spec.js +34 -0
  91. package/test/Components/Content/Blocks/Tab.spec.js +20 -0
  92. package/test/Components/Content/Blocks/Table.spec.js +21 -0
  93. package/test/Components/Content/Blocks/UserUpload.spec.js +25 -0
  94. package/test/Components/Content/Blocks/Video.spec.js +112 -0
  95. package/test/Components/Settings/AccordionSettings.spec.js +45 -0
  96. package/test/Components/Settings/ClickableIconsSettings.spec.js +20 -0
  97. package/test/Components/Settings/FeedbackSettings.spec.js +20 -0
  98. package/test/Components/Settings/ImageSettings.spec.js +20 -0
  99. package/test/Components/Settings/MathSettings.spec.js +20 -0
  100. package/test/Components/Settings/TabSettings.spec.js +45 -0
  101. package/test/Components/Settings/UserUploadSettings.spec.js +20 -0
  102. package/test/__mocks__/componentsMock.js +83 -0
  103. package/test/__mocks__/contentBlockMock.js +119 -0
  104. package/test/__mocks__/contentSettingsMock.js +54 -0
  105. package/test/__mocks__/fileMock.js +1 -0
  106. package/test/__mocks__/helpersMock.js +53 -0
  107. package/test/__mocks__/modelMock.js +82 -0
  108. package/test/__mocks__/styleMock.js +1 -0
  109. package/test/helpers/GlossaryHelper.spec.js +227 -0
  110. package/test/helpers/MathHelper.spec.js +128 -0
  111. package/test/mocks.js +140 -0
  112. package/tsconfig.json +15 -0
  113. package/utils/index.js +18 -0
package/.editorconfig ADDED
@@ -0,0 +1,13 @@
1
+ # editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = space
6
+ indent_size = 4
7
+ end_of_line = lf
8
+ charset = utf-8
9
+ trim_trailing_whitespace = true
10
+ insert_final_newline = true
11
+
12
+ [*.md]
13
+ trim_trailing_whitespace = false
package/.eslintrc.js ADDED
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ root: true,
3
+ env: {
4
+ browser: true,
5
+ node: true,
6
+ },
7
+ extends: ['plugin:nuxt/recommended', 'plugin:prettier/recommended'],
8
+ plugins: [],
9
+ // add your custom rules here
10
+ rules: {},
11
+ }
package/.prettierrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "semi": false,
3
+ "singleQuote": true
4
+ }
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Windward UI Plugin Library
2
+
3
+ Windward UI plugin library implements content components add amplify windward's ability to simplify editor's workflow
4
+
5
+ ## Getting Started Locally with npm
6
+
7
+ **Software Requirements**:
8
+
9
+ - Node v16+, npm v8+
10
+
11
+ Clone the windward-ui-plugin-core repository
12
+
13
+ ```
14
+ $ cd windward-ui-plugin-core
15
+ $ npm install
16
+ $ npm link
17
+ ```
18
+
19
+ Clone the windward-ui repository and follow README to setup
20
+
21
+ Link to Windward ui repo
22
+
23
+ ```
24
+ $ cd windward-ui
25
+ $ npm install
26
+ $ cd node_modules
27
+ $ npm link @mindedge/windward-ui-plugin-core
28
+ $ npm run dev
29
+ ```
30
+
31
+ Your Repos are linked at this point and you are ready to start developing windward plugins
32
+
33
+ ## Code Styles
34
+
35
+ ### `import x from 'path'`
36
+
37
+ 1. For files in **this repository** use `./` or `../`
38
+
39
+ eg:
40
+
41
+ ```
42
+ import UserFileAsset from './models/UserFileAsset'
43
+ ```
44
+
45
+ 2. For files in **the windward-core repository** use `~/`
46
+
47
+ eg:
48
+
49
+ ```
50
+ import ContentBlockAsset from '~/components/Content/ContentBlockAsset.vue'
51
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
52
+ ```
53
+
54
+ 3. When **unit testing only** and for files in **this repository** use `./` or `../` or `@/`
55
+
56
+ For ease of use the `@/` alias is available to navigate to the root of this project.
57
+ The `@/` alias is synonymous to the `~/` however it's bound to THIS repository instead of the windward-core repository.
58
+
59
+ eg:
60
+
61
+ ```
62
+ import UserUpload from '@/components/Content/Blocks/UserUpload.vue'
63
+ ```
64
+ 4. To add a page use the below format in your `pages: []` in `plugin.js`
65
+ ```javascript
66
+ {
67
+ // The name to be used by the vue router. This must be unique
68
+ name: 'PluginUserUploadPage',
69
+ // The vue router path. Use :course to allow the page to be accessed while in a course
70
+ path: '/course/:course/user-uploads',
71
+ // The template to use for the page
72
+ template: UserUploadPage,
73
+ }
74
+ ```
75
+
76
+ ## Code of Conduct
77
+
78
+ In order to ensure that the Windward community is welcoming to all we follow the same code of conduct as [Laravel](https://laravel.com/docs/contributions#code-of-conduct).
79
+
80
+ ## Security Vulnerabilities
81
+
82
+ If you discover a security vulnerability within Windward, please create a ticket on this repository and tag it as "security"
83
+
84
+ ## License
85
+
86
+ Windward LMS is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
@@ -0,0 +1 @@
1
+ module.exports = {presets: ['@babel/preset-env']}
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <div>
3
+ <v-expansion-panels
4
+ flat
5
+ accordion
6
+ focusable
7
+ id="expansion-panel-setting"
8
+ >
9
+ <v-expansion-panel
10
+ v-for="(item, itemIndex) in block.metadata.config.items"
11
+ :key="itemIndex"
12
+ >
13
+ <v-expansion-panel-header class="header" color="primary">
14
+ {{
15
+ item.header === '' || item.header === null
16
+ ? 'Item ' + (itemIndex + 1)
17
+ : item.header
18
+ }}
19
+ </v-expansion-panel-header>
20
+ <v-expansion-panel-content
21
+ class="body"
22
+ :key="expansionPanelKey"
23
+ >
24
+ <v-container>
25
+ <TextViewer
26
+ v-if="!item.expand"
27
+ v-model="item.content"
28
+ text-viewer
29
+ ></TextViewer>
30
+ <TextEditor
31
+ v-if="!render && item.expand"
32
+ v-model="
33
+ block.metadata.config.items[itemIndex].content
34
+ "
35
+ ></TextEditor>
36
+ </v-container>
37
+ </v-expansion-panel-content>
38
+ </v-expansion-panel>
39
+ </v-expansion-panels>
40
+ </div>
41
+ </template>
42
+
43
+ <script>
44
+ import _ from 'lodash'
45
+ import Crypto from '~/helpers/Crypto'
46
+ import TextEditor from '~/components/Text/TextEditor.vue'
47
+ import TextViewer from '~/components/Text/TextViewer.vue'
48
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
49
+
50
+ export default {
51
+ name: 'ContentBlockAccordion',
52
+ components: {
53
+ TextEditor,
54
+ TextViewer,
55
+ },
56
+ extends: BaseContentBlock,
57
+ beforeMount() {
58
+ if (_.isEmpty(this.block)) {
59
+ this.block = {}
60
+ }
61
+ if (_.isEmpty(this.block.metadata)) {
62
+ this.block.metadata = {}
63
+ }
64
+ if (_.isEmpty(this.block.metadata.config)) {
65
+ this.block.metadata.config = {}
66
+ }
67
+ if (_.isEmpty(this.block.metadata.config.items)) {
68
+ const defaultObject = {
69
+ header: '',
70
+ expand: false,
71
+ content: '',
72
+ }
73
+ this.block.metadata.config.items = []
74
+ this.block.metadata.config.items.push(defaultObject)
75
+ }
76
+ this.block.body = 'accordion'
77
+ },
78
+ data() {
79
+ return {
80
+ expansionPanelKey: '0',
81
+ editingInContentItem: false,
82
+ }
83
+ },
84
+ watch: {
85
+ value(newValue) {
86
+ const length = newValue.metadata.config.items.length
87
+ let counter = 0
88
+ newValue.metadata.config.items.forEach((element) => {
89
+ if (
90
+ element.expand === true &&
91
+ this.editingInContentItem !== true
92
+ ) {
93
+ this.editingInContentItem = true
94
+ this.expansionPanelKey = Crypto.id()
95
+ }
96
+ if (element.expand === false) {
97
+ counter = counter + 1
98
+ }
99
+ if (counter === length) {
100
+ this.editingInContentItem = false
101
+ }
102
+ })
103
+ },
104
+ render() {
105
+ if (this.render === true) {
106
+ this.block.metadata.config.items.forEach((element) => {
107
+ element.expand = false
108
+ })
109
+ }
110
+ },
111
+ },
112
+ methods: {
113
+ async onBeforeSave() {
114
+ this.block.metadata.config.items.forEach((element) => {
115
+ element.expand = false
116
+ })
117
+ },
118
+ },
119
+ }
120
+ </script>
121
+
122
+ <style scoped>
123
+ .header {
124
+ color: white;
125
+ margin-top: 1px;
126
+ }
127
+ .body {
128
+ background: #f6f9f9;
129
+ }
130
+ #expansion-panel-setting {
131
+ z-index: 0;
132
+ }
133
+ </style>
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <div>
3
+ <v-container>
4
+ <v-row no-gutters>
5
+ <h2>{{ block.metadata.config.title }}</h2>
6
+ </v-row>
7
+ <v-row no-gutters>
8
+ <h3>{{ block.metadata.config.description }}</h3>
9
+ </v-row>
10
+ <v-row no-gutters>
11
+ <p>
12
+ {{
13
+ $t(
14
+ 'windward.core.components.settings.clickable_icon.information'
15
+ )
16
+ }}
17
+ </p>
18
+ </v-row>
19
+ </v-container>
20
+ <v-container>
21
+ <v-row
22
+ v-for="(item, itemIndex) in block.metadata.config.items"
23
+ :key="itemIndex"
24
+ no-gutters
25
+ >
26
+ <v-col cols="2">
27
+ <v-btn
28
+ class="pt-8 pb-8 outlined-button mb-4"
29
+ :color="itemColor(item.color)"
30
+ x-large
31
+ outlined
32
+ @click="item.active = !item.active"
33
+ >
34
+ <v-icon>{{ item.icon }}</v-icon>
35
+ </v-btn>
36
+ </v-col>
37
+ <v-col cols="10" v-if="item.active">
38
+ <h4>{{ item.title }}</h4>
39
+ <TextViewer v-model="item.body"></TextViewer>
40
+ </v-col>
41
+ </v-row>
42
+ </v-container>
43
+ </div>
44
+ </template>
45
+ <script>
46
+ import _ from 'lodash'
47
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
48
+ import TextViewer from '~/components/Text/TextViewer'
49
+
50
+ export default {
51
+ name: 'ClickableIcons',
52
+ components: {
53
+ TextViewer,
54
+ },
55
+ extends: BaseContentBlock,
56
+ computed: {
57
+ itemColor() {
58
+ return (v) => {
59
+ if (_.isObject(v)) {
60
+ return _.get(v, 'class', '')
61
+ } else {
62
+ return v
63
+ }
64
+ }
65
+ },
66
+ },
67
+ beforeMount() {
68
+ // Apply the default config
69
+ this.block.body = 'clickable icons'
70
+ if (_.isEmpty(this.block.metadata.config.items)) {
71
+ this.block.metadata.config.items = []
72
+ }
73
+ if (_.isEmpty(this.block.metadata.config.title)) {
74
+ this.block.metadata.config.title = ''
75
+ }
76
+ if (_.isEmpty(this.block.metadata.config.description)) {
77
+ this.block.metadata.config.description = ''
78
+ }
79
+ if (this.block.metadata.config.items) {
80
+ this.block.metadata.config.items.forEach((element) => {
81
+ element.active = false
82
+ })
83
+ }
84
+ },
85
+ data() {
86
+ return {
87
+ api_key: process.env.TINY_MCE_API_KEY,
88
+ title: 'Title',
89
+ displayText: false,
90
+ }
91
+ },
92
+ }
93
+ </script>
94
+ <style scoped>
95
+ .outlined-button {
96
+ border-width: 4px;
97
+ }
98
+ </style>
@@ -0,0 +1,323 @@
1
+ <template>
2
+ <v-container>
3
+ <v-container v-if="!doesFeedbackExist">
4
+ <v-row>
5
+ <h2>
6
+ {{
7
+ block.metadata.config.definition.name
8
+ ? block.metadata.config.definition.name
9
+ : $t(
10
+ 'plugin.core.components.content.blocks.feedback.feedback'
11
+ )
12
+ }}
13
+ </h2>
14
+ </v-row>
15
+ <v-row class="pt-2">
16
+ <p>
17
+ {{
18
+ $t(
19
+ 'plugin.core.components.content.blocks.feedback.description'
20
+ )
21
+ }}
22
+ </p>
23
+ </v-row>
24
+ </v-container>
25
+ <v-container v-if="doesFeedbackExist">
26
+ <v-row>
27
+ <h2>
28
+ {{ block.metadata.config.definition.name }}
29
+ </h2>
30
+ </v-row>
31
+ <v-row class="pt-2">
32
+ <p>
33
+ {{
34
+ $t(
35
+ 'plugin.core.components.content.blocks.feedback.sent_feedback'
36
+ )
37
+ }}
38
+ </p>
39
+ </v-row>
40
+ </v-container>
41
+ <v-divider class="mt-4 mb-6"></v-divider>
42
+ <v-form
43
+ ref="form"
44
+ v-model="valid"
45
+ lazy-validation
46
+ v-if="!doesFeedbackExist"
47
+ >
48
+ <div v-if="block.metadata.config.definition.metadata">
49
+ <v-container
50
+ v-for="(question, questionIndex) in chosenPreset"
51
+ :key="questionIndex"
52
+ fluid
53
+ >
54
+ <v-container
55
+ v-if="question.type === 'likert'"
56
+ class="pa-0 ma-0"
57
+ >
58
+ <FeedbackQuestionLikert
59
+ v-model="chosenPreset[questionIndex]"
60
+ ></FeedbackQuestionLikert>
61
+ </v-container>
62
+ <v-container
63
+ v-if="question.type === 'true_false'"
64
+ class="pa-0 ma-0"
65
+ >
66
+ <FeedbackQuestionTrueFalse
67
+ v-model="chosenPreset[questionIndex]"
68
+ ></FeedbackQuestionTrueFalse>
69
+ </v-container>
70
+ <v-container
71
+ v-if="question.type === 'open_response'"
72
+ class="pa-0 ma-0"
73
+ >
74
+ <FeedbackQuestionOpenResponse
75
+ v-model="chosenPreset[questionIndex]"
76
+ ></FeedbackQuestionOpenResponse>
77
+ </v-container>
78
+ </v-container>
79
+ </div>
80
+ </v-form>
81
+ <v-row
82
+ class="d-flex justify-end align-center"
83
+ v-if="render && !doesFeedbackExist"
84
+ >
85
+ <v-btn
86
+ color="error"
87
+ class="text-center mt-4 mr-2"
88
+ @click="onReset()"
89
+ >{{
90
+ $t('plugin.core.components.content.blocks.feedback.reset')
91
+ }}</v-btn
92
+ >
93
+ <v-btn color="success" class="text-center mt-4" @click="validate">{{
94
+ $t('plugin.core.components.content.blocks.feedback.save')
95
+ }}</v-btn>
96
+ </v-row>
97
+ <v-form
98
+ ref="form"
99
+ v-model="valid"
100
+ disabled
101
+ lazy-validation
102
+ v-if="doesFeedbackExist"
103
+ >
104
+ <v-container
105
+ v-for="(
106
+ question, questionIndex
107
+ ) in existingFeedback.survey_question_answers"
108
+ :key="questionIndex"
109
+ fluid
110
+ >
111
+ <div v-if="question.type === 'likert'">
112
+ <FeedbackQuestionLikert
113
+ v-model="
114
+ existingFeedback.survey_question_answers[
115
+ questionIndex
116
+ ]
117
+ "
118
+ ></FeedbackQuestionLikert>
119
+ </div>
120
+ <div v-if="question.type === 'true_false'">
121
+ <FeedbackQuestionTrueFalse
122
+ v-model="
123
+ existingFeedback.survey_question_answers[
124
+ questionIndex
125
+ ]
126
+ "
127
+ ></FeedbackQuestionTrueFalse>
128
+ </div>
129
+ <div v-if="question.type === 'open_response'">
130
+ <FeedbackQuestionOpenResponse
131
+ v-model="
132
+ existingFeedback.survey_question_answers[
133
+ questionIndex
134
+ ]
135
+ "
136
+ :feedbackExist="true"
137
+ ></FeedbackQuestionOpenResponse>
138
+ </div>
139
+ </v-container>
140
+ </v-form>
141
+ </v-container>
142
+ </template>
143
+
144
+ <script>
145
+ import _ from 'lodash'
146
+ import { mapGetters } from 'vuex'
147
+ import FeedbackQuestionLikert from './FeedbackTemplates/FeedbackQuestionLikert.vue'
148
+ import FeedbackQuestionOpenResponse from './FeedbackTemplates/FeedbackQuestionOpenResponse.vue'
149
+ import FeedbackQuestionTrueFalse from './FeedbackTemplates/FeedbackQuestionTrueFalse.vue'
150
+ import Uuid from '~/helpers/Uuid'
151
+
152
+ import SurveyResult from '../../../models/SurveyResult'
153
+ import Enrollment from '~/models/Enrollment'
154
+ import ContentBlock from '~/models/ContentBlock'
155
+ import Crypto from '~/helpers/Crypto'
156
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
157
+
158
+ export default {
159
+ name: 'Feedback',
160
+ components: {
161
+ FeedbackQuestionLikert,
162
+ FeedbackQuestionOpenResponse,
163
+ FeedbackQuestionTrueFalse,
164
+ },
165
+ extends: BaseContentBlock,
166
+ beforeMount() {
167
+ if (_.isEmpty(this.block)) {
168
+ this.block = {}
169
+ }
170
+ if (_.isEmpty(this.block.metadata)) {
171
+ this.block.metadata = {}
172
+ }
173
+ if (_.isEmpty(this.block.metadata.config)) {
174
+ this.block.metadata.config = {}
175
+ }
176
+ if (_.isEmpty(this.block.metadata.config)) {
177
+ this.block.metadata.config = {}
178
+ }
179
+ if (_.isEmpty(this.block.metadata.config.definition)) {
180
+ this.block.metadata.config.definition = {}
181
+ }
182
+ this.block.body = 'feedback'
183
+ },
184
+ data() {
185
+ return {
186
+ saveState: false,
187
+ courseUserId: '',
188
+ valid: true,
189
+ key: '0',
190
+ existingFeedback: '',
191
+ doesFeedbackExist: false,
192
+ templates: '',
193
+ chosenPreset: '',
194
+ studentResponse: '',
195
+ noResponse: this.$t(
196
+ 'plugin.core.components.content.blocks.feedback.no_response_entered'
197
+ ),
198
+ }
199
+ },
200
+ computed: {
201
+ ...mapGetters({
202
+ enrollment: 'enrollment/get',
203
+ course: 'course/get',
204
+ }),
205
+ blockExists() {
206
+ return Uuid.test(this.block.id)
207
+ },
208
+ },
209
+ watch: {
210
+ value(newValue) {
211
+ if (
212
+ newValue.metadata.config.definition &&
213
+ newValue.metadata.config.definition.metadata &&
214
+ newValue.metadata.config.definition.metadata.definition &&
215
+ !this.render
216
+ ) {
217
+ this.setUpBlock()
218
+ }
219
+ },
220
+ },
221
+ mounted() {
222
+ if (this.blockExists) {
223
+ this.courseUserId = this.enrollment.id
224
+ this.onCheckForFeedback()
225
+ if (_.isEmpty(this.chosenPreset)) {
226
+ this.setUpBlock()
227
+ }
228
+ }
229
+ },
230
+ methods: {
231
+ setUpBlock() {
232
+ if (this.block.metadata.config.definition.metadata) {
233
+ this.block.metadata.config.definition.metadata.definition.forEach(
234
+ (element) => {
235
+ element.response = ''
236
+ }
237
+ )
238
+ this.chosenPreset =
239
+ this.block.metadata.config.definition.metadata.definition
240
+ }
241
+ },
242
+ async onCheckForFeedback() {
243
+ if (this.enrollment && this.block.id) {
244
+ this.existingFeedback = await new SurveyResult()
245
+ .for(
246
+ new Enrollment({ id: this.enrollment.id }),
247
+ new ContentBlock({ id: this.block.id })
248
+ )
249
+ .first()
250
+ }
251
+ if (!_.isEmpty(this.existingFeedback)) {
252
+ this.handleExistingFeedback()
253
+ } else {
254
+ this.onReset()
255
+ }
256
+ },
257
+ handleExistingFeedback() {
258
+ this.existingFeedback.survey_question_answers = JSON.parse(
259
+ this.existingFeedback.survey_question_answers
260
+ )
261
+ this.existingFeedback.survey_snapshot = JSON.parse(
262
+ this.existingFeedback.survey_snapshot
263
+ )
264
+ this.doesFeedbackExist = true
265
+ },
266
+ async onSaveFeedback() {
267
+ let version =
268
+ this.block.metadata.config.definition.id +
269
+ this.block.metadata.config.definition.updated_at
270
+ const feedbackData = {
271
+ survey_snapshot:
272
+ this.block.metadata.config.definition.metadata.definition,
273
+ survey_question_answers: this.chosenPreset,
274
+ version: version,
275
+ }
276
+ let feedback = new SurveyResult(feedbackData).for(
277
+ new Enrollment({ id: this.enrollment.id }),
278
+ new ContentBlock({ id: this.block.id })
279
+ )
280
+ try {
281
+ feedback = await feedback.save()
282
+ this.$toast.success(this.$t('shared.forms.saved'))
283
+ this.onCheckForFeedback()
284
+ } catch (e) {
285
+ this.$toast.error(this.$t('shared.forms.errors.unknown'))
286
+ }
287
+ },
288
+ async validate() {
289
+ const valid = await this.$refs.form.validate()
290
+ if (valid) {
291
+ this.onSaveFeedback()
292
+ } else {
293
+ this.$toast.error(
294
+ this.$t(
295
+ 'plugin.core.components.content.blocks.feedback.fill_out'
296
+ )
297
+ )
298
+ }
299
+ },
300
+ onReset() {
301
+ this.existingFeedback = null
302
+ if (this.chosenPreset) {
303
+ this.chosenPreset.forEach((element) => {
304
+ element.response = ''
305
+ })
306
+ }
307
+ this.doesFeedbackExist = false
308
+ this.key = Crypto.id()
309
+ },
310
+ },
311
+ }
312
+ </script>
313
+ <style scoped>
314
+ .question {
315
+ font-weight: bold;
316
+ }
317
+ .radio-group {
318
+ width: 100% !important;
319
+ }
320
+ .likert-radio {
321
+ margin-left: 55px;
322
+ }
323
+ </style>