@windward/core 0.21.0 → 0.22.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 +18 -0
- package/components/Content/Blocks/OpenResponseCollate.vue +27 -4
- package/components/Content/Blocks/Video.vue +55 -93
- package/components/Settings/ScenarioChoiceSettings.vue +57 -0
- package/components/Settings/UserUploadSettings.vue +1 -1
- package/components/Settings/VideoSettings/SourcePicker.vue +17 -2
- package/components/Settings/VideoSettings.vue +6 -13
- package/components/utils/GenerateAIQuestionButton.vue +260 -7
- package/components/utils/TinyMCEWrapper.vue +17 -2
- package/i18n/en-US/components/content/blocks/generate_questions.ts +21 -0
- package/i18n/en-US/components/settings/scenario_choice.ts +2 -0
- package/i18n/es-ES/components/content/blocks/generate_questions.ts +21 -0
- package/i18n/es-ES/components/settings/scenario_choice.ts +2 -0
- package/i18n/sv-SE/components/content/blocks/generate_questions.ts +21 -0
- package/i18n/sv-SE/components/settings/scenario_choice.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Release [0.22.0] - 2025-08-25
|
|
4
|
+
|
|
5
|
+
* Merged in bugfix/LE-2075-the-panel-of-the-text-block-isnt (pull request #427)
|
|
6
|
+
* Merged in bug-fix/LE-2060/llm-character-limit-validation (pull request #421)
|
|
7
|
+
* Merged in bugfix/LE-2031-open-response-download-collate-q (pull request #422)
|
|
8
|
+
* Merged in bugfix/LE-1960-video-using-urls-as-a-source (pull request #423)
|
|
9
|
+
* Merged release/0.22.0 into bugfix/LE-1960-video-using-urls-as-a-source
|
|
10
|
+
* Merged in bugfix/LE-2057-save-button-disappeared-again-on (pull request #419)
|
|
11
|
+
* Merged release/0.22.0 into bugfix/LE-1960-video-using-urls-as-a-source
|
|
12
|
+
* Merged in feature/LE-2036/word-jumble-gen (pull request #420)
|
|
13
|
+
* Merged in feature/LE-1997/scenario-gen (pull request #416)
|
|
14
|
+
* Merged in bugfix/LE-1928-user-upload-allowed-file-types (pull request #415)
|
|
15
|
+
* Merged release/0.21.0 into bugfix/LE-1960-video-using-urls-as-a-source
|
|
16
|
+
* Merged release/0.21.0 into bugfix/LE-1928-user-upload-allowed-file-types
|
|
17
|
+
|
|
18
|
+
## Hotfix [0.21.1] created - 2025-08-20
|
|
19
|
+
|
|
20
|
+
|
|
3
21
|
## Release [0.21.0] - 2025-08-01
|
|
4
22
|
|
|
5
23
|
* Merged in feature/LE-2045/lableing-fixes (pull request #414)
|
|
@@ -82,10 +82,9 @@ export default {
|
|
|
82
82
|
.get()
|
|
83
83
|
|
|
84
84
|
let collated = ''
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
)
|
|
85
|
+
|
|
86
|
+
// sort by states by page order in course then by order on the page
|
|
87
|
+
const sortedStates = this.sortStates(userState)
|
|
89
88
|
|
|
90
89
|
sortedStates.forEach((state) => {
|
|
91
90
|
// Prepend the prompt from the state if include prompts is enabled
|
|
@@ -150,6 +149,30 @@ export default {
|
|
|
150
149
|
)
|
|
151
150
|
}
|
|
152
151
|
},
|
|
152
|
+
sortStates(states) {
|
|
153
|
+
// order by appearance on page
|
|
154
|
+
states = states.sort((a, b) =>
|
|
155
|
+
a.content_block.order < b.content_block.order ? -1 : 1
|
|
156
|
+
)
|
|
157
|
+
const contentBlocksByPage = []
|
|
158
|
+
// get page order in course
|
|
159
|
+
const contentBlockTree = this.$ContentService.getFlatTree()
|
|
160
|
+
// get homepage and add to front
|
|
161
|
+
const homePage = this.$ContentService.getHomepage()
|
|
162
|
+
if (homePage !== null) {
|
|
163
|
+
contentBlockTree.unshift(homePage)
|
|
164
|
+
}
|
|
165
|
+
// order states by page in the course
|
|
166
|
+
contentBlockTree.forEach((block) => {
|
|
167
|
+
states.forEach((state) => {
|
|
168
|
+
if (state.content_block?.content_id === block.content?.id) {
|
|
169
|
+
contentBlocksByPage.push(state)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
return contentBlocksByPage
|
|
175
|
+
},
|
|
153
176
|
generateDocument(htmlBody, filename = '') {
|
|
154
177
|
// Specify file name. If one isn't supplied then a default name of `exported_document_YYYY-MM-DD.doc` is used
|
|
155
178
|
filename = filename
|
|
@@ -55,24 +55,24 @@
|
|
|
55
55
|
@seeking="onSeeking"
|
|
56
56
|
@timeupdate="onTimeupdate"
|
|
57
57
|
>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
<template #no-source>
|
|
59
|
+
<v-card>
|
|
60
|
+
<v-card-title class="justify-center">
|
|
61
|
+
<v-icon class="mr-2">mdi-cloud-question</v-icon>
|
|
62
|
+
{{
|
|
63
|
+
$t(
|
|
64
|
+
'windward.core.components.content.blocks.video.not_configured_title'
|
|
65
|
+
)
|
|
66
|
+
}}
|
|
67
|
+
</v-card-title>
|
|
68
|
+
<v-card-text>{{
|
|
63
69
|
$t(
|
|
64
|
-
'windward.core.components.content.blocks.video.
|
|
70
|
+
'windward.core.components.content.blocks.video.edit_prompt'
|
|
65
71
|
)
|
|
66
|
-
}}
|
|
67
|
-
</v-card
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
'windward.core.components.content.blocks.video.edit_prompt'
|
|
71
|
-
)
|
|
72
|
-
}}</v-card-text>
|
|
73
|
-
</v-card>
|
|
74
|
-
</template>
|
|
75
|
-
</VuetifyPlayer>
|
|
72
|
+
}}</v-card-text>
|
|
73
|
+
</v-card>
|
|
74
|
+
</template>
|
|
75
|
+
</VuetifyPlayer>
|
|
76
76
|
</div>
|
|
77
77
|
<!-- display first note in the playlist for now -->
|
|
78
78
|
<v-alert
|
|
@@ -103,6 +103,45 @@ export default {
|
|
|
103
103
|
VuetifyPlayer,
|
|
104
104
|
},
|
|
105
105
|
extends: BaseContentBlock,
|
|
106
|
+
data() {
|
|
107
|
+
return {
|
|
108
|
+
fileTab: null,
|
|
109
|
+
// Default config settings
|
|
110
|
+
defaultConfig: {
|
|
111
|
+
title: '',
|
|
112
|
+
instructions: '',
|
|
113
|
+
// Default settings for new blocks
|
|
114
|
+
// This will be overridden in beforeMount()
|
|
115
|
+
type: 'auto', // Allowed auto|video|audio. In audio mode the player has a max-height of 40px. Auto will switch between the other types when needed
|
|
116
|
+
attributes: {
|
|
117
|
+
autoplay: false, // Autoplay on load. It's in the spec but DON'T USE THIS
|
|
118
|
+
autopictureinpicture: false, // Start with picture in picture mode
|
|
119
|
+
controls: true, // Show video controls. When false only play/pause allowed but clicking on the video itself
|
|
120
|
+
controlslist: 'nodownload noremoteplayback', // Space separated string per <video>. Allowed 'nodownload nofullscreen noremoteplayback'
|
|
121
|
+
crossorigin: 'anonymous',
|
|
122
|
+
disablepictureinpicture: true, // Shows the picture in picture button
|
|
123
|
+
disableremoteplayback: true, // Shows the remote playback button but functionality does not exist when clicked
|
|
124
|
+
height: 'auto',
|
|
125
|
+
width: '100%',
|
|
126
|
+
rewind: true, // Enabled the rewind 10s button
|
|
127
|
+
loop: false, // Loop the video on completion
|
|
128
|
+
muted: false, // Start the video muted
|
|
129
|
+
playsinline: false, // Force inline & disable fullscreen
|
|
130
|
+
poster: '', // Overridden with the playlist.poster if one is set there
|
|
131
|
+
preload: '',
|
|
132
|
+
captionsmenu: true, // Show the captions below the video
|
|
133
|
+
playlistmenu: true, // Show the playlist menu if there's multiple videos
|
|
134
|
+
playlistautoadvance: true, // Play the next source group
|
|
135
|
+
playbackrates: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], // Default playback speeds
|
|
136
|
+
},
|
|
137
|
+
playlist: [],
|
|
138
|
+
},
|
|
139
|
+
tracking: {
|
|
140
|
+
hasSkipped: false,
|
|
141
|
+
percentComplete: 0,
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
},
|
|
106
145
|
computed: {
|
|
107
146
|
hasSource() {
|
|
108
147
|
// __isDirty is used to communicate from the settings panel that a source / caption
|
|
@@ -312,83 +351,6 @@ export default {
|
|
|
312
351
|
},
|
|
313
352
|
},
|
|
314
353
|
},
|
|
315
|
-
data() {
|
|
316
|
-
return {
|
|
317
|
-
fileTab: null,
|
|
318
|
-
// Default config settings
|
|
319
|
-
defaultConfig: {
|
|
320
|
-
title: '',
|
|
321
|
-
instructions: '',
|
|
322
|
-
// Default settings for new blocks
|
|
323
|
-
// This will be overridden in beforeMount()
|
|
324
|
-
type: 'auto', // Allowed auto|video|audio. In audio mode the player has a max-height of 40px. Auto will switch between the other types when needed
|
|
325
|
-
attributes: {
|
|
326
|
-
autoplay: false, // Autoplay on load. It's in the spec but DON'T USE THIS
|
|
327
|
-
autopictureinpicture: false, // Start with picture in picture mode
|
|
328
|
-
controls: true, // Show video controls. When false only play/pause allowed but clicking on the video itself
|
|
329
|
-
controlslist: 'nodownload noremoteplayback', // Space separated string per <video>. Allowed 'nodownload nofullscreen noremoteplayback'
|
|
330
|
-
crossorigin: 'anonymous',
|
|
331
|
-
disablepictureinpicture: true, // Shows the picture in picture button
|
|
332
|
-
disableremoteplayback: true, // Shows the remote playback button but functionality does not exist when clicked
|
|
333
|
-
height: 'auto',
|
|
334
|
-
width: '100%',
|
|
335
|
-
rewind: true, // Enabled the rewind 10s button
|
|
336
|
-
loop: false, // Loop the video on completion
|
|
337
|
-
muted: false, // Start the video muted
|
|
338
|
-
playsinline: false, // Force inline & disable fullscreen
|
|
339
|
-
poster: '', // Overridden with the playlist.poster if one is set there
|
|
340
|
-
preload: '',
|
|
341
|
-
captionsmenu: true, // Show the captions below the video
|
|
342
|
-
playlistmenu: true, // Show the playlist menu if there's multiple videos
|
|
343
|
-
playlistautoadvance: true, // Play the next source group
|
|
344
|
-
playbackrates: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], // Default playback speeds
|
|
345
|
-
},
|
|
346
|
-
playlist: [
|
|
347
|
-
/*{
|
|
348
|
-
name: '', // The video name to appear on playlists
|
|
349
|
-
poster: '', // A video specific poster in the playlist
|
|
350
|
-
ads: [
|
|
351
|
-
{
|
|
352
|
-
play_at_percent: 0,
|
|
353
|
-
sources: [
|
|
354
|
-
{
|
|
355
|
-
src: "https://domain.test/ad_example.mp4",
|
|
356
|
-
type: "video/mp4",
|
|
357
|
-
},
|
|
358
|
-
],
|
|
359
|
-
tracks: [
|
|
360
|
-
{
|
|
361
|
-
src: "https://domain.test/ad_example-US.vtt",
|
|
362
|
-
kind: "captions",
|
|
363
|
-
srclang: "en-US",
|
|
364
|
-
default: true,
|
|
365
|
-
},
|
|
366
|
-
],
|
|
367
|
-
},
|
|
368
|
-
],
|
|
369
|
-
sources: [
|
|
370
|
-
{
|
|
371
|
-
src: "https://domain.test/example.mp4",
|
|
372
|
-
type: "video/mp4",
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
tracks: [
|
|
376
|
-
{
|
|
377
|
-
src: "https://domain.test/example_en-US.vtt",
|
|
378
|
-
kind: "captions",
|
|
379
|
-
srclang: "en-US",
|
|
380
|
-
default: true,
|
|
381
|
-
},
|
|
382
|
-
],
|
|
383
|
-
},*/
|
|
384
|
-
],
|
|
385
|
-
},
|
|
386
|
-
tracking: {
|
|
387
|
-
hasSkipped: false,
|
|
388
|
-
percentComplete: 0,
|
|
389
|
-
},
|
|
390
|
-
}
|
|
391
|
-
},
|
|
392
354
|
beforeMount() {
|
|
393
355
|
// Apply the default config
|
|
394
356
|
if (_.isEmpty(this.block.metadata.config)) {
|
|
@@ -129,6 +129,20 @@
|
|
|
129
129
|
</v-btn>
|
|
130
130
|
</v-row>
|
|
131
131
|
</v-container>
|
|
132
|
+
|
|
133
|
+
<v-container class="pa-4 mb-6">
|
|
134
|
+
<v-row>
|
|
135
|
+
<v-col cols="12">
|
|
136
|
+
<GenerateAIQuestionButton
|
|
137
|
+
:course="course"
|
|
138
|
+
:content="currentContent"
|
|
139
|
+
:block="block"
|
|
140
|
+
question-type="scenario_game"
|
|
141
|
+
@click:generate="onGeneratedScenarioGame"
|
|
142
|
+
></GenerateAIQuestionButton>
|
|
143
|
+
</v-col>
|
|
144
|
+
</v-row>
|
|
145
|
+
</v-container>
|
|
132
146
|
|
|
133
147
|
<DialogBox
|
|
134
148
|
v-model="showLinkDialog"
|
|
@@ -196,6 +210,7 @@ import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue
|
|
|
196
210
|
import DialogBox from '~/components/Core/DialogBox.vue'
|
|
197
211
|
import Uuid from '~/helpers/Uuid'
|
|
198
212
|
import TextEditor from '~/components/Text/TextEditor'
|
|
213
|
+
import GenerateAIQuestionButton from '../utils/GenerateAIQuestionButton.vue'
|
|
199
214
|
|
|
200
215
|
export default {
|
|
201
216
|
name: 'ScenarioChoiceSettings',
|
|
@@ -204,6 +219,7 @@ export default {
|
|
|
204
219
|
TextEditor,
|
|
205
220
|
DialogBox,
|
|
206
221
|
BaseContentBlockSettings,
|
|
222
|
+
GenerateAIQuestionButton,
|
|
207
223
|
},
|
|
208
224
|
extends: BaseContentSettings,
|
|
209
225
|
data() {
|
|
@@ -240,6 +256,8 @@ export default {
|
|
|
240
256
|
computed: {
|
|
241
257
|
...mapGetters({
|
|
242
258
|
contentTree: 'content/getTree',
|
|
259
|
+
course: 'course/get',
|
|
260
|
+
currentContent: 'content/get',
|
|
243
261
|
}),
|
|
244
262
|
},
|
|
245
263
|
beforeMount() {
|
|
@@ -351,6 +369,45 @@ export default {
|
|
|
351
369
|
!_.isEmpty(this.block.metadata.config.link_content_id) &&
|
|
352
370
|
!_.isEmpty(this.block.metadata.config.link_text)
|
|
353
371
|
},
|
|
372
|
+
onGeneratedScenarioGame(activityData) {
|
|
373
|
+
// Process the activity data
|
|
374
|
+
if (activityData && activityData.metadata &&
|
|
375
|
+
activityData.metadata.config &&
|
|
376
|
+
activityData.metadata.config.items &&
|
|
377
|
+
Array.isArray(activityData.metadata.config.items)) {
|
|
378
|
+
|
|
379
|
+
// Clear existing items and replace with generated ones
|
|
380
|
+
this.block.metadata.config.items.splice(0, this.block.metadata.config.items.length)
|
|
381
|
+
|
|
382
|
+
// Add all new choices
|
|
383
|
+
activityData.metadata.config.items.forEach(item => {
|
|
384
|
+
this.block.metadata.config.items.push({
|
|
385
|
+
title: item.title || '',
|
|
386
|
+
body: item.body || '<p></p>',
|
|
387
|
+
correct: item.correct || false
|
|
388
|
+
})
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
// Update title and instructions if provided
|
|
392
|
+
if (activityData.metadata.config.title) {
|
|
393
|
+
this.block.metadata.config.title = activityData.metadata.config.title
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (activityData.metadata.config.instructions) {
|
|
397
|
+
this.block.metadata.config.instructions = activityData.metadata.config.instructions
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
this.$toast.success(
|
|
401
|
+
this.$t('windward.core.components.settings.scenario_choice.generated_successfully'),
|
|
402
|
+
{ duration: 3000 }
|
|
403
|
+
)
|
|
404
|
+
} else {
|
|
405
|
+
this.$toast.error(
|
|
406
|
+
this.$t('windward.core.components.settings.scenario_choice.invalid_response'),
|
|
407
|
+
{ duration: 5000 }
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
},
|
|
354
411
|
},
|
|
355
412
|
}
|
|
356
413
|
</script>
|
|
@@ -139,7 +139,7 @@ export default {
|
|
|
139
139
|
name: this.$t(
|
|
140
140
|
'windward.core.components.settings.user_upload.types.all_zip'
|
|
141
141
|
),
|
|
142
|
-
value: 'application/zip,application/gzip,application/x-gzip,application/x-tar,application/rar,application/x-rar-compressed,application/x-7z-compressed,application/x-bzip2,application/x-xz',
|
|
142
|
+
value: 'application/zip,application/gzip,application/x-gzip,application/x-tar,application/rar,application/x-rar-compressed,application/x-7z-compressed,application/x-bzip2,application/x-xz,application/x-zip-compressed',
|
|
143
143
|
},
|
|
144
144
|
]
|
|
145
145
|
},
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
$PermissionService.userHasAccessTo(
|
|
33
33
|
'windward.global.file',
|
|
34
34
|
'writable'
|
|
35
|
-
)
|
|
35
|
+
) && !isFromUrl
|
|
36
36
|
"
|
|
37
37
|
top
|
|
38
38
|
color="primary"
|
|
@@ -63,7 +63,10 @@
|
|
|
63
63
|
}}</span>
|
|
64
64
|
</v-tooltip>
|
|
65
65
|
|
|
66
|
-
<v-alert
|
|
66
|
+
<v-alert
|
|
67
|
+
v-if="sourceInherit && !hasLinkedCaptions && !isFromUrl"
|
|
68
|
+
type="warning"
|
|
69
|
+
>
|
|
67
70
|
{{
|
|
68
71
|
$t(
|
|
69
72
|
'windward.core.components.settings.video.inherit_missing_captions'
|
|
@@ -180,6 +183,18 @@ export default {
|
|
|
180
183
|
_.get(this.linkedCaptions, 'asset.public_url', '???')
|
|
181
184
|
)
|
|
182
185
|
},
|
|
186
|
+
isFromUrl() {
|
|
187
|
+
if (
|
|
188
|
+
this.source &&
|
|
189
|
+
_.isEmpty(this.source.id) &&
|
|
190
|
+
_.isEmpty(this.source.asset.id) &&
|
|
191
|
+
_.isEmpty(this.source.asset.name) &&
|
|
192
|
+
!_.isEmpty(this.source.asset.public_url)
|
|
193
|
+
) {
|
|
194
|
+
return true
|
|
195
|
+
}
|
|
196
|
+
return false
|
|
197
|
+
},
|
|
183
198
|
},
|
|
184
199
|
watch: {},
|
|
185
200
|
beforeMount() {},
|
|
@@ -197,7 +197,6 @@
|
|
|
197
197
|
<template #activator="{ on, attrs }">
|
|
198
198
|
<v-btn
|
|
199
199
|
v-bind="attrs"
|
|
200
|
-
v-on="on"
|
|
201
200
|
text
|
|
202
201
|
elevation="0"
|
|
203
202
|
color="error"
|
|
@@ -205,6 +204,7 @@
|
|
|
205
204
|
render ||
|
|
206
205
|
block.metadata.config.playlist.length <= 1
|
|
207
206
|
"
|
|
207
|
+
v-on="on"
|
|
208
208
|
@click="onRemovePlaylistItem"
|
|
209
209
|
>
|
|
210
210
|
<v-icon>mdi-delete</v-icon>
|
|
@@ -334,16 +334,6 @@
|
|
|
334
334
|
"
|
|
335
335
|
:disabled="render"
|
|
336
336
|
></v-switch>
|
|
337
|
-
<!--
|
|
338
|
-
<v-switch
|
|
339
|
-
v-model="block.metadata.config.attributes.playlistmenu"
|
|
340
|
-
:label="$t('windward.core.components.settings.video.video.attributes.playlistmenu')"
|
|
341
|
-
></v-switch>
|
|
342
|
-
<v-switch
|
|
343
|
-
v-model="block.metadata.config.attributes.playlistautoadvance"
|
|
344
|
-
:label="$t('windward.core.components.settings.video.video.attributes.playlistautoadvance')"
|
|
345
|
-
></v-switch>
|
|
346
|
-
-->
|
|
347
337
|
</v-col>
|
|
348
338
|
<v-col cols="6">
|
|
349
339
|
<v-switch
|
|
@@ -392,8 +382,8 @@
|
|
|
392
382
|
import _ from 'lodash'
|
|
393
383
|
import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
|
|
394
384
|
import ContentBlockAsset from '~/components/Content/ContentBlockAsset.vue'
|
|
395
|
-
import SourcePicker from './VideoSettings/SourcePicker.vue'
|
|
396
385
|
import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentBlockSettings.vue'
|
|
386
|
+
import SourcePicker from './VideoSettings/SourcePicker.vue'
|
|
397
387
|
|
|
398
388
|
export default {
|
|
399
389
|
name: 'VideoSettings',
|
|
@@ -602,7 +592,10 @@ export default {
|
|
|
602
592
|
this.$set(
|
|
603
593
|
this.block.metadata.config.playlist[this.playlistIndex].ads,
|
|
604
594
|
adIndex,
|
|
605
|
-
{
|
|
595
|
+
{
|
|
596
|
+
sources: [],
|
|
597
|
+
tracks: [],
|
|
598
|
+
}
|
|
606
599
|
)
|
|
607
600
|
}
|
|
608
601
|
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
</div>
|
|
45
45
|
</v-col>
|
|
46
46
|
<v-col
|
|
47
|
-
v-if="isFlashcardType || isMatchingGameType || isSortingGameType"
|
|
47
|
+
v-if="isFlashcardType || isMatchingGameType || isSortingGameType || isWordJumbleType"
|
|
48
48
|
cols="auto"
|
|
49
49
|
class="d-flex align-center"
|
|
50
50
|
>
|
|
@@ -111,7 +111,7 @@ export default {
|
|
|
111
111
|
'windward.core.components.content.blocks.generate_questions.blooms.none'
|
|
112
112
|
),
|
|
113
113
|
},
|
|
114
|
-
replaceExisting: this.questionType === 'bucket_game' ? true : this.replaceExistingMode,
|
|
114
|
+
replaceExisting: this.questionType === 'bucket_game' || this.questionType === 'word_jumble' ? true : this.replaceExistingMode,
|
|
115
115
|
}
|
|
116
116
|
},
|
|
117
117
|
computed: {
|
|
@@ -125,11 +125,18 @@ export default {
|
|
|
125
125
|
return this.questionType === 'bucket_game'
|
|
126
126
|
},
|
|
127
127
|
isMatchingGameType() {
|
|
128
|
+
|
|
128
129
|
return this.questionType === 'matching_game'
|
|
129
130
|
},
|
|
130
131
|
isSortingGameType() {
|
|
131
132
|
return this.questionType === 'sorting_game'
|
|
132
133
|
},
|
|
134
|
+
isScenarioGameType() {
|
|
135
|
+
return this.questionType === 'scenario_game'
|
|
136
|
+
},
|
|
137
|
+
isWordJumbleType() {
|
|
138
|
+
return this.questionType === 'word_jumble'
|
|
139
|
+
},
|
|
133
140
|
replaceExistingLabel() {
|
|
134
141
|
if (this.isBucketGameType) {
|
|
135
142
|
return this.$t(
|
|
@@ -147,6 +154,10 @@ export default {
|
|
|
147
154
|
return this.$t(
|
|
148
155
|
'windward.games.components.settings.sorting_game.form.replace_existing')
|
|
149
156
|
}
|
|
157
|
+
if (this.isWordJumbleType) {
|
|
158
|
+
return this.$t(
|
|
159
|
+
'windward.games.components.settings.word_jumble.form.replace_existing')
|
|
160
|
+
}
|
|
150
161
|
return this.$t(
|
|
151
162
|
'windward.games.components.settings.flashcard.form.replace_existing'
|
|
152
163
|
|
|
@@ -181,6 +192,66 @@ export default {
|
|
|
181
192
|
return fullTree
|
|
182
193
|
},
|
|
183
194
|
taxonomyLevels() {
|
|
195
|
+
// For word jumble games, only show None, Remember, Understand, Apply
|
|
196
|
+
if (this.isWordJumbleType) {
|
|
197
|
+
return [
|
|
198
|
+
{
|
|
199
|
+
value: 'None',
|
|
200
|
+
text: this.$t(
|
|
201
|
+
'windward.core.components.content.blocks.generate_questions.blooms.none'
|
|
202
|
+
),
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
value: 'Remember',
|
|
206
|
+
text: this.$t(
|
|
207
|
+
'windward.core.components.content.blocks.generate_questions.blooms.remember'
|
|
208
|
+
),
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
value: 'Understand',
|
|
212
|
+
text: this.$t(
|
|
213
|
+
'windward.core.components.content.blocks.generate_questions.blooms.understand'
|
|
214
|
+
),
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
value: 'Apply',
|
|
218
|
+
text: this.$t(
|
|
219
|
+
'windward.core.components.content.blocks.generate_questions.blooms.apply'
|
|
220
|
+
),
|
|
221
|
+
},
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// For scenario games, only show None, Apply, Analyze, Evaluate
|
|
226
|
+
if (this.isScenarioGameType) {
|
|
227
|
+
return [
|
|
228
|
+
{
|
|
229
|
+
value: 'None',
|
|
230
|
+
text: this.$t(
|
|
231
|
+
'windward.core.components.content.blocks.generate_questions.blooms.none'
|
|
232
|
+
),
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
value: 'Apply',
|
|
236
|
+
text: this.$t(
|
|
237
|
+
'windward.core.components.content.blocks.generate_questions.blooms.apply'
|
|
238
|
+
),
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
value: 'Analyze',
|
|
242
|
+
text: this.$t(
|
|
243
|
+
'windward.core.components.content.blocks.generate_questions.blooms.analyze'
|
|
244
|
+
),
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
value: 'Evaluate',
|
|
248
|
+
text: this.$t(
|
|
249
|
+
'windward.core.components.content.blocks.generate_questions.blooms.evaluate'
|
|
250
|
+
),
|
|
251
|
+
},
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
|
|
184
255
|
// Basic Bloom's taxonomy levels available to all question types
|
|
185
256
|
let basicBloomTaxonomy = [
|
|
186
257
|
{
|
|
@@ -212,6 +283,7 @@ export default {
|
|
|
212
283
|
// Only add higher-level Bloom's taxonomy for supported question types
|
|
213
284
|
const supportsAdvancedTaxonomy =
|
|
214
285
|
this.isSortingGameType ||
|
|
286
|
+
this.isScenarioGameType ||
|
|
215
287
|
([
|
|
216
288
|
'multi_choice_single_answer',
|
|
217
289
|
'multi_choice_multi_answer',
|
|
@@ -257,6 +329,14 @@ export default {
|
|
|
257
329
|
return this.$t(
|
|
258
330
|
'windward.core.components.content.blocks.generate_questions.button_label_sorting_game'
|
|
259
331
|
)
|
|
332
|
+
} else if (this.questionType === 'scenario_game') {
|
|
333
|
+
return this.$t(
|
|
334
|
+
'windward.core.components.content.blocks.generate_questions.button_label_scenario_game'
|
|
335
|
+
)
|
|
336
|
+
} else if (this.questionType === 'word_jumble') {
|
|
337
|
+
return this.$t(
|
|
338
|
+
'windward.core.components.content.blocks.generate_questions.button_label_word_jumble'
|
|
339
|
+
)
|
|
260
340
|
} else {
|
|
261
341
|
return this.$t(
|
|
262
342
|
'windward.core.components.content.blocks.generate_questions.button_label'
|
|
@@ -466,6 +546,96 @@ export default {
|
|
|
466
546
|
'Invalid response from sorting game generation'
|
|
467
547
|
)
|
|
468
548
|
}
|
|
549
|
+
} else if (this.questionType === 'scenario_game') {
|
|
550
|
+
// SCENARIO GAME GENERATION
|
|
551
|
+
const activity = new Activity()
|
|
552
|
+
|
|
553
|
+
const endpoint = `suggest/scenario_game${bloomsRequest}`;
|
|
554
|
+
|
|
555
|
+
response = await Activity.custom(
|
|
556
|
+
course,
|
|
557
|
+
content,
|
|
558
|
+
activity,
|
|
559
|
+
endpoint
|
|
560
|
+
).get()
|
|
561
|
+
|
|
562
|
+
let activityData = null
|
|
563
|
+
|
|
564
|
+
if (response && response.activity) {
|
|
565
|
+
activityData = response.activity
|
|
566
|
+
} else if (
|
|
567
|
+
response &&
|
|
568
|
+
response.length > 0 &&
|
|
569
|
+
response[0] &&
|
|
570
|
+
response[0].activity
|
|
571
|
+
) {
|
|
572
|
+
activityData = response[0].activity
|
|
573
|
+
} else if (Array.isArray(response) && response.length > 0) {
|
|
574
|
+
activityData = response[0]
|
|
575
|
+
} else if (response) {
|
|
576
|
+
activityData = response
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (activityData && activityData.metadata &&
|
|
580
|
+
activityData.metadata.config &&
|
|
581
|
+
activityData.metadata.config.items &&
|
|
582
|
+
Array.isArray(activityData.metadata.config.items)
|
|
583
|
+
) {
|
|
584
|
+
// For scenario games, always replace existing content
|
|
585
|
+
this.$emit(
|
|
586
|
+
'click:generate',
|
|
587
|
+
activityData,
|
|
588
|
+
true
|
|
589
|
+
)
|
|
590
|
+
} else {
|
|
591
|
+
throw new Error('activity.error.content_mismatch')
|
|
592
|
+
}
|
|
593
|
+
} else if (this.questionType === 'word_jumble') {
|
|
594
|
+
// WORD JUMBLE GENERATION
|
|
595
|
+
const activity = new Activity()
|
|
596
|
+
|
|
597
|
+
const endpoint = `suggest/word_jumble${bloomsRequest}`
|
|
598
|
+
|
|
599
|
+
response = await Activity.custom(
|
|
600
|
+
course,
|
|
601
|
+
content,
|
|
602
|
+
activity,
|
|
603
|
+
endpoint
|
|
604
|
+
).get()
|
|
605
|
+
|
|
606
|
+
let activityData = null
|
|
607
|
+
|
|
608
|
+
if (response && response.activity) {
|
|
609
|
+
activityData = response.activity
|
|
610
|
+
} else if (
|
|
611
|
+
response &&
|
|
612
|
+
response.length > 0 &&
|
|
613
|
+
response[0] &&
|
|
614
|
+
response[0].activity
|
|
615
|
+
) {
|
|
616
|
+
activityData = response[0].activity
|
|
617
|
+
} else if (Array.isArray(response) && response.length > 0) {
|
|
618
|
+
activityData = response[0]
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (
|
|
622
|
+
activityData &&
|
|
623
|
+
activityData.metadata &&
|
|
624
|
+
activityData.metadata.config &&
|
|
625
|
+
activityData.metadata.config.words &&
|
|
626
|
+
Array.isArray(activityData.metadata.config.words)
|
|
627
|
+
) {
|
|
628
|
+
// We pass the activity data and the replace flag to the parent component
|
|
629
|
+
this.$emit(
|
|
630
|
+
'click:generate',
|
|
631
|
+
activityData,
|
|
632
|
+
this.replaceExisting
|
|
633
|
+
)
|
|
634
|
+
} else {
|
|
635
|
+
throw new Error(
|
|
636
|
+
'Invalid response from word jumble generation'
|
|
637
|
+
)
|
|
638
|
+
}
|
|
469
639
|
} else {
|
|
470
640
|
// ASSESSMENT QUESTION GENERATION
|
|
471
641
|
const assessment = new Assessment({ id: this.block.id })
|
|
@@ -499,8 +669,29 @@ export default {
|
|
|
499
669
|
|
|
500
670
|
let errorText = ''
|
|
501
671
|
|
|
502
|
-
// Check for
|
|
503
|
-
|
|
672
|
+
// Check for character limit error by structured data first
|
|
673
|
+
const errorSubtype = error.response?.data?.error?.details?.error_subtype
|
|
674
|
+
const errorDetails = error.response?.data?.error?.details
|
|
675
|
+
|
|
676
|
+
if (errorSubtype === 'CHARACTER_LIMIT_EXCEEDED') {
|
|
677
|
+
// Use structured data if available
|
|
678
|
+
const field = errorDetails?.field || 'content'
|
|
679
|
+
const limit = errorDetails?.limit
|
|
680
|
+
const actual = errorDetails?.actual
|
|
681
|
+
|
|
682
|
+
// Use parameterized message if we have the data
|
|
683
|
+
if (limit && actual) {
|
|
684
|
+
errorText = this.$t(`${basePath}.character_limit_detailed`, { field, limit, actual })
|
|
685
|
+
} else {
|
|
686
|
+
// Fallback to generic message
|
|
687
|
+
errorText = this.$t(`${basePath}.character_limit`)
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
// Keep backward compatibility - check for old message pattern
|
|
691
|
+
else if (errorDetails?.message === 'An error occurred while generating content. Try generating again.') {
|
|
692
|
+
errorText = this.$t(`${basePath}.character_limit`)
|
|
693
|
+
} else if (
|
|
694
|
+
// Check for content mismatch error specifically for bucket games
|
|
504
695
|
(errorMessage === 'activity.error.content_mismatch' ||
|
|
505
696
|
errorType === 'content_mismatch') &&
|
|
506
697
|
this.questionType === 'bucket_game'
|
|
@@ -533,11 +724,73 @@ export default {
|
|
|
533
724
|
this.$t(
|
|
534
725
|
`${basePath}.content_mismatch_sorting_game_support`
|
|
535
726
|
)
|
|
536
|
-
} else
|
|
727
|
+
} else if (
|
|
728
|
+
(errorMessage === 'activity.error.content_mismatch' ||
|
|
729
|
+
errorType === 'content_mismatch') &&
|
|
730
|
+
this.questionType === 'scenario_game'
|
|
731
|
+
) {
|
|
537
732
|
errorText =
|
|
538
|
-
this.$t(`${basePath}
|
|
733
|
+
this.$t(`${basePath}.content_mismatch_scenario_game`) +
|
|
539
734
|
'\n\n' +
|
|
540
|
-
this.$t(
|
|
735
|
+
this.$t(
|
|
736
|
+
`${basePath}.content_mismatch_scenario_game_support`
|
|
737
|
+
)
|
|
738
|
+
} else if (
|
|
739
|
+
(errorMessage === 'activity.error.content_mismatch' ||
|
|
740
|
+
errorType === 'content_mismatch') &&
|
|
741
|
+
this.questionType === 'word_jumble'
|
|
742
|
+
) {
|
|
743
|
+
errorText =
|
|
744
|
+
this.$t(`${basePath}.content_mismatch_word_jumble`) +
|
|
745
|
+
'\n\n' +
|
|
746
|
+
this.$t(
|
|
747
|
+
`${basePath}.content_mismatch_word_jumble_support`
|
|
748
|
+
)
|
|
749
|
+
} else if (errorType === 'insufficient_content' && error.response?.data?.error?.details) {
|
|
750
|
+
// Handle dynamic Bloom's taxonomy insufficient content errors
|
|
751
|
+
const details = error.response.data.error.details
|
|
752
|
+
const bloomsLevel = details.blooms_level
|
|
753
|
+
const minimumRequired = details.minimum_required
|
|
754
|
+
|
|
755
|
+
if (bloomsLevel && bloomsLevel !== 'None') {
|
|
756
|
+
// Message with Bloom's level
|
|
757
|
+
errorText =
|
|
758
|
+
this.$t(`${basePath}.insufficient_content_blooms`, { bloomsLevel }) +
|
|
759
|
+
'\n\n' +
|
|
760
|
+
this.$t(`${basePath}.insufficient_content_blooms_support`, { minimumRequired })
|
|
761
|
+
} else if (minimumRequired) {
|
|
762
|
+
// Message without Bloom's level but with word count
|
|
763
|
+
errorText =
|
|
764
|
+
this.$t(`${basePath}.insufficient_content_dynamic`) +
|
|
765
|
+
'\n\n' +
|
|
766
|
+
this.$t(`${basePath}.insufficient_content_dynamic_support`, { minimumRequired })
|
|
767
|
+
} else {
|
|
768
|
+
// Fallback to static translation
|
|
769
|
+
errorText =
|
|
770
|
+
this.$t(`${basePath}.${errorType}`) +
|
|
771
|
+
'\n\n' +
|
|
772
|
+
this.$t(`${basePath}.${errorType}_support`)
|
|
773
|
+
}
|
|
774
|
+
} else {
|
|
775
|
+
// Check if the error type is a valid i18n key
|
|
776
|
+
const errorKey = `${basePath}.${errorType}`
|
|
777
|
+
const supportKey = `${basePath}.${errorType}_support`
|
|
778
|
+
|
|
779
|
+
// Try to get the translation, fall back to default error if not found
|
|
780
|
+
const hasTranslation = this.$te(errorKey)
|
|
781
|
+
|
|
782
|
+
if (hasTranslation) {
|
|
783
|
+
errorText =
|
|
784
|
+
this.$t(errorKey) +
|
|
785
|
+
'\n\n' +
|
|
786
|
+
this.$t(supportKey)
|
|
787
|
+
} else {
|
|
788
|
+
// Fall back to default error message
|
|
789
|
+
errorText =
|
|
790
|
+
this.$t(`${basePath}.default`) +
|
|
791
|
+
'\n\n' +
|
|
792
|
+
this.$t(`${basePath}.default_support`)
|
|
793
|
+
}
|
|
541
794
|
|
|
542
795
|
if (errorType === 'technical') {
|
|
543
796
|
const errorCode =
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
</slot>
|
|
38
38
|
</div>
|
|
39
39
|
<v-btn-toggle
|
|
40
|
-
v-if="
|
|
40
|
+
v-if="hasActions"
|
|
41
41
|
dense
|
|
42
42
|
multiple
|
|
43
43
|
class="pt-1 d-flex justify-end"
|
|
44
44
|
>
|
|
45
45
|
<slot name="actions-prepend" :render="render"></slot>
|
|
46
46
|
<slot name="actions" :render="render">
|
|
47
|
-
<v-btn-toggle dense>
|
|
47
|
+
<v-btn-toggle v-if="allowRead && !render" dense>
|
|
48
48
|
<v-btn color="primary" outlined @click="read">
|
|
49
49
|
<v-icon> mdi-play</v-icon>
|
|
50
50
|
</v-btn>
|
|
@@ -164,6 +164,21 @@ export default {
|
|
|
164
164
|
},
|
|
165
165
|
|
|
166
166
|
computed: {
|
|
167
|
+
hasActions() {
|
|
168
|
+
if (this.allowRead && !this.render) {
|
|
169
|
+
return true
|
|
170
|
+
}
|
|
171
|
+
// reach into parent text editor to see if a slot has been used.
|
|
172
|
+
// Cant access this.$slots when daisy chained like we're doing with texteditor
|
|
173
|
+
if (_.isEmpty(this.$parent) || _.isEmpty(this.$parent.$slots)) {
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
return (
|
|
177
|
+
typeof this.$parent.$slots['actions-prepend'] !== 'undefined' ||
|
|
178
|
+
typeof this.$parent.$slots['actions'] !== 'undefined' ||
|
|
179
|
+
typeof this.$parent.$slots['actions-append'] !== 'undefined'
|
|
180
|
+
)
|
|
181
|
+
},
|
|
167
182
|
dataCy() {
|
|
168
183
|
return (
|
|
169
184
|
this.dataKey ||
|
|
@@ -7,6 +7,14 @@ export default {
|
|
|
7
7
|
insufficient_content: 'More content needed to generate questions.',
|
|
8
8
|
insufficient_content_support:
|
|
9
9
|
'Please add more text, examples, or explanations to this section. We recommend at least 50 words of relevant content to generate appropriate questions.',
|
|
10
|
+
|
|
11
|
+
insufficient_content_blooms: 'Insufficient content to generate quality questions at the {bloomsLevel} level.',
|
|
12
|
+
insufficient_content_blooms_support:
|
|
13
|
+
'Please add more detailed text, examples, or explanations to this section. We recommend at least {minimumRequired} words of relevant content to generate appropriate questions at this level.',
|
|
14
|
+
|
|
15
|
+
insufficient_content_dynamic: 'Insufficient content to generate quality questions.',
|
|
16
|
+
insufficient_content_dynamic_support:
|
|
17
|
+
'Please add more detailed text, examples, or explanations to this section. We recommend at least {minimumRequired} words of relevant content to generate appropriate questions.',
|
|
10
18
|
|
|
11
19
|
content_mismatch: "Content doesn't match question type.",
|
|
12
20
|
content_mismatch_support:
|
|
@@ -24,6 +32,17 @@ export default {
|
|
|
24
32
|
content_mismatch_sorting_game_support:
|
|
25
33
|
"Consider adding content with sequential steps, chronological events, or items that have a clear logical order for students to arrange.",
|
|
26
34
|
|
|
35
|
+
content_mismatch_scenario_game: "Content not suitable for scenario games.",
|
|
36
|
+
content_mismatch_scenario_game_support:
|
|
37
|
+
"Consider adding content that describes concepts, processes, or principles that can be applied in real-world scenarios.",
|
|
38
|
+
|
|
39
|
+
content_mismatch_word_jumble: "Content not suitable for word jumble games.",
|
|
40
|
+
content_mismatch_word_jumble_support:
|
|
41
|
+
"Consider adding content with clear vocabulary terms, glossary words, or key concepts that can be unscrambled.",
|
|
42
|
+
|
|
43
|
+
character_limit: 'An error occurred while generating content. Try generating again.',
|
|
44
|
+
character_limit_detailed: 'The {field} exceeds the {limit} character limit (currently {actual} characters). Try generating again with shorter content.',
|
|
45
|
+
|
|
27
46
|
llm_unavailable: 'Question generation temporarily unavailable.',
|
|
28
47
|
llm_unavailable_support:
|
|
29
48
|
"We're unable to connect to our AI service at the moment. Please try again in a few minutes or contact support if the issue persists.",
|
|
@@ -37,6 +56,8 @@ export default {
|
|
|
37
56
|
button_label_bucket_game: 'Generate Buckets',
|
|
38
57
|
button_label_matching_game: 'Generate Matches',
|
|
39
58
|
button_label_sorting_game: 'Generate Items',
|
|
59
|
+
button_label_scenario_game: 'Generate Scenarios',
|
|
60
|
+
button_label_word_jumble: 'Generate Items',
|
|
40
61
|
selected_pages: 'Selected Page',
|
|
41
62
|
ai_assistance: 'AI Assistance',
|
|
42
63
|
blooms: {
|
|
@@ -9,6 +9,14 @@ export default {
|
|
|
9
9
|
'Se necesita más contenido para generar preguntas.',
|
|
10
10
|
insufficient_content_support:
|
|
11
11
|
'Agregue más texto, ejemplos o explicaciones a esta sección. Recomendamos al menos 2 o 3 párrafos de contenido.',
|
|
12
|
+
|
|
13
|
+
insufficient_content_blooms: 'Contenido insuficiente para generar preguntas de calidad en el nivel de {bloomsLevel}.',
|
|
14
|
+
insufficient_content_blooms_support:
|
|
15
|
+
'Por favor, agregue texto más detallado, ejemplos o explicaciones a esta sección. Recomendamos al menos {minimumRequired} palabras de contenido relevante para generar preguntas apropiadas en este nivel.',
|
|
16
|
+
|
|
17
|
+
insufficient_content_dynamic: 'Contenido insuficiente para generar preguntas de calidad.',
|
|
18
|
+
insufficient_content_dynamic_support:
|
|
19
|
+
'Por favor, agregue texto más detallado, ejemplos o explicaciones a esta sección. Recomendamos al menos {minimumRequired} palabras de contenido relevante para generar preguntas apropiadas.',
|
|
12
20
|
|
|
13
21
|
content_mismatch: 'El contenido no coincide con el tipo de pregunta.',
|
|
14
22
|
content_mismatch_support:
|
|
@@ -26,6 +34,17 @@ export default {
|
|
|
26
34
|
content_mismatch_sorting_game_support:
|
|
27
35
|
'Considera agregar contenido con pasos secuenciales, eventos cronológicos o elementos que tengan un orden lógico claro para que los estudiantes los organicen.',
|
|
28
36
|
|
|
37
|
+
content_mismatch_scenario_game: 'El contenido no es adecuado para juegos de escenarios.',
|
|
38
|
+
content_mismatch_scenario_game_support:
|
|
39
|
+
'Considera agregar contenido que describa conceptos, procesos o principios que se puedan aplicar en escenarios del mundo real.',
|
|
40
|
+
|
|
41
|
+
content_mismatch_word_jumble: 'El contenido no es adecuado para juegos de revoltijo de palabras.',
|
|
42
|
+
content_mismatch_word_jumble_support:
|
|
43
|
+
'Considera agregar contenido con términos de vocabulario claros, palabras de glosario o conceptos clave que puedan ser desordenados.',
|
|
44
|
+
|
|
45
|
+
character_limit: 'Se produjo un error al generar el contenido. Intenta generar de nuevo.',
|
|
46
|
+
character_limit_detailed: 'El campo {field} excede el límite de {limit} caracteres (actualmente {actual} caracteres). Intenta generar de nuevo con contenido más corto.',
|
|
47
|
+
|
|
29
48
|
llm_unavailable:
|
|
30
49
|
'La generación de preguntas no está disponible temporalmente.',
|
|
31
50
|
llm_unavailable_support:
|
|
@@ -40,6 +59,8 @@ export default {
|
|
|
40
59
|
button_label_bucket_game: 'Generar categorías',
|
|
41
60
|
button_label_matching_game: 'Generar coincidencias',
|
|
42
61
|
button_label_sorting_game: 'Generar elementos',
|
|
62
|
+
button_label_scenario_game: 'Generar escenarios',
|
|
63
|
+
button_label_word_jumble: 'Generar elementos',
|
|
43
64
|
selected_pages: 'Página seleccionada',
|
|
44
65
|
ai_assistance: 'Asistencia de IA',
|
|
45
66
|
blooms: {
|
|
@@ -7,6 +7,14 @@ export default {
|
|
|
7
7
|
insufficient_content: 'Mer innehåll behövs för att generera frågor.',
|
|
8
8
|
insufficient_content_support:
|
|
9
9
|
'Vänligen lägg till mer text, exempel eller förklaringar till det här avsnittet. Vi rekommenderar minst 2-3 stycken innehåll.',
|
|
10
|
+
|
|
11
|
+
insufficient_content_blooms: 'Otillräckligt innehåll för att generera kvalitetsfrågor på {bloomsLevel}-nivån.',
|
|
12
|
+
insufficient_content_blooms_support:
|
|
13
|
+
'Vänligen lägg till mer detaljerad text, exempel eller förklaringar till detta avsnitt. Vi rekommenderar minst {minimumRequired} ord av relevant innehåll för att generera lämpliga frågor på denna nivå.',
|
|
14
|
+
|
|
15
|
+
insufficient_content_dynamic: 'Otillräckligt innehåll för att generera kvalitetsfrågor.',
|
|
16
|
+
insufficient_content_dynamic_support:
|
|
17
|
+
'Vänligen lägg till mer detaljerad text, exempel eller förklaringar till detta avsnitt. Vi rekommenderar minst {minimumRequired} ord av relevant innehåll för att generera lämpliga frågor.',
|
|
10
18
|
|
|
11
19
|
content_mismatch: 'Innehållet matchar inte frågetyp.',
|
|
12
20
|
content_mismatch_support:
|
|
@@ -24,6 +32,17 @@ export default {
|
|
|
24
32
|
content_mismatch_sorting_game_support:
|
|
25
33
|
'Överväg att lägga till innehåll med sekventiella steg, kronologiska händelser eller objekt som har en tydlig logisk ordning för elever att arrangera.',
|
|
26
34
|
|
|
35
|
+
content_mismatch_scenario_game: 'Innehållet är inte lämpligt för scenariospel.',
|
|
36
|
+
content_mismatch_scenario_game_support:
|
|
37
|
+
'Överväg att lägga till innehåll som beskriver koncept, processer eller principer som kan tillämpas i verkliga scenarier.',
|
|
38
|
+
|
|
39
|
+
content_mismatch_word_jumble: 'Innehållet är inte lämpligt för ordvirrvarr-spel.',
|
|
40
|
+
content_mismatch_word_jumble_support:
|
|
41
|
+
'Överväg att lägga till innehåll med tydliga vokabulärtermer, ordlistor eller nyckelkoncept som kan blandas.',
|
|
42
|
+
|
|
43
|
+
character_limit: 'Ett fel uppstod när innehållet genererades. Försök generera igen.',
|
|
44
|
+
character_limit_detailed: 'Fältet {field} överskrider gränsen på {limit} tecken (för närvarande {actual} tecken). Försök generera igen med kortare innehåll.',
|
|
45
|
+
|
|
27
46
|
llm_unavailable: 'Frågegenerering tillfälligt otillgänglig.',
|
|
28
47
|
llm_unavailable_support:
|
|
29
48
|
'Vi kan inte ansluta till vår AI-tjänst för tillfället. Försök igen om några minuter.',
|
|
@@ -37,6 +56,8 @@ export default {
|
|
|
37
56
|
button_label_bucket_game: 'Generera kategorier',
|
|
38
57
|
button_label_matching_game: 'Generera matchningar',
|
|
39
58
|
button_label_sorting_game: 'Generera objekt',
|
|
59
|
+
button_label_scenario_game: 'Generera scenarier',
|
|
60
|
+
button_label_word_jumble: 'Generera objekt',
|
|
40
61
|
selected_pages: 'Vald sida',
|
|
41
62
|
ai_assistance: 'AI-hjälp',
|
|
42
63
|
blooms: {
|