@windward/core 0.0.5 → 0.0.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.
- package/README.md +2 -1
- package/components/Content/Blocks/ClickableIcons.vue +10 -1
- package/components/Content/Blocks/Feedback.vue +12 -4
- package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.vue +10 -9
- package/components/Content/Blocks/Image.vue +1 -6
- package/components/Content/Blocks/OpenResponse.vue +137 -0
- package/components/Content/Blocks/OpenResponseCollate.vue +158 -0
- package/components/Navigation/Items/AskTheExpert.vue +173 -0
- package/components/Settings/ClickableIconsSettings.vue +10 -5
- package/components/Settings/OpenResponseCollateSettings.vue +170 -0
- package/components/Settings/OpenResponseSettings.vue +81 -0
- package/components/utils/ContentViewer.vue +15 -4
- package/components/utils/FillInBlank/FillInBlankInput.vue +208 -0
- package/components/utils/FillInBlank/FillInTheBlanksManager.vue +98 -0
- package/components/utils/MathExpressionEditor.vue +8 -6
- package/components/utils/TinyMCEWrapper.vue +59 -9
- package/components/utils/assets/tinymce/css/content.scss +9 -0
- package/components/utils/glossary/CourseGlossary.vue +12 -8
- package/components/utils/glossary/CourseGlossaryForm.vue +24 -5
- package/helpers/FillInBlankHelper.ts +55 -0
- package/helpers/GlossaryHelper.ts +4 -4
- package/helpers/tinymce/plugin.ts +99 -4
- package/i18n/en-US/components/content/blocks/feedback.ts +3 -0
- package/i18n/en-US/components/content/blocks/index.ts +4 -0
- package/i18n/en-US/components/content/blocks/open_response.ts +5 -0
- package/i18n/en-US/components/content/blocks/open_response_collate.ts +6 -0
- package/i18n/en-US/components/index.ts +0 -1
- package/i18n/en-US/components/navigation/ask_the_expert.ts +11 -0
- package/i18n/en-US/components/navigation/index.ts +2 -0
- package/i18n/en-US/components/settings/index.ts +5 -1
- package/i18n/en-US/components/settings/open_response.ts +5 -0
- package/i18n/en-US/components/settings/open_response_collate.ts +6 -0
- package/i18n/en-US/components/utils/FillInBlank/FillInBlankInput.ts +13 -0
- package/i18n/en-US/components/utils/FillInBlank/FillInTheBlanksManager.ts +11 -0
- package/i18n/en-US/components/utils/FillInBlank/index.ts +6 -0
- package/i18n/en-US/components/utils/index.ts +2 -1
- package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +1 -0
- package/i18n/en-US/shared/content_blocks.ts +2 -0
- package/i18n/en-US/shared/menu.ts +1 -0
- package/i18n/en-US/shared/settings.ts +3 -1
- package/i18n/es-ES/components/content/blocks/feedback.ts +29 -0
- package/i18n/es-ES/components/content/blocks/image.ts +5 -0
- package/i18n/es-ES/components/content/blocks/index.ts +19 -0
- package/i18n/es-ES/components/content/blocks/open_response.ts +6 -0
- package/i18n/es-ES/components/content/blocks/open_response_collate.ts +6 -0
- package/i18n/es-ES/components/content/blocks/tab.ts +4 -0
- package/i18n/es-ES/components/content/blocks/table.ts +4 -0
- package/i18n/es-ES/components/content/blocks/user_upload.ts +13 -0
- package/i18n/es-ES/components/content/blocks/video.ts +55 -0
- package/i18n/es-ES/components/content/index.ts +5 -0
- package/i18n/es-ES/components/index.ts +12 -0
- package/i18n/es-ES/components/navigation/ask_the_expert.ts +11 -0
- package/i18n/es-ES/components/navigation/image.ts +4 -0
- package/i18n/es-ES/components/navigation/index.ts +7 -0
- package/i18n/es-ES/components/navigation/user_upload.ts +3 -0
- package/i18n/es-ES/components/settings/clickable_icon.ts +10 -0
- package/i18n/es-ES/components/settings/image.ts +1 -0
- package/i18n/es-ES/components/settings/index.ts +17 -0
- package/i18n/es-ES/components/settings/open_response.ts +5 -0
- package/i18n/es-ES/components/settings/open_response_collate.ts +7 -0
- package/i18n/es-ES/components/settings/text_editor.ts +7 -0
- package/i18n/es-ES/components/settings/user_upload.ts +11 -0
- package/i18n/es-ES/components/settings/video.ts +13 -0
- package/i18n/es-ES/components/utils/index.ts +5 -0
- package/i18n/es-ES/components/utils/tiny_mce_wrapper.ts +18 -0
- package/i18n/es-ES/index.ts +16 -0
- package/i18n/es-ES/modules/index.ts +5 -0
- package/i18n/es-ES/pages/glossary.ts +7 -0
- package/i18n/es-ES/pages/index.ts +7 -0
- package/i18n/es-ES/pages/user_upload.ts +3 -0
- package/i18n/es-ES/shared/content_blocks.ts +22 -0
- package/i18n/es-ES/shared/index.ts +11 -0
- package/i18n/es-ES/shared/menu.ts +3 -0
- package/i18n/es-ES/shared/permission.ts +15 -0
- package/i18n/es-ES/shared/settings.ts +19 -0
- package/i18n/sv-SE/components/content/blocks/feedback.ts +29 -0
- package/i18n/sv-SE/components/content/blocks/image.ts +5 -0
- package/i18n/sv-SE/components/content/blocks/index.ts +19 -0
- package/i18n/sv-SE/components/content/blocks/open_response.ts +6 -0
- package/i18n/sv-SE/components/content/blocks/open_response_collate.ts +6 -0
- package/i18n/sv-SE/components/content/blocks/tab.ts +4 -0
- package/i18n/sv-SE/components/content/blocks/table.ts +4 -0
- package/i18n/sv-SE/components/content/blocks/user_upload.ts +13 -0
- package/i18n/sv-SE/components/content/blocks/video.ts +53 -0
- package/i18n/sv-SE/components/content/index.ts +5 -0
- package/i18n/sv-SE/components/index.ts +12 -0
- package/i18n/sv-SE/components/navigation/ask_the_expert.ts +11 -0
- package/i18n/sv-SE/components/navigation/image.ts +4 -0
- package/i18n/sv-SE/components/navigation/index.ts +7 -0
- package/i18n/sv-SE/components/navigation/user_upload.ts +3 -0
- package/i18n/sv-SE/components/settings/clickable_icon.ts +10 -0
- package/i18n/sv-SE/components/settings/image.ts +1 -0
- package/i18n/sv-SE/components/settings/index.ts +17 -0
- package/i18n/sv-SE/components/settings/open_response.ts +5 -0
- package/i18n/sv-SE/components/settings/open_response_collate.ts +6 -0
- package/i18n/sv-SE/components/settings/text_editor.ts +7 -0
- package/i18n/sv-SE/components/settings/user_upload.ts +11 -0
- package/i18n/sv-SE/components/settings/video.ts +13 -0
- package/i18n/sv-SE/components/utils/index.ts +5 -0
- package/i18n/sv-SE/components/utils/tiny_mce_wrapper.ts +18 -0
- package/i18n/sv-SE/index.ts +16 -0
- package/i18n/sv-SE/modules/index.ts +5 -0
- package/i18n/sv-SE/pages/glossary.ts +7 -0
- package/i18n/sv-SE/pages/index.ts +7 -0
- package/i18n/sv-SE/pages/user_upload.ts +3 -0
- package/i18n/sv-SE/shared/content_blocks.ts +22 -0
- package/i18n/sv-SE/shared/index.ts +11 -0
- package/i18n/sv-SE/shared/menu.ts +3 -0
- package/i18n/sv-SE/shared/permission.ts +15 -0
- package/i18n/sv-SE/shared/settings.ts +17 -0
- package/package.json +1 -1
- package/plugin.js +68 -0
- package/test/Components/Content/Blocks/OpenResponse.spec.js +31 -0
- package/test/Components/Content/Blocks/OpenResponseCollate.spec.js +36 -0
- package/test/Components/Settings/OpenResponseCollateSettings.spec.js +20 -0
- package/test/Components/Settings/OpenResponseSettings.spec.js +20 -0
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Link to Windward ui repo
|
|
|
24
24
|
$ cd windward-ui
|
|
25
25
|
$ npm install
|
|
26
26
|
$ cd node_modules
|
|
27
|
-
$ npm link @
|
|
27
|
+
$ npm link @windward/core
|
|
28
28
|
$ npm run dev
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -61,6 +61,7 @@ Your Repos are linked at this point and you are ready to start developing windwa
|
|
|
61
61
|
```
|
|
62
62
|
import UserUpload from '@/components/Content/Blocks/UserUpload.vue'
|
|
63
63
|
```
|
|
64
|
+
|
|
64
65
|
4. To add a page use the below format in your `pages: []` in `plugin.js`
|
|
65
66
|
```javascript
|
|
66
67
|
{
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
outlined
|
|
32
32
|
@click="item.active = !item.active"
|
|
33
33
|
>
|
|
34
|
-
<v-icon>{{ item.icon }}</v-icon>
|
|
34
|
+
<v-icon>{{ onHandleHtmlEntities(item.icon) }}</v-icon>
|
|
35
35
|
</v-btn>
|
|
36
36
|
</v-col>
|
|
37
37
|
<v-col cols="10" v-if="item.active">
|
|
@@ -89,6 +89,15 @@ export default {
|
|
|
89
89
|
displayText: false,
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
|
+
methods: {
|
|
93
|
+
onHandleHtmlEntities(str) {
|
|
94
|
+
let txt = document.createElement('textarea')
|
|
95
|
+
|
|
96
|
+
txt.innerHTML = str
|
|
97
|
+
|
|
98
|
+
return txt.value
|
|
99
|
+
},
|
|
100
|
+
},
|
|
92
101
|
}
|
|
93
102
|
</script>
|
|
94
103
|
<style scoped>
|
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
</h3>
|
|
14
14
|
</v-row>
|
|
15
15
|
<v-row class="pt-2">
|
|
16
|
+
<p>
|
|
17
|
+
{{
|
|
18
|
+
$t(
|
|
19
|
+
'windward.core.components.content.blocks.feedback.likert_directions'
|
|
20
|
+
)
|
|
21
|
+
}}
|
|
22
|
+
</p>
|
|
16
23
|
<p>
|
|
17
24
|
{{
|
|
18
25
|
$t(
|
|
@@ -24,9 +31,9 @@
|
|
|
24
31
|
</v-container>
|
|
25
32
|
<v-container v-if="doesFeedbackExist">
|
|
26
33
|
<v-row>
|
|
27
|
-
<
|
|
28
|
-
{{
|
|
29
|
-
</
|
|
34
|
+
<h2>
|
|
35
|
+
{{ existingFeedback.survey_type }}
|
|
36
|
+
</h2>
|
|
30
37
|
</v-row>
|
|
31
38
|
<v-row class="pt-2">
|
|
32
39
|
<p>
|
|
@@ -187,8 +194,8 @@ export default {
|
|
|
187
194
|
courseUserId: '',
|
|
188
195
|
valid: true,
|
|
189
196
|
key: '0',
|
|
190
|
-
existingFeedback: '',
|
|
191
197
|
doesFeedbackExist: false,
|
|
198
|
+
existingFeedback: '',
|
|
192
199
|
templates: '',
|
|
193
200
|
chosenPreset: '',
|
|
194
201
|
studentResponse: '',
|
|
@@ -271,6 +278,7 @@ export default {
|
|
|
271
278
|
survey_snapshot:
|
|
272
279
|
this.block.metadata.config.definition.metadata.definition,
|
|
273
280
|
survey_question_answers: this.chosenPreset,
|
|
281
|
+
survey_type: this.block.metadata.config.definition.name,
|
|
274
282
|
version: version,
|
|
275
283
|
}
|
|
276
284
|
let feedback = new SurveyResult(feedbackData).for(
|
|
@@ -4,27 +4,28 @@
|
|
|
4
4
|
{{ value.body }}
|
|
5
5
|
</p>
|
|
6
6
|
<div v-if="!feedbackExist">
|
|
7
|
-
<
|
|
7
|
+
<v-text-field
|
|
8
|
+
outlined
|
|
9
|
+
v-model="value.response"
|
|
10
|
+
:label="
|
|
11
|
+
$t('windward.core.components.content.blocks.feedback.enter')
|
|
12
|
+
"
|
|
13
|
+
></v-text-field>
|
|
8
14
|
</div>
|
|
9
15
|
<div v-else>
|
|
10
16
|
<div v-if="value.response">
|
|
11
|
-
<
|
|
17
|
+
<div>{{ value.response }}</div>
|
|
12
18
|
</div>
|
|
13
19
|
<div v-else>
|
|
14
|
-
<
|
|
20
|
+
<div>{{ noResponse }}</div>
|
|
15
21
|
</div>
|
|
16
22
|
</div>
|
|
17
23
|
</v-container>
|
|
18
24
|
</template>
|
|
19
25
|
<script>
|
|
20
|
-
import TextEditor from '~/components/Text/TextEditor.vue'
|
|
21
|
-
import TextViewer from '~/components/Text/TextViewer.vue'
|
|
22
26
|
export default {
|
|
23
27
|
name: 'FeedbackQuestionOpenResponse',
|
|
24
|
-
components: {
|
|
25
|
-
TextEditor,
|
|
26
|
-
TextViewer,
|
|
27
|
-
},
|
|
28
|
+
components: {},
|
|
28
29
|
props: {
|
|
29
30
|
value: { type: Object, required: true, default: '' },
|
|
30
31
|
feedbackExist: { type: Boolean, required: false, default: false },
|
|
@@ -48,10 +48,10 @@ export default {
|
|
|
48
48
|
},
|
|
49
49
|
data() {
|
|
50
50
|
return {
|
|
51
|
+
id: 'image_' + Crypto.id(),
|
|
51
52
|
aspectRatio: undefined,
|
|
52
53
|
}
|
|
53
54
|
},
|
|
54
|
-
extends: BaseContentBlock,
|
|
55
55
|
beforeMount() {
|
|
56
56
|
if (_.isEmpty(this.block.metadata.config)) {
|
|
57
57
|
this.block.metadata.config = {}
|
|
@@ -63,11 +63,6 @@ export default {
|
|
|
63
63
|
this.block.metadata.config.aria_describedby = ''
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
data() {
|
|
67
|
-
return {
|
|
68
|
-
id: 'image_' + Crypto.id(),
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
66
|
computed: {
|
|
72
67
|
describedById() {
|
|
73
68
|
// If there's a described by
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="stateLoaded">
|
|
4
|
+
<div v-if="block.body && !submitted">
|
|
5
|
+
<TextViewer v-model="block.body" :height="200"></TextViewer>
|
|
6
|
+
<TextEditor
|
|
7
|
+
v-model="response"
|
|
8
|
+
:height="200"
|
|
9
|
+
menubar=""
|
|
10
|
+
></TextEditor>
|
|
11
|
+
<p class="pa-3 text-center blue-grey lighten-5">
|
|
12
|
+
<v-btn
|
|
13
|
+
color="primary"
|
|
14
|
+
:disabled="!canSubmit"
|
|
15
|
+
@click="onSubmit"
|
|
16
|
+
>
|
|
17
|
+
{{ $t('shared.forms.submit') }}
|
|
18
|
+
</v-btn>
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div v-else-if="block.body && submitted">
|
|
23
|
+
<TextViewer v-model="block.body" :height="200"></TextViewer>
|
|
24
|
+
<p>
|
|
25
|
+
{{
|
|
26
|
+
$t(
|
|
27
|
+
'windward.core.components.content.blocks.open_response.your_response'
|
|
28
|
+
)
|
|
29
|
+
}}
|
|
30
|
+
</p>
|
|
31
|
+
<v-alert color="light-blue lighten-4">
|
|
32
|
+
<TextViewer v-model="response" :height="200"></TextViewer>
|
|
33
|
+
</v-alert>
|
|
34
|
+
<div v-if="block.metadata.config.sample_response">
|
|
35
|
+
<p>
|
|
36
|
+
{{
|
|
37
|
+
$t(
|
|
38
|
+
'windward.core.components.content.blocks.open_response.sample_response'
|
|
39
|
+
)
|
|
40
|
+
}}
|
|
41
|
+
</p>
|
|
42
|
+
<v-alert color="light-blue lighten-4">
|
|
43
|
+
<TextViewer
|
|
44
|
+
v-model="block.metadata.config.sample_response"
|
|
45
|
+
:height="200"
|
|
46
|
+
></TextViewer>
|
|
47
|
+
</v-alert>
|
|
48
|
+
</div>
|
|
49
|
+
<p class="pa-3 text-center blue-grey lighten-5">
|
|
50
|
+
<v-btn color="primary" @click="submitted = false">{{
|
|
51
|
+
$t('shared.forms.edit')
|
|
52
|
+
}}</v-btn>
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<v-alert v-else type="warning">
|
|
57
|
+
{{
|
|
58
|
+
$t(
|
|
59
|
+
'windward.core.components.content.blocks.open_response.initial_setup'
|
|
60
|
+
)
|
|
61
|
+
}}
|
|
62
|
+
</v-alert>
|
|
63
|
+
</div>
|
|
64
|
+
<div v-if="!stateLoaded">
|
|
65
|
+
<v-progress-circular
|
|
66
|
+
indeterminate
|
|
67
|
+
:size="64"
|
|
68
|
+
:width="4"
|
|
69
|
+
color="white"
|
|
70
|
+
>
|
|
71
|
+
</v-progress-circular>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script>
|
|
77
|
+
import _ from 'lodash'
|
|
78
|
+
import TextViewer from '~/components/Text/TextViewer.vue'
|
|
79
|
+
import TextEditor from '~/components/Text/TextEditor.vue'
|
|
80
|
+
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
81
|
+
|
|
82
|
+
export default {
|
|
83
|
+
name: 'ContentBlockOpenResponse',
|
|
84
|
+
extends: BaseContentBlock,
|
|
85
|
+
components: {
|
|
86
|
+
TextViewer,
|
|
87
|
+
TextEditor,
|
|
88
|
+
},
|
|
89
|
+
data() {
|
|
90
|
+
return {
|
|
91
|
+
stateLoaded: false,
|
|
92
|
+
response: '',
|
|
93
|
+
submitted: false,
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
beforeMount() {
|
|
97
|
+
if (_.isEmpty(this.block.body)) {
|
|
98
|
+
this.block.body = ''
|
|
99
|
+
}
|
|
100
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
101
|
+
this.block.metadata.config = {}
|
|
102
|
+
}
|
|
103
|
+
if (_.isEmpty(this.block.metadata.config.sample_response)) {
|
|
104
|
+
this.block.metadata.config.sample_response = ''
|
|
105
|
+
}
|
|
106
|
+
if (_.isEmpty(this.block.metadata.config.starting_text)) {
|
|
107
|
+
this.block.metadata.config.starting_text = ''
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
computed: {
|
|
111
|
+
canSubmit() {
|
|
112
|
+
// Make sure the response is not empty and not equal to the starting text
|
|
113
|
+
return (
|
|
114
|
+
this.response &&
|
|
115
|
+
this.response !== this.block.metadata.config.starting_text
|
|
116
|
+
)
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
watch: {},
|
|
120
|
+
mounted() {},
|
|
121
|
+
methods: {
|
|
122
|
+
onAfterSetContentBlockState() {
|
|
123
|
+
// If after the state is applied the response is still empty then apply the default response
|
|
124
|
+
if (this.response === '') {
|
|
125
|
+
this.response = this.block.metadata.config.starting_text
|
|
126
|
+
}
|
|
127
|
+
this.stateLoaded = true
|
|
128
|
+
},
|
|
129
|
+
onSubmit() {
|
|
130
|
+
this.submitted = true
|
|
131
|
+
|
|
132
|
+
// Force the state to save on submit and not wait
|
|
133
|
+
this.$Tracking.flushState()
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
</script>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="block.metadata.config.linked.length">
|
|
4
|
+
<v-btn color="primary" block @click="onCollate">
|
|
5
|
+
<v-icon class="mr-3">mdi-file-word</v-icon>
|
|
6
|
+
{{
|
|
7
|
+
$t(
|
|
8
|
+
'windward.core.components.content.blocks.open_response_collate.download_document'
|
|
9
|
+
)
|
|
10
|
+
}}
|
|
11
|
+
</v-btn>
|
|
12
|
+
</div>
|
|
13
|
+
<v-alert v-else type="warning">
|
|
14
|
+
{{
|
|
15
|
+
$t(
|
|
16
|
+
'windward.core.components.content.blocks.open_response_collate.initial_setup'
|
|
17
|
+
)
|
|
18
|
+
}}
|
|
19
|
+
</v-alert>
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
import _ from 'lodash'
|
|
25
|
+
import { mapGetters } from 'vuex'
|
|
26
|
+
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
27
|
+
import UserContentBlockState from '~/models/UserContentBlockState'
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
name: 'ContentBlockOpenResponseCollate',
|
|
31
|
+
extends: BaseContentBlock,
|
|
32
|
+
components: {},
|
|
33
|
+
data() {
|
|
34
|
+
return {}
|
|
35
|
+
},
|
|
36
|
+
beforeMount() {
|
|
37
|
+
if (_.isEmpty(this.block.body)) {
|
|
38
|
+
this.block.body = ''
|
|
39
|
+
}
|
|
40
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
41
|
+
this.block.metadata.config = {}
|
|
42
|
+
}
|
|
43
|
+
if (_.isEmpty(this.block.metadata.config.linked)) {
|
|
44
|
+
this.block.metadata.config.linked = []
|
|
45
|
+
}
|
|
46
|
+
if (_.isEmpty(this.block.metadata.config.filename)) {
|
|
47
|
+
this.block.metadata.config.filename = ''
|
|
48
|
+
}
|
|
49
|
+
if (_.isEmpty(this.block.metadata.config.include_prompts)) {
|
|
50
|
+
this.block.metadata.config.include_prompts = false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
computed: {
|
|
54
|
+
...mapGetters({
|
|
55
|
+
organization: 'organization/get',
|
|
56
|
+
course: 'course/get',
|
|
57
|
+
enrollment: 'enrollment/get',
|
|
58
|
+
content: 'content/get',
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
watch: {},
|
|
62
|
+
mounted() {},
|
|
63
|
+
methods: {
|
|
64
|
+
async onCollate() {
|
|
65
|
+
let userState = await UserContentBlockState.where({
|
|
66
|
+
'metadata->block->tag': 'plugin-core-open-response',
|
|
67
|
+
course_user_id: this.enrollment.id,
|
|
68
|
+
})
|
|
69
|
+
.whereIn('content_block_id', this.block.metadata.config.linked)
|
|
70
|
+
.get()
|
|
71
|
+
let collated = ''
|
|
72
|
+
|
|
73
|
+
userState.forEach((state) => {
|
|
74
|
+
// Prepend the prompt from the state if include prompts is enabled
|
|
75
|
+
if (this.block.metadata.config.include_prompts) {
|
|
76
|
+
collated +=
|
|
77
|
+
'<strong>' +
|
|
78
|
+
state.metadata.block.body +
|
|
79
|
+
'</strong><hr />'
|
|
80
|
+
}
|
|
81
|
+
if (!_.isEmpty(state.metadata.response)) {
|
|
82
|
+
collated += '\n' + state.metadata.response
|
|
83
|
+
} else {
|
|
84
|
+
collated +=
|
|
85
|
+
'\n<p>' +
|
|
86
|
+
this.$t(
|
|
87
|
+
'windward.core.components.content.blocks.open_response_collate.no_response'
|
|
88
|
+
) +
|
|
89
|
+
'</p>'
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
let filename = this.block.metadata.config.filename
|
|
93
|
+
if (_.isEmpty(this.block.metadata.config.filename)) {
|
|
94
|
+
// Default filename is the users name + the current page
|
|
95
|
+
filename =
|
|
96
|
+
this.$auth.user.last_name +
|
|
97
|
+
'_' +
|
|
98
|
+
this.$auth.user.first_name +
|
|
99
|
+
'_' +
|
|
100
|
+
_.get(this.content, 'content.name_prefix') +
|
|
101
|
+
_.get(this.content, 'content.name')
|
|
102
|
+
|
|
103
|
+
// Change spaces to underscores and remove special characters
|
|
104
|
+
filename = filename.replaceAll(/\s+/gi, '_')
|
|
105
|
+
filename = filename.replaceAll(/[^a-z0-9\-\_]/gi, '')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.generateDocument(collated, filename)
|
|
109
|
+
},
|
|
110
|
+
generateDocument(htmlBody, filename = '') {
|
|
111
|
+
// Specify file name. If one isn't supplied then a default name of `exported_document_YYYY-MM-DD.doc` is used
|
|
112
|
+
filename = filename
|
|
113
|
+
? filename + '.doc'
|
|
114
|
+
: 'exported_document_' +
|
|
115
|
+
new Date().toISOString().split('T')[0] +
|
|
116
|
+
'.doc'
|
|
117
|
+
|
|
118
|
+
var preHtml =
|
|
119
|
+
"<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'>" +
|
|
120
|
+
"<head><meta charset='utf-8'><title>" +
|
|
121
|
+
filename +
|
|
122
|
+
'</title></head>' +
|
|
123
|
+
'<body>'
|
|
124
|
+
var postHtml = '</body></html>'
|
|
125
|
+
var html = preHtml + htmlBody + postHtml
|
|
126
|
+
|
|
127
|
+
var blob = new Blob(['\ufeff', html], {
|
|
128
|
+
type: 'application/msword',
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Specify link url
|
|
132
|
+
var url =
|
|
133
|
+
'data:application/vnd.ms-word;charset=utf-8,' +
|
|
134
|
+
encodeURIComponent(html)
|
|
135
|
+
|
|
136
|
+
// Create download link element
|
|
137
|
+
var downloadLink = document.createElement('a')
|
|
138
|
+
|
|
139
|
+
document.body.appendChild(downloadLink)
|
|
140
|
+
|
|
141
|
+
if (navigator.msSaveOrOpenBlob) {
|
|
142
|
+
navigator.msSaveOrOpenBlob(blob, filename)
|
|
143
|
+
} else {
|
|
144
|
+
// Create a link to the file
|
|
145
|
+
downloadLink.href = url
|
|
146
|
+
|
|
147
|
+
// Setting the file name
|
|
148
|
+
downloadLink.download = filename
|
|
149
|
+
|
|
150
|
+
//triggering the function
|
|
151
|
+
downloadLink.click()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
document.body.removeChild(downloadLink)
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
</script>
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-list-item @click="dialog = true">
|
|
3
|
+
<v-list-item-action>
|
|
4
|
+
<v-icon>{{ config.icon }}</v-icon>
|
|
5
|
+
</v-list-item-action>
|
|
6
|
+
<v-list-item-content>
|
|
7
|
+
<v-list-item-title>
|
|
8
|
+
{{ $t(config.i18n) }}
|
|
9
|
+
</v-list-item-title>
|
|
10
|
+
<Dialog v-model="dialog" color="primary" text :trigger="false">
|
|
11
|
+
<template #title>{{ $t(config.i18n) }}</template>
|
|
12
|
+
<template #form="{ on, attrs }">
|
|
13
|
+
<v-card flat v-bind="attrs" v-on="on">
|
|
14
|
+
<v-container fluid>
|
|
15
|
+
<v-row>
|
|
16
|
+
<v-col cols="12" class="pl-6">
|
|
17
|
+
<h3>
|
|
18
|
+
{{
|
|
19
|
+
$t(
|
|
20
|
+
'windward.core.components.navigation.ask_the_expert.description'
|
|
21
|
+
)
|
|
22
|
+
}}
|
|
23
|
+
</h3>
|
|
24
|
+
</v-col>
|
|
25
|
+
<v-col
|
|
26
|
+
v-if="
|
|
27
|
+
$PermissionService.userHasAccessTo(
|
|
28
|
+
'windward.global.course,windward.organization.course.contentBlock',
|
|
29
|
+
'writable'
|
|
30
|
+
)
|
|
31
|
+
"
|
|
32
|
+
cols="12"
|
|
33
|
+
>
|
|
34
|
+
<v-col cols="12">
|
|
35
|
+
<h3 class="pb-4">
|
|
36
|
+
{{
|
|
37
|
+
$t(
|
|
38
|
+
'windward.core.components.navigation.ask_the_expert.config'
|
|
39
|
+
)
|
|
40
|
+
}}
|
|
41
|
+
</h3>
|
|
42
|
+
</v-col>
|
|
43
|
+
<v-col cols="12">
|
|
44
|
+
<v-text-field
|
|
45
|
+
v-model="courseAndContentName"
|
|
46
|
+
disabled
|
|
47
|
+
outlined
|
|
48
|
+
:label="
|
|
49
|
+
$t(
|
|
50
|
+
'windward.core.components.navigation.ask_the_expert.course_info'
|
|
51
|
+
)
|
|
52
|
+
"
|
|
53
|
+
></v-text-field>
|
|
54
|
+
</v-col>
|
|
55
|
+
</v-col>
|
|
56
|
+
<v-col
|
|
57
|
+
v-if="
|
|
58
|
+
!$PermissionService.userHasAccessTo(
|
|
59
|
+
'windward.global.course,windward.organization.course.contentBlock',
|
|
60
|
+
'writable'
|
|
61
|
+
)
|
|
62
|
+
"
|
|
63
|
+
cols="12"
|
|
64
|
+
>
|
|
65
|
+
<v-col cols="12">
|
|
66
|
+
<v-text-field
|
|
67
|
+
v-model="subject"
|
|
68
|
+
outlined
|
|
69
|
+
:label="
|
|
70
|
+
$t(
|
|
71
|
+
'windward.core.components.navigation.ask_the_expert.subject'
|
|
72
|
+
)
|
|
73
|
+
"
|
|
74
|
+
></v-text-field>
|
|
75
|
+
</v-col>
|
|
76
|
+
<v-col cols="12">
|
|
77
|
+
<v-textarea v-model="body" outlined>
|
|
78
|
+
<template #label>
|
|
79
|
+
<div>
|
|
80
|
+
{{
|
|
81
|
+
$t(
|
|
82
|
+
'windward.core.components.navigation.ask_the_expert.question'
|
|
83
|
+
)
|
|
84
|
+
}}
|
|
85
|
+
</div>
|
|
86
|
+
</template>
|
|
87
|
+
</v-textarea>
|
|
88
|
+
</v-col>
|
|
89
|
+
<v-col
|
|
90
|
+
cols="12"
|
|
91
|
+
class="pt-5 pb-5 d-flex flex-row-reverse"
|
|
92
|
+
>
|
|
93
|
+
<v-btn
|
|
94
|
+
class="primary"
|
|
95
|
+
:disabled="valid"
|
|
96
|
+
@click="onSubmit"
|
|
97
|
+
>{{
|
|
98
|
+
$t('shared.forms.submit')
|
|
99
|
+
}}</v-btn
|
|
100
|
+
>
|
|
101
|
+
</v-col>
|
|
102
|
+
</v-col>
|
|
103
|
+
</v-row>
|
|
104
|
+
</v-container>
|
|
105
|
+
</v-card>
|
|
106
|
+
</template>
|
|
107
|
+
</Dialog>
|
|
108
|
+
</v-list-item-content>
|
|
109
|
+
</v-list-item>
|
|
110
|
+
</template>
|
|
111
|
+
|
|
112
|
+
<script>
|
|
113
|
+
import { mapGetters } from 'vuex'
|
|
114
|
+
import AuthUserRepository from '~/models/repositories/AuthUserRepository'
|
|
115
|
+
import AskTheExpert from '~/models/AskTheExpert'
|
|
116
|
+
|
|
117
|
+
export default {
|
|
118
|
+
name: 'NavigationItemAskTheExpert',
|
|
119
|
+
components: {},
|
|
120
|
+
props: {
|
|
121
|
+
config: { type: Object, required: true },
|
|
122
|
+
},
|
|
123
|
+
data() {
|
|
124
|
+
return {
|
|
125
|
+
currentUser: new AuthUserRepository(this.$nuxt),
|
|
126
|
+
expert: '',
|
|
127
|
+
course: this.$ContentService.getCourse(),
|
|
128
|
+
content: this.$ContentService.get().content,
|
|
129
|
+
courseAndContentName: '',
|
|
130
|
+
body: '',
|
|
131
|
+
subject: '',
|
|
132
|
+
dialog: false,
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
computed: {
|
|
136
|
+
...mapGetters({
|
|
137
|
+
enrollment: 'enrollment/get',
|
|
138
|
+
}),
|
|
139
|
+
valid() {
|
|
140
|
+
if (this.body === '' || this.subject === '') {
|
|
141
|
+
return true
|
|
142
|
+
} else {
|
|
143
|
+
return false
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
mounted() {
|
|
148
|
+
this.courseAndContentName = this.course.name + ': ' + this.content.name
|
|
149
|
+
},
|
|
150
|
+
methods: {
|
|
151
|
+
async onSubmit() {
|
|
152
|
+
const askTheExpertSubmission = new AskTheExpert({
|
|
153
|
+
body: this.body,
|
|
154
|
+
subject: this.subject,
|
|
155
|
+
currentUser: this.currentUser,
|
|
156
|
+
enrollment: this.enrollment,
|
|
157
|
+
content: this.content,
|
|
158
|
+
course: this.course,
|
|
159
|
+
courseAndContentName: this.courseAndContentName,
|
|
160
|
+
})
|
|
161
|
+
await askTheExpertSubmission.save()
|
|
162
|
+
if (askTheExpertSubmission) {
|
|
163
|
+
this.$dialog.success('Ask The Expert Email Sent Successfully')
|
|
164
|
+
this.body = ''
|
|
165
|
+
this.subject = ''
|
|
166
|
+
this.dialog = false
|
|
167
|
+
} else {
|
|
168
|
+
this.$dialog.error('Ask The Expert Email Failed')
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
</script>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<v-container>
|
|
4
|
-
<v-col>
|
|
4
|
+
<v-col class="pa-0">
|
|
5
5
|
<v-text-field
|
|
6
6
|
v-model="block.metadata.config.title"
|
|
7
7
|
:label="
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
)
|
|
19
19
|
"
|
|
20
20
|
></v-text-field>
|
|
21
|
-
<v-btn
|
|
21
|
+
<v-btn
|
|
22
|
+
color="primary"
|
|
23
|
+
outlined
|
|
24
|
+
@click="onAddElement"
|
|
25
|
+
class="btn-add"
|
|
22
26
|
><v-icon>mdi-plus</v-icon
|
|
23
27
|
>{{ $t('shared.forms.add') }}</v-btn
|
|
24
28
|
>
|
|
@@ -29,7 +33,7 @@
|
|
|
29
33
|
:key="itemIndex"
|
|
30
34
|
class="elevation-0"
|
|
31
35
|
>
|
|
32
|
-
<v-expansion-panel-header class="elevation-0">{{
|
|
36
|
+
<v-expansion-panel-header class="elevation-0 pa-0">{{
|
|
33
37
|
'item ' + (itemIndex + 1)
|
|
34
38
|
}}</v-expansion-panel-header>
|
|
35
39
|
<v-expansion-panel-content
|
|
@@ -37,8 +41,6 @@
|
|
|
37
41
|
:key="expansionPanelKey"
|
|
38
42
|
>
|
|
39
43
|
<v-text-field
|
|
40
|
-
:counter="1"
|
|
41
|
-
maxlength="1"
|
|
42
44
|
v-model="
|
|
43
45
|
block.metadata.config.items[itemIndex].icon
|
|
44
46
|
"
|
|
@@ -186,4 +188,7 @@ export default {
|
|
|
186
188
|
.v-progress-circular {
|
|
187
189
|
margin: 1rem;
|
|
188
190
|
}
|
|
191
|
+
.btn-add {
|
|
192
|
+
width: 100%;
|
|
193
|
+
}
|
|
189
194
|
</style>
|