@windward/games 0.20.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/multipleChoice/MultipleChoice.vue +17 -10
- package/components/settings/MatchingGameManager.vue +147 -0
- package/components/settings/SortingGameSettingsManager.vue +102 -1
- package/components/settings/WordJumbleSettingsManager.vue +92 -0
- package/i18n/en-US/components/settings/matching_game.ts +5 -0
- package/i18n/en-US/components/settings/sorting_game.ts +7 -0
- package/i18n/en-US/components/settings/word_jumble.ts +9 -0
- package/i18n/es-ES/components/settings/matching_game.ts +5 -0
- package/i18n/es-ES/components/settings/sorting_game.ts +7 -0
- package/i18n/es-ES/components/settings/word_jumble.ts +9 -0
- package/i18n/sv-SE/components/settings/matching_game.ts +5 -0
- package/i18n/sv-SE/components/settings/sorting_game.ts +7 -0
- package/i18n/sv-SE/components/settings/word_jumble.ts +9 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Release [0.22.0] - 2025-08-28
|
|
4
|
+
|
|
5
|
+
* Merged in feature/LE-2036/update-core-package-v (pull request #252)
|
|
6
|
+
* Merged in feature/LE-1851-multiple-choice-block-feedback-l (pull request #251)
|
|
7
|
+
* Merged release/0.22.0 into feature/LE-1851-multiple-choice-block-feedback-l
|
|
8
|
+
* Merged in feature/LE-2036/word-jumble-gen (pull request #250)
|
|
9
|
+
* Merged in feature/LE-1851-multiple-choice-block-feedback-l (pull request #249)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Release [0.21.0] - 2025-07-29
|
|
13
|
+
|
|
14
|
+
* Merged in feature/LE-1912/sorting-game-generation (pull request #246)
|
|
15
|
+
* Merged in feature/LE-1913/matching-block (pull request #245)
|
|
16
|
+
* Merge branch 'release/0.21.0' into feature/LE-1913/matching-block
|
|
17
|
+
* Merge branch 'develop' into Feature/LE-1913/matching-block
|
|
18
|
+
* Merge branch 'develop' into feature/LE-1882/bucket-games
|
|
19
|
+
|
|
20
|
+
|
|
3
21
|
## Release [0.20.0] - 2025-06-25
|
|
4
22
|
|
|
5
23
|
* Merged in feature/LE-1948/empty-bucket (pull request #242)
|
|
@@ -228,13 +228,14 @@
|
|
|
228
228
|
</div>
|
|
229
229
|
</template>
|
|
230
230
|
<template #form="{ on, attrs }">
|
|
231
|
-
<
|
|
231
|
+
<p v-bind="attrs" v-on="on">
|
|
232
232
|
<TextViewer v-if="hint" v-model="hintText"></TextViewer>
|
|
233
233
|
<TextViewer
|
|
234
234
|
v-if="answerDescriptionModal"
|
|
235
235
|
v-model="answerDescription"
|
|
236
|
+
class="dialog-text-viewer"
|
|
236
237
|
></TextViewer>
|
|
237
|
-
</
|
|
238
|
+
</p>
|
|
238
239
|
</template>
|
|
239
240
|
</DialogBox>
|
|
240
241
|
</v-carousel>
|
|
@@ -320,10 +321,14 @@ export default {
|
|
|
320
321
|
onAnswerDescription(question, answer) {
|
|
321
322
|
this.activeQuestion = question
|
|
322
323
|
this.activeAnswer = answer
|
|
323
|
-
//launches modal and displays anwer description
|
|
324
|
+
// launches modal and displays anwer description
|
|
324
325
|
this.dialog = true
|
|
325
326
|
this.answerDescriptionModal = true
|
|
326
|
-
|
|
327
|
+
if (question.metadata.config.single_feedback) {
|
|
328
|
+
this.answerDescription = question.answer_description
|
|
329
|
+
} else if (!_.isEmpty(answer.feedback)) {
|
|
330
|
+
this.answerDescription = answer.feedback
|
|
331
|
+
}
|
|
327
332
|
},
|
|
328
333
|
onChooseAnswer(answer, question) {
|
|
329
334
|
// check to see if student already answered this question
|
|
@@ -467,13 +472,12 @@ export default {
|
|
|
467
472
|
}
|
|
468
473
|
)
|
|
469
474
|
if (studentsQuestionResponse) {
|
|
470
|
-
if (
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
answer.chosen === false
|
|
475
|
+
if (
|
|
476
|
+
answer.chosen === true &&
|
|
477
|
+
(!_.isEmpty(answer.feedback) ||
|
|
478
|
+
question.metadata.config.single_feedback)
|
|
475
479
|
) {
|
|
476
|
-
return 'mdi-
|
|
480
|
+
return 'mdi-message-alert-outline'
|
|
477
481
|
}
|
|
478
482
|
}
|
|
479
483
|
},
|
|
@@ -613,4 +617,7 @@ export default {
|
|
|
613
617
|
min-width: 20% !important;
|
|
614
618
|
margin-right: 16px;
|
|
615
619
|
}
|
|
620
|
+
.dialog-text-viewer {
|
|
621
|
+
color: var(--v-primary-base);
|
|
622
|
+
}
|
|
616
623
|
</style>
|
|
@@ -248,12 +248,28 @@
|
|
|
248
248
|
indeterminate
|
|
249
249
|
></v-progress-circular>
|
|
250
250
|
</div>
|
|
251
|
+
<v-container class="pa-4 mb-6">
|
|
252
|
+
<v-row>
|
|
253
|
+
<v-col cols="12">
|
|
254
|
+
<GenerateAIQuestionButton
|
|
255
|
+
:course="course"
|
|
256
|
+
:content="content"
|
|
257
|
+
:block="block"
|
|
258
|
+
question-type="matching_game"
|
|
259
|
+
:replace-existing-mode="replaceExisting"
|
|
260
|
+
@click:generate="onGeneratedMatchingGame"
|
|
261
|
+
></GenerateAIQuestionButton>
|
|
262
|
+
</v-col>
|
|
263
|
+
</v-row>
|
|
264
|
+
</v-container>
|
|
251
265
|
</div>
|
|
252
266
|
</template>
|
|
253
267
|
|
|
254
268
|
<script>
|
|
255
269
|
import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
|
|
256
270
|
import _ from 'lodash'
|
|
271
|
+
import { mapGetters } from 'vuex'
|
|
272
|
+
import { GenerateAIQuestionButton } from '@windward/core/utils'
|
|
257
273
|
import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentBlockSettings.vue'
|
|
258
274
|
import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue'
|
|
259
275
|
import Uuid from '~/helpers/Uuid'
|
|
@@ -266,6 +282,7 @@ export default {
|
|
|
266
282
|
SortableExpansionPanel,
|
|
267
283
|
ImageAssetSettings,
|
|
268
284
|
BaseContentBlockSettings,
|
|
285
|
+
GenerateAIQuestionButton
|
|
269
286
|
},
|
|
270
287
|
beforeMount() {
|
|
271
288
|
if (_.isEmpty(this.block)) {
|
|
@@ -326,8 +343,15 @@ export default {
|
|
|
326
343
|
return {
|
|
327
344
|
valid: true,
|
|
328
345
|
loading: false,
|
|
346
|
+
replaceExisting: false,
|
|
329
347
|
}
|
|
330
348
|
},
|
|
349
|
+
computed: {
|
|
350
|
+
...mapGetters({
|
|
351
|
+
course: 'course/get',
|
|
352
|
+
content: 'content/get',
|
|
353
|
+
}),
|
|
354
|
+
},
|
|
331
355
|
mounted() {},
|
|
332
356
|
methods: {
|
|
333
357
|
onAddPrompt(index = 0) {
|
|
@@ -464,6 +488,129 @@ export default {
|
|
|
464
488
|
// holder array now has new answer positions
|
|
465
489
|
this.block.metadata.config.prompts = holderArray
|
|
466
490
|
},
|
|
491
|
+
// Handler for receiving matching game data from GenerateAIQuestionButton
|
|
492
|
+
onGeneratedMatchingGame(activityData, replaceMode) {
|
|
493
|
+
|
|
494
|
+
this.loading = true
|
|
495
|
+
try {
|
|
496
|
+
// Process the activity data
|
|
497
|
+
if (activityData && activityData.metadata &&
|
|
498
|
+
activityData.metadata.config &&
|
|
499
|
+
activityData.metadata.config.answerObjects &&
|
|
500
|
+
activityData.metadata.config.prompts &&
|
|
501
|
+
Array.isArray(activityData.metadata.config.answerObjects) &&
|
|
502
|
+
Array.isArray(activityData.metadata.config.prompts)) {
|
|
503
|
+
|
|
504
|
+
if (replaceMode) {
|
|
505
|
+
// Replace mode: Clear existing answers and prompts
|
|
506
|
+
this.block.metadata.config.answerObjects.splice(0, this.block.metadata.config.answerObjects.length)
|
|
507
|
+
this.block.metadata.config.prompts.splice(0, this.block.metadata.config.prompts.length)
|
|
508
|
+
|
|
509
|
+
// Add all new answer objects and prompts
|
|
510
|
+
activityData.metadata.config.answerObjects.forEach((answerObj, index) => {
|
|
511
|
+
this.block.metadata.config.answerObjects.push({
|
|
512
|
+
id: index.toString(), // string id
|
|
513
|
+
display: answerObj.display || ''
|
|
514
|
+
})
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
// Add all prompts - following the exact pattern from BucketGameSettingsManager
|
|
518
|
+
activityData.metadata.config.prompts.forEach((promptArray, index) => {
|
|
519
|
+
this.block.metadata.config.prompts[index] = []
|
|
520
|
+
if (Array.isArray(promptArray)) {
|
|
521
|
+
promptArray.forEach(prompt => {
|
|
522
|
+
// Ensure the answer object reference is correct
|
|
523
|
+
const answerObj = this.block.metadata.config.answerObjects[index]
|
|
524
|
+
this.block.metadata.config.prompts[index].push({
|
|
525
|
+
id: index.toString(), // string id
|
|
526
|
+
textOrImage: prompt.textOrImage || 'text',
|
|
527
|
+
prompt: prompt.prompt || '',
|
|
528
|
+
matchExplanation: prompt.matchExplanation || this.$t('windward.games.components.settings.matching_game.form.correct_match'),
|
|
529
|
+
fileConfig: prompt.fileConfig || {
|
|
530
|
+
hideBackground: true,
|
|
531
|
+
},
|
|
532
|
+
answer: answerObj
|
|
533
|
+
})
|
|
534
|
+
})
|
|
535
|
+
}
|
|
536
|
+
})
|
|
537
|
+
} else {
|
|
538
|
+
// Merge mode: Add new terms and prompts to existing ones
|
|
539
|
+
// Check if there's only one empty answer (the default one created on init)
|
|
540
|
+
const hasOnlyEmptyAnswer = this.block.metadata.config.answerObjects.length === 1 &&
|
|
541
|
+
this.block.metadata.config.answerObjects[0].display === '';
|
|
542
|
+
|
|
543
|
+
if (hasOnlyEmptyAnswer) {
|
|
544
|
+
// If there's only one empty answer, replace it instead of merging
|
|
545
|
+
this.block.metadata.config.answerObjects.splice(0, 1);
|
|
546
|
+
this.block.metadata.config.prompts.splice(0, 1);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const existingAnswerCount = this.block.metadata.config.answerObjects.length
|
|
550
|
+
|
|
551
|
+
// Add new answer objects
|
|
552
|
+
activityData.metadata.config.answerObjects.forEach((answerObj, index) => {
|
|
553
|
+
const newIndex = existingAnswerCount + index
|
|
554
|
+
this.block.metadata.config.answerObjects.push({
|
|
555
|
+
id: newIndex.toString(), // string id
|
|
556
|
+
display: answerObj.display || ''
|
|
557
|
+
})
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
// Add new prompts
|
|
561
|
+
activityData.metadata.config.prompts.forEach((promptArray, index) => {
|
|
562
|
+
const adjustedIndex = existingAnswerCount + index
|
|
563
|
+
this.block.metadata.config.prompts[adjustedIndex] = []
|
|
564
|
+
if (Array.isArray(promptArray)) {
|
|
565
|
+
promptArray.forEach(prompt => {
|
|
566
|
+
const answerObj = this.block.metadata.config.answerObjects[adjustedIndex]
|
|
567
|
+
this.block.metadata.config.prompts[adjustedIndex].push({
|
|
568
|
+
id: adjustedIndex.toString(), // string id
|
|
569
|
+
textOrImage: prompt.textOrImage || 'text',
|
|
570
|
+
prompt: prompt.prompt || '',
|
|
571
|
+
matchExplanation: prompt.matchExplanation || this.$t('windward.games.components.settings.matching_game.form.correct_match'),
|
|
572
|
+
fileConfig: prompt.fileConfig || {
|
|
573
|
+
hideBackground: true,
|
|
574
|
+
},
|
|
575
|
+
answer: answerObj
|
|
576
|
+
})
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
})
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Update title and instructions if provided and we're in replace mode
|
|
583
|
+
if (replaceMode) {
|
|
584
|
+
if (activityData.metadata.config.title) {
|
|
585
|
+
this.block.metadata.config.title = activityData.metadata.config.title
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (activityData.metadata.config.instructions) {
|
|
589
|
+
this.block.metadata.config.instructions = activityData.metadata.config.instructions
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
this.$toast.success(
|
|
594
|
+
replaceMode
|
|
595
|
+
? this.$t('windward.games.components.settings.matching_game.form.replaced_successfully')
|
|
596
|
+
: this.$t('windward.games.components.settings.matching_game.form.added_successfully'),
|
|
597
|
+
{ duration: 3000 }
|
|
598
|
+
)
|
|
599
|
+
} else {
|
|
600
|
+
this.$toast.error(this.$t('windward.games.components.settings.matching_game.form.invalid_response'), {
|
|
601
|
+
duration: 5000
|
|
602
|
+
})
|
|
603
|
+
}
|
|
604
|
+
} catch (error) {
|
|
605
|
+
// Extract error message from the response
|
|
606
|
+
const errorMessage = error.message || 'Unknown error occurred'
|
|
607
|
+
this.$toast.error(`${this.$t('windward.games.components.settings.matching_game.form.failed_to_process')}: ${errorMessage}`, {
|
|
608
|
+
duration: 5000
|
|
609
|
+
})
|
|
610
|
+
} finally {
|
|
611
|
+
this.loading = false
|
|
612
|
+
}
|
|
613
|
+
}
|
|
467
614
|
},
|
|
468
615
|
}
|
|
469
616
|
</script>
|
|
@@ -79,6 +79,20 @@
|
|
|
79
79
|
:disabled="render"
|
|
80
80
|
></v-textarea>
|
|
81
81
|
</v-container>
|
|
82
|
+
<v-container class="pa-4 mb-6">
|
|
83
|
+
<v-row>
|
|
84
|
+
<v-col cols="12">
|
|
85
|
+
<GenerateAIQuestionButton
|
|
86
|
+
:course="course"
|
|
87
|
+
:content="currentContent"
|
|
88
|
+
:block="block"
|
|
89
|
+
question-type="sorting_game"
|
|
90
|
+
:replace-existing-mode="replaceExisting"
|
|
91
|
+
@click:generate="onGeneratedSortingGame"
|
|
92
|
+
></GenerateAIQuestionButton>
|
|
93
|
+
</v-col>
|
|
94
|
+
</v-row>
|
|
95
|
+
</v-container>
|
|
82
96
|
<div v-if="loading" class="text-center">
|
|
83
97
|
<v-progress-circular
|
|
84
98
|
:size="70"
|
|
@@ -92,6 +106,8 @@
|
|
|
92
106
|
|
|
93
107
|
<script>
|
|
94
108
|
import _ from 'lodash'
|
|
109
|
+
import { mapGetters } from 'vuex'
|
|
110
|
+
import { GenerateAIQuestionButton } from '@windward/core/utils'
|
|
95
111
|
import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
|
|
96
112
|
import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentBlockSettings.vue'
|
|
97
113
|
import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue'
|
|
@@ -100,7 +116,7 @@ import Uuid from '~/helpers/Uuid'
|
|
|
100
116
|
export default {
|
|
101
117
|
name: 'SortingGameSettingsManager',
|
|
102
118
|
extends: BaseContentSettings,
|
|
103
|
-
components: { SortableExpansionPanel, BaseContentBlockSettings },
|
|
119
|
+
components: { SortableExpansionPanel, BaseContentBlockSettings, GenerateAIQuestionButton },
|
|
104
120
|
beforeMount() {
|
|
105
121
|
if (_.isEmpty(this.block)) {
|
|
106
122
|
this.block = {}
|
|
@@ -148,6 +164,7 @@ export default {
|
|
|
148
164
|
valid: true,
|
|
149
165
|
dialog: false,
|
|
150
166
|
loading: false,
|
|
167
|
+
replaceExisting: false,
|
|
151
168
|
headers: [
|
|
152
169
|
{
|
|
153
170
|
text: 'Bucket Text',
|
|
@@ -159,6 +176,12 @@ export default {
|
|
|
159
176
|
cursor: null,
|
|
160
177
|
}
|
|
161
178
|
},
|
|
179
|
+
computed: {
|
|
180
|
+
...mapGetters({
|
|
181
|
+
course: 'course/get',
|
|
182
|
+
currentContent: 'content/get',
|
|
183
|
+
}),
|
|
184
|
+
},
|
|
162
185
|
methods: {
|
|
163
186
|
onAddElement() {
|
|
164
187
|
this.block.metadata.config.answer.forEach((element) => {
|
|
@@ -179,6 +202,84 @@ export default {
|
|
|
179
202
|
return (element.id = startingIndex)
|
|
180
203
|
})
|
|
181
204
|
},
|
|
205
|
+
onGeneratedSortingGame(activityData, replaceMode) {
|
|
206
|
+
this.loading = true
|
|
207
|
+
try {
|
|
208
|
+
// Process the activity data
|
|
209
|
+
if (activityData && activityData.metadata &&
|
|
210
|
+
activityData.metadata.config &&
|
|
211
|
+
activityData.metadata.config.answer &&
|
|
212
|
+
Array.isArray(activityData.metadata.config.answer)) {
|
|
213
|
+
|
|
214
|
+
// Save new items
|
|
215
|
+
const newItems = activityData.metadata.config.answer
|
|
216
|
+
|
|
217
|
+
if (replaceMode) {
|
|
218
|
+
// Replace mode: Clear existing items
|
|
219
|
+
this.block.metadata.config.answer.splice(0, this.block.metadata.config.answer.length)
|
|
220
|
+
|
|
221
|
+
// Add all new items - ensure they have the correct structure
|
|
222
|
+
newItems.forEach((item, index) => {
|
|
223
|
+
this.block.metadata.config.answer.push({
|
|
224
|
+
id: index,
|
|
225
|
+
value: item.value || '',
|
|
226
|
+
expand: false,
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
} else {
|
|
230
|
+
// Merge mode: Add new items to existing ones
|
|
231
|
+
const hasOnlyEmptyAnswer = this.block.metadata.config.answer.length === 1 &&
|
|
232
|
+
this.block.metadata.config.answer[0].value === ''
|
|
233
|
+
|
|
234
|
+
if (hasOnlyEmptyAnswer) {
|
|
235
|
+
// If there's only one empty answer, replace it instead of merging
|
|
236
|
+
this.block.metadata.config.answer.splice(0, 1)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let currentId = this.block.metadata.config.answer.length
|
|
240
|
+
|
|
241
|
+
newItems.forEach((item) => {
|
|
242
|
+
this.block.metadata.config.answer.push({
|
|
243
|
+
id: currentId,
|
|
244
|
+
value: item.value || '',
|
|
245
|
+
expand: false,
|
|
246
|
+
})
|
|
247
|
+
currentId += 1
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Update title and instructions if provided and we're in replace mode
|
|
252
|
+
if (replaceMode) {
|
|
253
|
+
if (activityData.metadata.config.title) {
|
|
254
|
+
this.block.metadata.config.title = activityData.metadata.config.title
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (activityData.metadata.config.instructions) {
|
|
258
|
+
this.block.metadata.config.instructions = activityData.metadata.config.instructions
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Update feedback if provided
|
|
262
|
+
if (activityData.metadata.config.feedback_correct) {
|
|
263
|
+
this.block.metadata.config.feedback_correct = activityData.metadata.config.feedback_correct
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
this.$toast.success(
|
|
268
|
+
replaceMode
|
|
269
|
+
? this.$t('windward.games.components.settings.sorting_game.form.replaced_successfully')
|
|
270
|
+
: this.$t('windward.games.components.settings.sorting_game.form.added_successfully'),
|
|
271
|
+
{ duration: 3000 }
|
|
272
|
+
)
|
|
273
|
+
} else {
|
|
274
|
+
throw new Error('activity.error.technical')
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
// Let the error bubble up to GenerateAIQuestionButton with proper error type
|
|
278
|
+
throw error
|
|
279
|
+
} finally {
|
|
280
|
+
this.loading = false
|
|
281
|
+
}
|
|
282
|
+
},
|
|
182
283
|
},
|
|
183
284
|
}
|
|
184
285
|
</script>
|
|
@@ -119,10 +119,26 @@
|
|
|
119
119
|
indeterminate
|
|
120
120
|
></v-progress-circular>
|
|
121
121
|
</div>
|
|
122
|
+
<v-container class="pa-4 mb-6">
|
|
123
|
+
<v-row>
|
|
124
|
+
<v-col cols="12">
|
|
125
|
+
<GenerateAIQuestionButton
|
|
126
|
+
:course="course"
|
|
127
|
+
:content="currentContent"
|
|
128
|
+
:block="block"
|
|
129
|
+
question-type="word_jumble"
|
|
130
|
+
:replace-existing-mode="replaceExisting"
|
|
131
|
+
@click:generate="onGeneratedWordJumble"
|
|
132
|
+
></GenerateAIQuestionButton>
|
|
133
|
+
</v-col>
|
|
134
|
+
</v-row>
|
|
135
|
+
</v-container>
|
|
122
136
|
</div>
|
|
123
137
|
</template>
|
|
124
138
|
<script>
|
|
125
139
|
import _ from 'lodash'
|
|
140
|
+
import { mapGetters } from 'vuex'
|
|
141
|
+
import { GenerateAIQuestionButton } from '@windward/core/utils'
|
|
126
142
|
import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
|
|
127
143
|
import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue'
|
|
128
144
|
import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentBlockSettings.vue'
|
|
@@ -133,6 +149,7 @@ export default {
|
|
|
133
149
|
components: {
|
|
134
150
|
SortableExpansionPanel,
|
|
135
151
|
BaseContentBlockSettings,
|
|
152
|
+
GenerateAIQuestionButton,
|
|
136
153
|
},
|
|
137
154
|
beforeMount() {
|
|
138
155
|
if (_.isEmpty(this.block)) {
|
|
@@ -173,8 +190,16 @@ export default {
|
|
|
173
190
|
return {
|
|
174
191
|
valid: true,
|
|
175
192
|
loading: false,
|
|
193
|
+
replaceExisting: true,
|
|
176
194
|
}
|
|
177
195
|
},
|
|
196
|
+
computed: {
|
|
197
|
+
|
|
198
|
+
...mapGetters({
|
|
199
|
+
course: 'course/get',
|
|
200
|
+
currentContent: 'content/get',
|
|
201
|
+
}),
|
|
202
|
+
},
|
|
178
203
|
methods: {
|
|
179
204
|
onBeforeSave() {
|
|
180
205
|
this.block.metadata.config.words.forEach((element) => {
|
|
@@ -226,6 +251,73 @@ export default {
|
|
|
226
251
|
this.block.metadata.config.currentWord =
|
|
227
252
|
this.block.metadata.config.words.length - 1
|
|
228
253
|
},
|
|
254
|
+
onGeneratedWordJumble(activityData, replaceMode) {
|
|
255
|
+
this.loading = true
|
|
256
|
+
try {
|
|
257
|
+
// Process the activity data
|
|
258
|
+
if (activityData && activityData.metadata &&
|
|
259
|
+
activityData.metadata.config &&
|
|
260
|
+
activityData.metadata.config.words &&
|
|
261
|
+
Array.isArray(activityData.metadata.config.words)) {
|
|
262
|
+
|
|
263
|
+
const newWords = activityData.metadata.config.words
|
|
264
|
+
|
|
265
|
+
if (replaceMode) { this.block.metadata.config.words.splice(0, this.block.metadata.config.words.length)
|
|
266
|
+
newWords.forEach(word => {
|
|
267
|
+
this.block.metadata.config.words.push({
|
|
268
|
+
id: word.id || this.block.metadata.config.words.length + 1,
|
|
269
|
+
value: word.value || '',
|
|
270
|
+
hint: word.hint || '',
|
|
271
|
+
shuffledWord: this.shuffle(word.value || '')
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
} else {
|
|
275
|
+
newWords.forEach(word => {
|
|
276
|
+
this.block.metadata.config.words.push({
|
|
277
|
+
id: this.block.metadata.config.words.length + 1,
|
|
278
|
+
value: word.value || '',
|
|
279
|
+
hint: word.hint || '',
|
|
280
|
+
shuffledWord: this.shuffle(word.value || '')
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (replaceMode && activityData.metadata.config.title) {
|
|
286
|
+
this.block.metadata.config.title = activityData.metadata.config.title
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (replaceMode) {
|
|
290
|
+
if (activityData.metadata.config.feedback_correct) {
|
|
291
|
+
this.block.metadata.config.feedback_correct = activityData.metadata.config.feedback_correct
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (activityData.metadata.config.feedback_incorrect) {
|
|
295
|
+
this.block.metadata.config.feedback_incorrect = activityData.metadata.config.feedback_incorrect
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this.block.metadata.config.currentWord = 0
|
|
300
|
+
|
|
301
|
+
this.$toast.success(
|
|
302
|
+
replaceMode
|
|
303
|
+
? this.$t('windward.games.components.settings.word_jumble.form.replaced_successfully')
|
|
304
|
+
: this.$t('windward.games.components.settings.word_jumble.form.added_successfully'),
|
|
305
|
+
{ duration: 3000 }
|
|
306
|
+
)
|
|
307
|
+
} else {
|
|
308
|
+
this.$toast.error(this.$t('windward.games.components.settings.word_jumble.form.invalid_response'), {
|
|
309
|
+
duration: 5000
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
const errorMessage = error.message || 'Unknown error occurred'
|
|
314
|
+
this.$toast.error(`${this.$t('windward.games.components.settings.word_jumble.form.failed_to_process')}: ${errorMessage}`, {
|
|
315
|
+
duration: 5000
|
|
316
|
+
})
|
|
317
|
+
} finally {
|
|
318
|
+
this.loading = false
|
|
319
|
+
}
|
|
320
|
+
},
|
|
229
321
|
},
|
|
230
322
|
}
|
|
231
323
|
</script>
|
|
@@ -7,6 +7,11 @@ export default {
|
|
|
7
7
|
correct_match: 'This match is correct.',
|
|
8
8
|
incorrect_match: 'That’s an incorrect match. Try again.',
|
|
9
9
|
correct_match_label: 'Explanation of the correct match',
|
|
10
|
+
replace_existing: 'Replace Existing Matches',
|
|
11
|
+
added_successfully: 'Matches added successfully',
|
|
12
|
+
replaced_successfully: 'Matches replaced successfully',
|
|
13
|
+
invalid_response: 'Invalid response from AI generation',
|
|
14
|
+
failed_to_process: 'Failed to process AI response',
|
|
10
15
|
feedback: {
|
|
11
16
|
correct: 'feedback when correct',
|
|
12
17
|
incorrect: 'feedback when incorrect',
|
|
@@ -5,4 +5,11 @@ export default {
|
|
|
5
5
|
instructions: 'Drag each statement into the correct order. When you are finished, click the Check Your Answers button. Statements in the correct order will appear green, while statements not in the correct order will appear red.',
|
|
6
6
|
feedback_correct_label: 'Explanation of the correct order',
|
|
7
7
|
default_feedback_correct: 'That is the correct order.',
|
|
8
|
+
form: {
|
|
9
|
+
replace_existing: 'Replace existing items',
|
|
10
|
+
replaced_successfully: 'Items replaced successfully',
|
|
11
|
+
added_successfully: 'New items added successfully',
|
|
12
|
+
invalid_response: 'Invalid response from sorting game generation',
|
|
13
|
+
failed_to_process: 'Failed to process generated sorting game'
|
|
14
|
+
}
|
|
8
15
|
}
|
|
@@ -9,4 +9,13 @@ export default {
|
|
|
9
9
|
add: 'Add Word',
|
|
10
10
|
feedback_correct: 'Feedback when correct',
|
|
11
11
|
feedback_incorrect: 'Feedback when incorrect',
|
|
12
|
+
|
|
13
|
+
// AI generation keys in nested form structure
|
|
14
|
+
form: {
|
|
15
|
+
replace_existing: 'Replace existing words',
|
|
16
|
+
replaced_successfully: 'Words replaced successfully',
|
|
17
|
+
added_successfully: 'New words added successfully',
|
|
18
|
+
invalid_response: 'Invalid response from word jumble generation',
|
|
19
|
+
failed_to_process: 'Failed to process generated words'
|
|
20
|
+
}
|
|
12
21
|
}
|
|
@@ -16,5 +16,10 @@ export default {
|
|
|
16
16
|
char_count_less_then:
|
|
17
17
|
'El número de caracteres para este campo debe ser menor que',
|
|
18
18
|
},
|
|
19
|
+
replace_existing: 'Reemplazar coincidencias existentes',
|
|
20
|
+
added_successfully: 'Coincidencias añadidas correctamente',
|
|
21
|
+
replaced_successfully: 'Coincidencias reemplazadas correctamente',
|
|
22
|
+
invalid_response: 'Respuesta inválida de la generación de IA',
|
|
23
|
+
failed_to_process: 'Error al procesar la respuesta de IA'
|
|
19
24
|
},
|
|
20
25
|
}
|
|
@@ -5,4 +5,11 @@ export default {
|
|
|
5
5
|
instructions: 'Arrastre cada declaración al orden correcto. Cuando haya terminado, haga clic en el botón Verifique sus respuestas. Las declaraciones en el orden correcto aparecerán en verde, mientras que las declaraciones que no estén en el orden correcto aparecerán en rojo.',
|
|
6
6
|
feedback_correct_label: 'Explicación del orden correcto.',
|
|
7
7
|
default_feedback_correct: 'Ese es el orden correcto.',
|
|
8
|
+
form: {
|
|
9
|
+
replace_existing: 'Reemplazar elementos existentes',
|
|
10
|
+
replaced_successfully: 'Elementos reemplazados exitosamente',
|
|
11
|
+
added_successfully: 'Nuevos elementos agregados exitosamente',
|
|
12
|
+
invalid_response: 'Respuesta inválida de la generación de juego de ordenamiento',
|
|
13
|
+
failed_to_process: 'Error al procesar el juego de ordenamiento generado'
|
|
14
|
+
}
|
|
8
15
|
}
|
|
@@ -9,4 +9,13 @@ export default {
|
|
|
9
9
|
add: 'Agregar palabra',
|
|
10
10
|
feedback_correct: 'Comentarios cuando sea correcto',
|
|
11
11
|
feedback_incorrect: 'Comentarios cuando sean incorrectos',
|
|
12
|
+
|
|
13
|
+
// AI generation keys in nested form structure
|
|
14
|
+
form: {
|
|
15
|
+
replace_existing: 'Reemplazar palabras existentes',
|
|
16
|
+
replaced_successfully: 'Palabras reemplazadas exitosamente',
|
|
17
|
+
added_successfully: 'Nuevas palabras añadidas exitosamente',
|
|
18
|
+
invalid_response: 'Respuesta inválida de la generación de revoltijo de palabras',
|
|
19
|
+
failed_to_process: 'Error al procesar las palabras generadas'
|
|
20
|
+
}
|
|
12
21
|
}
|
|
@@ -16,5 +16,10 @@ export default {
|
|
|
16
16
|
char_count_less_then:
|
|
17
17
|
'Teckenantalet för detta fält måste vara mindre än',
|
|
18
18
|
},
|
|
19
|
+
replace_existing: 'Ersätt befintliga matchningar',
|
|
20
|
+
added_successfully: 'Matchningar har lagts till',
|
|
21
|
+
replaced_successfully: 'Matchningar har ersatts',
|
|
22
|
+
invalid_response: 'Ogiltigt svar från AI-generering',
|
|
23
|
+
failed_to_process: 'Misslyckades med att bearbeta AI-svar'
|
|
19
24
|
},
|
|
20
25
|
}
|
|
@@ -5,4 +5,11 @@ export default {
|
|
|
5
5
|
instructions: 'Dra varje påstående i rätt ordning. När du är klar klickar du på knappen Kontrollera dina svar. Påståenden i rätt ordning visas i grönt, medan påståenden som inte är i rätt ordning visas röda.',
|
|
6
6
|
feedback_correct_label: 'Förklaring av rätt ordning',
|
|
7
7
|
default_feedback_correct: 'Det är rätt ordning.',
|
|
8
|
+
form: {
|
|
9
|
+
replace_existing: 'Ersätt befintliga objekt',
|
|
10
|
+
replaced_successfully: 'Objekt ersatta framgångsrikt',
|
|
11
|
+
added_successfully: 'Nya objekt tillagda framgångsrikt',
|
|
12
|
+
invalid_response: 'Ogiltigt svar från sorteringsspelsgenerering',
|
|
13
|
+
failed_to_process: 'Misslyckades med att bearbeta genererat sorteringsspel'
|
|
14
|
+
}
|
|
8
15
|
}
|
|
@@ -9,4 +9,13 @@ export default {
|
|
|
9
9
|
add: 'Lägg till ord',
|
|
10
10
|
feedback_correct: 'Återkoppling när korrekt',
|
|
11
11
|
feedback_incorrect: 'Återkoppling när felaktig',
|
|
12
|
+
|
|
13
|
+
// AI generation keys in nested form structure
|
|
14
|
+
form: {
|
|
15
|
+
replace_existing: 'Ersätt befintliga ord',
|
|
16
|
+
replaced_successfully: 'Ord ersatta framgångsrikt',
|
|
17
|
+
added_successfully: 'Nya ord tillagda framgångsrikt',
|
|
18
|
+
invalid_response: 'Ogiltig respons från ordvirrvarr-generering',
|
|
19
|
+
failed_to_process: 'Misslyckades med att bearbeta genererade ord'
|
|
20
|
+
}
|
|
12
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windward/games",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Windward UI Plugin Games",
|
|
5
5
|
"main": "plugin.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"homepage": "https://bitbucket.org/mindedge/windward-ui-plugin-games#readme",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@windward/core": "^0.
|
|
24
|
+
"@windward/core": "^0.22.0",
|
|
25
25
|
"eslint": "^8.11.0",
|
|
26
26
|
"lodash": "^4.17.21",
|
|
27
27
|
"prettier": "^2.6.0",
|