@windward/core 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/components/Content/Blocks/Accordion.vue +39 -38
- package/components/Content/Blocks/OpenResponse.vue +15 -11
- package/components/Navigation/Items/UserUploadNav.vue +4 -0
- package/components/Settings/TextEditorSettings.vue +1 -0
- package/components/utils/ContentViewer.vue +3 -0
- package/components/utils/FillInBlank/FillInBlankInput.vue +32 -28
- package/components/{Content/Blocks → utils}/GenerateAIQuestionButton.vue +117 -25
- package/components/utils/TinyMCEWrapper.vue +14 -57
- package/helpers/tinymce/WindwardPlugins.ts +36 -3
- package/models/Activity.ts +9 -0
- package/package.json +1 -1
- package/utils/index.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<v-container
|
|
4
|
-
class="pa-0"
|
|
5
4
|
v-if="
|
|
6
5
|
block.metadata.config.title ||
|
|
7
6
|
block.metadata.config.instructions
|
|
8
7
|
"
|
|
8
|
+
class="pa-0"
|
|
9
9
|
>
|
|
10
10
|
<h2 v-if="block.metadata.config.title" tabindex="0">
|
|
11
11
|
{{ block.metadata.config.title }}
|
|
@@ -31,9 +31,10 @@
|
|
|
31
31
|
:key="itemIndex"
|
|
32
32
|
>
|
|
33
33
|
<v-expansion-panel-header
|
|
34
|
+
:ref="'expansion-panel-' + itemIndex"
|
|
34
35
|
class="expansion-panel-header"
|
|
35
36
|
color="primary"
|
|
36
|
-
|
|
37
|
+
role="button"
|
|
37
38
|
@click="onOpenAccordion(itemIndex)"
|
|
38
39
|
>
|
|
39
40
|
{{
|
|
@@ -43,8 +44,8 @@
|
|
|
43
44
|
}}
|
|
44
45
|
</v-expansion-panel-header>
|
|
45
46
|
<v-expansion-panel-content
|
|
46
|
-
class="expansion-panel-body"
|
|
47
47
|
:key="expansionPanelKey"
|
|
48
|
+
class="expansion-panel-body"
|
|
48
49
|
>
|
|
49
50
|
<v-container
|
|
50
51
|
class="px-0 d-flex"
|
|
@@ -87,9 +88,9 @@
|
|
|
87
88
|
import _ from 'lodash'
|
|
88
89
|
import Crypto from '~/helpers/Crypto'
|
|
89
90
|
import ImageAssetViewer from '~/components/Content/ImageAssetViewer.vue'
|
|
91
|
+
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
90
92
|
import TextEditor from '~/components/Text/TextEditor'
|
|
91
93
|
import TextViewer from '~/components/Text/TextViewer'
|
|
92
|
-
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
93
94
|
|
|
94
95
|
export default {
|
|
95
96
|
name: 'ContentBlockAccordion',
|
|
@@ -99,40 +100,6 @@ export default {
|
|
|
99
100
|
ImageAssetViewer,
|
|
100
101
|
},
|
|
101
102
|
extends: BaseContentBlock,
|
|
102
|
-
beforeMount() {
|
|
103
|
-
if (_.isEmpty(this.block)) {
|
|
104
|
-
this.block = {}
|
|
105
|
-
}
|
|
106
|
-
if (_.isEmpty(this.block.metadata)) {
|
|
107
|
-
this.block.metadata = {}
|
|
108
|
-
}
|
|
109
|
-
if (_.isEmpty(this.block.metadata.config)) {
|
|
110
|
-
this.block.metadata.config = {}
|
|
111
|
-
}
|
|
112
|
-
if (_.isEmpty(this.block.metadata.config.title)) {
|
|
113
|
-
this.block.metadata.config.title = ''
|
|
114
|
-
}
|
|
115
|
-
if (_.isEmpty(this.block.metadata.config.items)) {
|
|
116
|
-
const defaultObject = {
|
|
117
|
-
header: '',
|
|
118
|
-
expand: false,
|
|
119
|
-
content: '',
|
|
120
|
-
fileConfig: {
|
|
121
|
-
display: {
|
|
122
|
-
width: 100,
|
|
123
|
-
margin: '',
|
|
124
|
-
padding: '',
|
|
125
|
-
},
|
|
126
|
-
hideBackground: true,
|
|
127
|
-
},
|
|
128
|
-
}
|
|
129
|
-
this.block.metadata.config.items = []
|
|
130
|
-
this.block.metadata.config.items.push(defaultObject)
|
|
131
|
-
}
|
|
132
|
-
this.block.body = this.$t(
|
|
133
|
-
'windward.core.shared.content_blocks.title.accordion'
|
|
134
|
-
)
|
|
135
|
-
},
|
|
136
103
|
data() {
|
|
137
104
|
return {
|
|
138
105
|
expansionPanelKey: '0',
|
|
@@ -184,6 +151,40 @@ export default {
|
|
|
184
151
|
}
|
|
185
152
|
},
|
|
186
153
|
},
|
|
154
|
+
beforeMount() {
|
|
155
|
+
if (_.isEmpty(this.block)) {
|
|
156
|
+
this.block = {}
|
|
157
|
+
}
|
|
158
|
+
if (_.isEmpty(this.block.metadata)) {
|
|
159
|
+
this.block.metadata = {}
|
|
160
|
+
}
|
|
161
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
162
|
+
this.block.metadata.config = {}
|
|
163
|
+
}
|
|
164
|
+
if (_.isEmpty(this.block.metadata.config.title)) {
|
|
165
|
+
this.block.metadata.config.title = ''
|
|
166
|
+
}
|
|
167
|
+
if (_.isEmpty(this.block.metadata.config.items)) {
|
|
168
|
+
const defaultObject = {
|
|
169
|
+
header: '',
|
|
170
|
+
expand: false,
|
|
171
|
+
content: '',
|
|
172
|
+
fileConfig: {
|
|
173
|
+
display: {
|
|
174
|
+
width: 100,
|
|
175
|
+
margin: '',
|
|
176
|
+
padding: '',
|
|
177
|
+
},
|
|
178
|
+
hideBackground: true,
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
this.block.metadata.config.items = []
|
|
182
|
+
this.block.metadata.config.items.push(defaultObject)
|
|
183
|
+
}
|
|
184
|
+
this.block.body = this.$t(
|
|
185
|
+
'windward.core.shared.content_blocks.title.accordion'
|
|
186
|
+
)
|
|
187
|
+
},
|
|
187
188
|
mounted() {
|
|
188
189
|
if (this.render) {
|
|
189
190
|
// ensure panels are always loaded closed
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<v-container
|
|
4
|
-
class="pa-0"
|
|
5
4
|
v-if="
|
|
6
5
|
block.metadata.config.title ||
|
|
7
6
|
block.metadata.config.instructions
|
|
8
7
|
"
|
|
8
|
+
class="pa-0"
|
|
9
9
|
>
|
|
10
10
|
<h2 v-if="block.metadata.config.title" tabindex="0">
|
|
11
11
|
{{ block.metadata.config.title }}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
<div v-if="block.body && !submitted">
|
|
24
24
|
<TextViewer v-model="block.body" :height="200"></TextViewer>
|
|
25
25
|
<TextEditor
|
|
26
|
+
:key="updateKey"
|
|
26
27
|
v-model="response"
|
|
27
28
|
:height="200"
|
|
28
29
|
menubar=""
|
|
@@ -104,23 +105,25 @@
|
|
|
104
105
|
<script>
|
|
105
106
|
import _ from 'lodash'
|
|
106
107
|
import { mapGetters } from 'vuex'
|
|
107
|
-
import TextViewer from '~/components/Text/TextViewer'
|
|
108
|
-
import TextEditor from '~/components/Text/TextEditor'
|
|
109
108
|
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
110
109
|
import UserContentBlockState from '~/models/UserContentBlockState'
|
|
110
|
+
import Crypto from '~/helpers/Crypto'
|
|
111
|
+
import TextViewer from '~/components/Text/TextViewer'
|
|
112
|
+
import TextEditor from '~/components/Text/TextEditor'
|
|
111
113
|
|
|
112
114
|
export default {
|
|
113
115
|
name: 'ContentBlockOpenResponse',
|
|
114
|
-
extends: BaseContentBlock,
|
|
115
116
|
components: {
|
|
116
117
|
TextViewer,
|
|
117
118
|
TextEditor,
|
|
118
119
|
},
|
|
120
|
+
extends: BaseContentBlock,
|
|
119
121
|
data() {
|
|
120
122
|
return {
|
|
121
123
|
stateLoaded: false,
|
|
122
124
|
response: '',
|
|
123
125
|
submitted: false,
|
|
126
|
+
updateKey: Crypto.id(),
|
|
124
127
|
}
|
|
125
128
|
},
|
|
126
129
|
computed: {
|
|
@@ -135,6 +138,13 @@ export default {
|
|
|
135
138
|
)
|
|
136
139
|
},
|
|
137
140
|
},
|
|
141
|
+
watch: {
|
|
142
|
+
render(newVal) {
|
|
143
|
+
if (newVal) {
|
|
144
|
+
this.onAfterSetContentBlockState()
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
},
|
|
138
148
|
beforeMount() {
|
|
139
149
|
if (_.isEmpty(this.block.body)) {
|
|
140
150
|
this.block.body = ''
|
|
@@ -155,13 +165,6 @@ export default {
|
|
|
155
165
|
this.block.metadata.config.starting_text = ''
|
|
156
166
|
}
|
|
157
167
|
},
|
|
158
|
-
watch: {
|
|
159
|
-
render(newVal) {
|
|
160
|
-
if (newVal) {
|
|
161
|
-
this.onAfterSetContentBlockState()
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
168
|
mounted() {},
|
|
166
169
|
methods: {
|
|
167
170
|
async onAfterSetContentBlockState() {
|
|
@@ -194,6 +197,7 @@ export default {
|
|
|
194
197
|
// If we don't have a block_id then we are in the initial setup state
|
|
195
198
|
this.response = this.block.metadata.config.starting_text
|
|
196
199
|
}
|
|
200
|
+
this.updateKey = Crypto.id()
|
|
197
201
|
this.stateLoaded = true
|
|
198
202
|
},
|
|
199
203
|
onSubmit() {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<v-tooltip right>
|
|
3
3
|
<template #activator="{ on, attrs }">
|
|
4
4
|
<v-list-item
|
|
5
|
+
:class="color"
|
|
5
6
|
:to="
|
|
6
7
|
'/course/' +
|
|
7
8
|
course.id +
|
|
@@ -38,6 +39,9 @@ import { mapGetters } from 'vuex'
|
|
|
38
39
|
export default {
|
|
39
40
|
components: {},
|
|
40
41
|
middleware: ['auth'],
|
|
42
|
+
props: {
|
|
43
|
+
color: { type: String, required: false, default: '' },
|
|
44
|
+
},
|
|
41
45
|
data() {
|
|
42
46
|
return {}
|
|
43
47
|
},
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
color="primary"
|
|
31
31
|
elevation="0"
|
|
32
32
|
outlined
|
|
33
|
-
@click="
|
|
33
|
+
@click="close"
|
|
34
|
+
@keydown.esc="close"
|
|
34
35
|
>
|
|
35
36
|
{{
|
|
36
37
|
$t(
|
|
@@ -46,12 +47,11 @@
|
|
|
46
47
|
|
|
47
48
|
<div class="fib">
|
|
48
49
|
<v-text-field
|
|
50
|
+
v-model="userInput"
|
|
49
51
|
:disabled="false"
|
|
50
52
|
outlined
|
|
51
53
|
dense
|
|
52
|
-
v-model="userInput"
|
|
53
54
|
clear-icon="mdi-close-circle-outline"
|
|
54
|
-
@click:append-outer="toggleToolTip"
|
|
55
55
|
clearable
|
|
56
56
|
:background-color="feedback"
|
|
57
57
|
:placeholder="
|
|
@@ -59,18 +59,19 @@
|
|
|
59
59
|
'windward.core.components.utils.fill_in_the_blank.fill_in_blank_input.add_answer'
|
|
60
60
|
)
|
|
61
61
|
"
|
|
62
|
-
@input="showAnswer = false"
|
|
63
|
-
@keydown="onPressEnter"
|
|
64
62
|
:aria-label="
|
|
65
63
|
$t(
|
|
66
64
|
'windward.core.components.utils.tiny_mce_wrapper.fill_in_blank'
|
|
67
65
|
)
|
|
68
66
|
"
|
|
67
|
+
@click:append-outer="toggleToolTip"
|
|
68
|
+
@input="showAnswer = false"
|
|
69
|
+
@keydown="onPressEnter"
|
|
69
70
|
></v-text-field>
|
|
70
71
|
|
|
71
72
|
<v-btn
|
|
72
73
|
v-if="userInput && userInput.length > 0"
|
|
73
|
-
|
|
74
|
+
ref="btnCheck"
|
|
74
75
|
elevation="0"
|
|
75
76
|
color="primary"
|
|
76
77
|
outlined
|
|
@@ -79,25 +80,22 @@
|
|
|
79
80
|
'windward.core.components.utils.fill_in_the_blank.fill_in_blank_input.check_answer'
|
|
80
81
|
)
|
|
81
82
|
"
|
|
83
|
+
@click="toggleToolTip"
|
|
82
84
|
>{{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
: $t(
|
|
88
|
-
'windward.core.components.utils.fill_in_the_blank.fill_in_blank_input.check'
|
|
89
|
-
)
|
|
85
|
+
$t(
|
|
86
|
+
'windward.core.components.utils.fill_in_the_blank.fill_in_blank_input.check'
|
|
87
|
+
)
|
|
90
88
|
}}</v-btn
|
|
91
89
|
>
|
|
92
90
|
</div>
|
|
93
91
|
</span>
|
|
94
92
|
</template>
|
|
95
93
|
<script>
|
|
96
|
-
import ContentViewer from '../ContentViewer.vue'
|
|
97
94
|
import _ from 'lodash'
|
|
95
|
+
|
|
98
96
|
export default {
|
|
99
97
|
name: 'FillInBlankInput',
|
|
100
|
-
components: {
|
|
98
|
+
components: {},
|
|
101
99
|
props: {
|
|
102
100
|
answer: { type: String, required: true },
|
|
103
101
|
description: {
|
|
@@ -144,6 +142,20 @@ export default {
|
|
|
144
142
|
return ''
|
|
145
143
|
},
|
|
146
144
|
},
|
|
145
|
+
watch: {
|
|
146
|
+
// set focus on the action button when dialog loaded for accessibility
|
|
147
|
+
show: function (newValue) {
|
|
148
|
+
if (newValue) {
|
|
149
|
+
this.timerId = setTimeout(() => {
|
|
150
|
+
this.setFocusAction()
|
|
151
|
+
}, 100)
|
|
152
|
+
} else {
|
|
153
|
+
if (this.timerId !== '') {
|
|
154
|
+
clearTimeout(this.timeout)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
},
|
|
147
159
|
methods: {
|
|
148
160
|
setFocusAction() {
|
|
149
161
|
this.$refs.action_button_1.$el.focus()
|
|
@@ -171,19 +183,11 @@ export default {
|
|
|
171
183
|
|
|
172
184
|
this.show = !this.show
|
|
173
185
|
},
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.timerId = setTimeout(() => {
|
|
180
|
-
this.setFocusAction()
|
|
181
|
-
}, 100)
|
|
182
|
-
} else {
|
|
183
|
-
if (this.timerId !== '') {
|
|
184
|
-
clearTimeout(this.timeout)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
186
|
+
close() {
|
|
187
|
+
this.show = false
|
|
188
|
+
this.$nextTick(() => {
|
|
189
|
+
this.$refs.btnCheck.$el.focus()
|
|
190
|
+
})
|
|
187
191
|
},
|
|
188
192
|
},
|
|
189
193
|
}
|
|
@@ -40,6 +40,25 @@
|
|
|
40
40
|
return-object
|
|
41
41
|
></v-select>
|
|
42
42
|
</v-col>
|
|
43
|
+
<v-col
|
|
44
|
+
cols="auto"
|
|
45
|
+
class="d-flex align-center"
|
|
46
|
+
v-if="isFlashcardType"
|
|
47
|
+
>
|
|
48
|
+
<v-switch
|
|
49
|
+
v-model="replaceExisting"
|
|
50
|
+
hide-details
|
|
51
|
+
dense
|
|
52
|
+
color="secondary"
|
|
53
|
+
class="mt-0 pt-0"
|
|
54
|
+
:label="
|
|
55
|
+
$t(
|
|
56
|
+
'windward.games.components.settings.flashcard.form.replace_existing'
|
|
57
|
+
)
|
|
58
|
+
"
|
|
59
|
+
:disabled="isLoading"
|
|
60
|
+
></v-switch>
|
|
61
|
+
</v-col>
|
|
43
62
|
<v-col>
|
|
44
63
|
<v-btn
|
|
45
64
|
elevation="0"
|
|
@@ -76,7 +95,7 @@ import AssessmentQuestion from '~/models/AssessmentQuestion'
|
|
|
76
95
|
import Course from '~/models/Course'
|
|
77
96
|
import Assessment from '~/models/Assessment'
|
|
78
97
|
import Content from '~/models/Content'
|
|
79
|
-
import
|
|
98
|
+
import Activity from '../../models/Activity'
|
|
80
99
|
|
|
81
100
|
export default {
|
|
82
101
|
name: 'GenerateAIQuestionButton',
|
|
@@ -85,6 +104,7 @@ export default {
|
|
|
85
104
|
content: { type: Object, required: true },
|
|
86
105
|
block: { type: Object, required: true },
|
|
87
106
|
questionType: { type: String, required: true },
|
|
107
|
+
replaceExistingMode: { type: Boolean, default: false },
|
|
88
108
|
},
|
|
89
109
|
data() {
|
|
90
110
|
return {
|
|
@@ -96,12 +116,16 @@ export default {
|
|
|
96
116
|
'windward.core.components.content.blocks.generate_questions.blooms.none'
|
|
97
117
|
),
|
|
98
118
|
},
|
|
119
|
+
replaceExisting: this.replaceExistingMode,
|
|
99
120
|
}
|
|
100
121
|
},
|
|
101
122
|
computed: {
|
|
102
123
|
...mapGetters({
|
|
103
124
|
contentTree: 'content/getTree',
|
|
104
125
|
}),
|
|
126
|
+
isFlashcardType() {
|
|
127
|
+
return this.questionType === 'flashcard'
|
|
128
|
+
},
|
|
105
129
|
flattenedContent() {
|
|
106
130
|
let cloneContentTree = _.cloneDeep(this.contentTree)
|
|
107
131
|
const homepage = this.$ContentService.getHomepage()
|
|
@@ -131,6 +155,7 @@ export default {
|
|
|
131
155
|
return fullTree
|
|
132
156
|
},
|
|
133
157
|
taxonomyLevels() {
|
|
158
|
+
// Basic Bloom's taxonomy levels available to all question types
|
|
134
159
|
let basicBloomTaxonomy = [
|
|
135
160
|
{
|
|
136
161
|
value: 'None',
|
|
@@ -158,10 +183,13 @@ export default {
|
|
|
158
183
|
},
|
|
159
184
|
]
|
|
160
185
|
|
|
186
|
+
// Only add higher-level Bloom's taxonomy for supported question types
|
|
187
|
+
// Flashcards use only basic levels
|
|
161
188
|
if (
|
|
162
|
-
this.
|
|
163
|
-
this.questionType === '
|
|
164
|
-
|
|
189
|
+
!this.isFlashcardType &&
|
|
190
|
+
(this.questionType === 'multi_choice_single_answer' ||
|
|
191
|
+
this.questionType === 'ordering' ||
|
|
192
|
+
this.questionType === 'multi_choice_multi_answer')
|
|
165
193
|
) {
|
|
166
194
|
const multiBlooms = [
|
|
167
195
|
{
|
|
@@ -185,32 +213,96 @@ export default {
|
|
|
185
213
|
methods: {
|
|
186
214
|
async generateAIQuestion() {
|
|
187
215
|
this.isLoading = true
|
|
188
|
-
let bloomsRequest = ''
|
|
189
|
-
if (
|
|
190
|
-
this.selectedDifficulty.text !==
|
|
191
|
-
this.$t(
|
|
192
|
-
'windward.core.components.content.blocks.generate_questions.blooms.none'
|
|
193
|
-
)
|
|
194
|
-
) {
|
|
195
|
-
// send value to api call so its not changing with language
|
|
196
|
-
bloomsRequest = `?blooms_level=${this.selectedDifficulty.value}`
|
|
197
|
-
}
|
|
198
216
|
try {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
let bloomsRequest = ''
|
|
218
|
+
if (
|
|
219
|
+
this.selectedDifficulty.text !==
|
|
220
|
+
this.$t(
|
|
221
|
+
'windward.core.components.content.blocks.generate_questions.blooms.none'
|
|
222
|
+
)
|
|
223
|
+
) {
|
|
224
|
+
bloomsRequest = `?blooms_level=${this.selectedDifficulty.value}`
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const course = new Course(this.course)
|
|
228
|
+
const content = new Content(
|
|
229
|
+
this.selectedContent || this.content
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
let response
|
|
233
|
+
if (this.questionType === 'flashcard') {
|
|
234
|
+
// FLASHCARD GENERATION
|
|
235
|
+
const activity = new Activity()
|
|
236
|
+
|
|
237
|
+
const endpoint = `suggest/flashcard${bloomsRequest}`
|
|
238
|
+
|
|
239
|
+
// Call the endpoint exactly like FlashCardSlidesManager does
|
|
240
|
+
response = await Activity.custom(
|
|
241
|
+
course,
|
|
242
|
+
content,
|
|
243
|
+
activity,
|
|
244
|
+
endpoint
|
|
245
|
+
).get()
|
|
246
|
+
|
|
247
|
+
let activityData = null
|
|
248
|
+
|
|
249
|
+
if (response && response.activity) {
|
|
250
|
+
activityData = response.activity
|
|
251
|
+
} else if (
|
|
252
|
+
response &&
|
|
253
|
+
response.length > 0 &&
|
|
254
|
+
response[0] &&
|
|
255
|
+
response[0].activity
|
|
256
|
+
) {
|
|
257
|
+
activityData = response[0].activity
|
|
258
|
+
} else if (Array.isArray(response) && response.length > 0) {
|
|
259
|
+
activityData = response[0]
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (
|
|
263
|
+
activityData &&
|
|
264
|
+
activityData.metadata &&
|
|
265
|
+
activityData.metadata.config &&
|
|
266
|
+
activityData.metadata.config.cards &&
|
|
267
|
+
Array.isArray(activityData.metadata.config.cards)
|
|
268
|
+
) {
|
|
269
|
+
// We pass the activity data and the replace flag to the parent component
|
|
270
|
+
this.$emit(
|
|
271
|
+
'click:generate',
|
|
272
|
+
activityData,
|
|
273
|
+
this.replaceExisting
|
|
274
|
+
)
|
|
275
|
+
} else {
|
|
276
|
+
throw new Error(
|
|
277
|
+
'Invalid response from flashcard generation'
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
// ASSESSMENT QUESTION GENERATION
|
|
282
|
+
const assessment = new Assessment({ id: this.block.id })
|
|
283
|
+
const question = new AssessmentQuestion()
|
|
284
|
+
|
|
285
|
+
response = await AssessmentQuestion.custom(
|
|
286
|
+
course,
|
|
287
|
+
content,
|
|
288
|
+
assessment,
|
|
289
|
+
question,
|
|
290
|
+
`suggest/${this.questionType}${bloomsRequest}`
|
|
291
|
+
).get()
|
|
292
|
+
|
|
293
|
+
if (response && response.length > 0) {
|
|
294
|
+
const generatedQuestion = response[0]
|
|
295
|
+
this.$emit('click:generate', generatedQuestion)
|
|
296
|
+
} else {
|
|
297
|
+
throw new Error(
|
|
298
|
+
'Invalid response from question generation'
|
|
299
|
+
)
|
|
300
|
+
}
|
|
210
301
|
}
|
|
211
302
|
} catch (error) {
|
|
212
303
|
const errorMessage =
|
|
213
304
|
error.response?.data?.error?.message ||
|
|
305
|
+
error.message ||
|
|
214
306
|
'assessment.error.technical'
|
|
215
307
|
const errorType = errorMessage.split('.').pop()
|
|
216
308
|
const basePath =
|
|
@@ -138,7 +138,7 @@ export default {
|
|
|
138
138
|
type: String,
|
|
139
139
|
required: false,
|
|
140
140
|
default:
|
|
141
|
-
'
|
|
141
|
+
'styles | bold italic underline strikethrough removeformat | alignleft aligncenter alignright | table tablerowprops tablecellprops |bullist numlist outdent indent |glossaryButton fibFormatButton mathButton a11yButton | undo redo',
|
|
142
142
|
},
|
|
143
143
|
rootBlock: { type: String, required: false, default: 'div' },
|
|
144
144
|
label: { type: String, required: false, default: '' },
|
|
@@ -212,7 +212,7 @@ export default {
|
|
|
212
212
|
},
|
|
213
213
|
format: {
|
|
214
214
|
title: 'Format',
|
|
215
|
-
items: ' bold italic underline strikethrough superscript subscript codeformat | formats align | language | removeformat',
|
|
215
|
+
items: ' bold italic underline strikethrough superscript subscript codeformat | formats align | language | removeformat | glossary',
|
|
216
216
|
},
|
|
217
217
|
},
|
|
218
218
|
autoresize_min_height: 100,
|
|
@@ -337,6 +337,9 @@ export default {
|
|
|
337
337
|
},
|
|
338
338
|
formats: {
|
|
339
339
|
glossary: {
|
|
340
|
+
title: this.$t(
|
|
341
|
+
'windward.core.components.utils.tiny_mce_wrapper.glossary'
|
|
342
|
+
),
|
|
340
343
|
inline: 'span',
|
|
341
344
|
attributes: {
|
|
342
345
|
'aria-label': this.$t(
|
|
@@ -378,67 +381,22 @@ export default {
|
|
|
378
381
|
],
|
|
379
382
|
},
|
|
380
383
|
style_formats: [
|
|
381
|
-
{
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
{ title: 'Heading 6', format: 'h6' },
|
|
389
|
-
],
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
title: 'Inline',
|
|
393
|
-
items: [
|
|
394
|
-
{ title: 'Bold', format: 'bold' },
|
|
395
|
-
{ title: 'Italic', format: 'italic' },
|
|
396
|
-
{ title: 'Underline', format: 'underline' },
|
|
397
|
-
{ title: 'Strikethrough', format: 'strikethrough' },
|
|
398
|
-
{ title: 'Superscript', format: 'superscript' },
|
|
399
|
-
{ title: 'Subscript', format: 'subscript' },
|
|
400
|
-
{ title: 'Code', format: 'code' },
|
|
401
|
-
],
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
title: 'Blocks',
|
|
405
|
-
items: [{ title: 'Paragraph', format: 'p' }],
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
title: 'Align',
|
|
409
|
-
items: [
|
|
410
|
-
{ title: 'Left', format: 'alignleft' },
|
|
411
|
-
{ title: 'Center', format: 'aligncenter' },
|
|
412
|
-
{ title: 'Right', format: 'alignright' },
|
|
413
|
-
{ title: 'Justify', format: 'alignjustify' },
|
|
414
|
-
],
|
|
415
|
-
},
|
|
416
|
-
{
|
|
417
|
-
title: 'LearningEdge',
|
|
418
|
-
items: [
|
|
419
|
-
{
|
|
420
|
-
title: this.$t(
|
|
421
|
-
'windward.core.components.utils.tiny_mce_wrapper.term'
|
|
422
|
-
),
|
|
423
|
-
format: 'glossary',
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
title: this.$t(
|
|
427
|
-
'windward.core.components.utils.tiny_mce_wrapper.fill_blank'
|
|
428
|
-
),
|
|
429
|
-
format: 'fib',
|
|
430
|
-
},
|
|
431
|
-
],
|
|
432
|
-
},
|
|
384
|
+
{ title: 'Normal Text', format: 'div' },
|
|
385
|
+
{ title: 'Paragraph', format: 'p' },
|
|
386
|
+
{ title: 'Heading 2', format: 'h2' },
|
|
387
|
+
{ title: 'Heading 3', format: 'h3' },
|
|
388
|
+
{ title: 'Heading 4', format: 'h4' },
|
|
389
|
+
{ title: 'Heading 5', format: 'h5' },
|
|
390
|
+
{ title: 'Heading 6', format: 'h6' },
|
|
433
391
|
],
|
|
434
392
|
placeholder: this.label
|
|
435
393
|
? this.label
|
|
436
394
|
: this.$t('components.content.settings.base.placeholder'),
|
|
437
|
-
//required as it will be displayed as inline style in tinymce renderer
|
|
395
|
+
// required as it will be displayed as inline style in tinymce renderer
|
|
438
396
|
skin: false,
|
|
439
397
|
content_css: this.$vuetify.theme.isDark ? 'dark' : 'default',
|
|
440
398
|
|
|
441
|
-
//we need to inject the glossary style directly
|
|
399
|
+
// we need to inject the glossary style directly
|
|
442
400
|
content_style:
|
|
443
401
|
ContentCss +
|
|
444
402
|
EditorCss +
|
|
@@ -477,7 +435,6 @@ export default {
|
|
|
477
435
|
this.text = this.value
|
|
478
436
|
}
|
|
479
437
|
},
|
|
480
|
-
|
|
481
438
|
methods: {
|
|
482
439
|
getEditor() {
|
|
483
440
|
if (this.$refs.editor && this.$refs.editor.editor) {
|
|
@@ -142,7 +142,17 @@ export class WindwardPlugins {
|
|
|
142
142
|
'glossaryIcon',
|
|
143
143
|
this.$t('windward.core.components.utils.tiny_mce_wrapper.glossary'),
|
|
144
144
|
() => {
|
|
145
|
-
|
|
145
|
+
if (
|
|
146
|
+
this.editor.selection
|
|
147
|
+
.getSel()
|
|
148
|
+
.anchorNode.parentElement.classList.contains(
|
|
149
|
+
'glossary-word'
|
|
150
|
+
)
|
|
151
|
+
) {
|
|
152
|
+
this.editor.formatter.remove('glossary')
|
|
153
|
+
} else {
|
|
154
|
+
this.editor.formatter.apply('glossary')
|
|
155
|
+
}
|
|
146
156
|
}
|
|
147
157
|
)
|
|
148
158
|
this.addButtonToEditor(
|
|
@@ -210,13 +220,29 @@ export class WindwardPlugins {
|
|
|
210
220
|
itemKey: string,
|
|
211
221
|
itemText: string,
|
|
212
222
|
command: string,
|
|
213
|
-
icon: string
|
|
223
|
+
icon: string,
|
|
224
|
+
type: string = 'command'
|
|
214
225
|
): void {
|
|
215
226
|
this.editor.ui.registry.addMenuItem(itemKey, {
|
|
216
227
|
text: itemText,
|
|
217
228
|
icon,
|
|
218
229
|
onAction: () => {
|
|
219
|
-
|
|
230
|
+
switch (type) {
|
|
231
|
+
case 'format':
|
|
232
|
+
if (
|
|
233
|
+
this.editor.selection
|
|
234
|
+
.getNode()
|
|
235
|
+
.classList.contains('glossary-word')
|
|
236
|
+
) {
|
|
237
|
+
this.editor.formatter.remove(command)
|
|
238
|
+
} else {
|
|
239
|
+
this.editor.formatter.apply(command)
|
|
240
|
+
}
|
|
241
|
+
break
|
|
242
|
+
default:
|
|
243
|
+
this.editor.execCommand(command, true)
|
|
244
|
+
break
|
|
245
|
+
}
|
|
220
246
|
},
|
|
221
247
|
})
|
|
222
248
|
}
|
|
@@ -233,6 +259,13 @@ export class WindwardPlugins {
|
|
|
233
259
|
'fib-window',
|
|
234
260
|
'fibIcon'
|
|
235
261
|
)
|
|
262
|
+
this.addEditorMenuItem(
|
|
263
|
+
'glossary',
|
|
264
|
+
'Glossary',
|
|
265
|
+
'glossary',
|
|
266
|
+
'glossaryIcon',
|
|
267
|
+
'format'
|
|
268
|
+
)
|
|
236
269
|
}
|
|
237
270
|
|
|
238
271
|
/**
|
package/package.json
CHANGED
package/utils/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import TinyMCEWrapper from '../components/utils/TinyMCEWrapper.vue'
|
|
|
7
7
|
import MathExpressionEditor from '../components/utils/MathExpressionEditor'
|
|
8
8
|
import CourseGlossary from '../components/utils/glossary/CourseGlossary.vue'
|
|
9
9
|
import CourseGlossaryForm from '../components/utils/glossary/CourseGlossaryForm.vue'
|
|
10
|
-
import GenerateAIQuestionButton from '../components/
|
|
10
|
+
import GenerateAIQuestionButton from '../components/utils/GenerateAIQuestionButton.vue'
|
|
11
11
|
|
|
12
12
|
export {
|
|
13
13
|
MathHelper,
|