@windward/games 0.21.0 → 0.23.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.
@@ -82,14 +82,18 @@
82
82
  <v-container class="pa-4 mb-6">
83
83
  <v-row>
84
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>
85
+ <PluginRef
86
+ target="contentBlockSettingTool"
87
+ :attrs="{
88
+ value: block,
89
+ course: course,
90
+ content: currentContent,
91
+ }"
92
+ :on="{
93
+ input: onPluginSetBlock,
94
+ append: onPluginAppendBlock,
95
+ }"
96
+ ></PluginRef>
93
97
  </v-col>
94
98
  </v-row>
95
99
  </v-container>
@@ -107,16 +111,20 @@
107
111
  <script>
108
112
  import _ from 'lodash'
109
113
  import { mapGetters } from 'vuex'
110
- import { GenerateAIQuestionButton } from '@windward/core/utils'
111
114
  import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
112
115
  import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentBlockSettings.vue'
113
116
  import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue'
117
+ import PluginRef from '~/components/Core/PluginRef.vue'
114
118
  import Uuid from '~/helpers/Uuid'
115
119
 
116
120
  export default {
117
121
  name: 'SortingGameSettingsManager',
118
122
  extends: BaseContentSettings,
119
- components: { SortableExpansionPanel, BaseContentBlockSettings, GenerateAIQuestionButton },
123
+ components: {
124
+ SortableExpansionPanel,
125
+ BaseContentBlockSettings,
126
+ PluginRef,
127
+ },
120
128
  beforeMount() {
121
129
  if (_.isEmpty(this.block)) {
122
130
  this.block = {}
@@ -202,79 +210,116 @@ export default {
202
210
  return (element.id = startingIndex)
203
211
  })
204
212
  },
205
- onGeneratedSortingGame(activityData, replaceMode) {
213
+ onPluginSetBlock(activityData) {
206
214
  this.loading = true
207
215
  try {
208
216
  // Process the activity data
209
- if (activityData && activityData.metadata &&
217
+ if (
218
+ activityData &&
219
+ activityData.metadata &&
210
220
  activityData.metadata.config &&
211
221
  activityData.metadata.config.answer &&
212
- Array.isArray(activityData.metadata.config.answer)) {
213
-
222
+ Array.isArray(activityData.metadata.config.answer)
223
+ ) {
214
224
  // Save new items
215
225
  const newItems = activityData.metadata.config.answer
216
226
 
217
- if (replaceMode) {
218
- // Replace mode: Clear existing items
219
- this.block.metadata.config.answer.splice(0, this.block.metadata.config.answer.length)
227
+ // Replace mode: Clear existing items
228
+ this.block.metadata.config.answer.splice(
229
+ 0,
230
+ this.block.metadata.config.answer.length
231
+ )
220
232
 
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
- })
233
+ // Add all new items - ensure they have the correct structure
234
+ newItems.forEach((item, index) => {
235
+ this.block.metadata.config.answer.push({
236
+ id: index,
237
+ value: item.value || '',
238
+ expand: false,
228
239
  })
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
- }
240
+ })
238
241
 
239
- let currentId = this.block.metadata.config.answer.length
242
+ // Update title and instructions if provided and we're in replace mode
240
243
 
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
- })
244
+ if (activityData.metadata.config.title) {
245
+ this.block.metadata.config.title =
246
+ activityData.metadata.config.title
249
247
  }
250
248
 
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
- }
249
+ if (activityData.metadata.config.instructions) {
250
+ this.block.metadata.config.instructions =
251
+ activityData.metadata.config.instructions
252
+ }
253
+
254
+ // Update feedback if provided
255
+ if (activityData.metadata.config.feedback_correct) {
256
+ this.block.metadata.config.feedback_correct =
257
+ activityData.metadata.config.feedback_correct
258
+ }
259
+
260
+ this.$toast.success(
261
+ this.$t(
262
+ 'windward.games.components.settings.sorting_game.form.replaced_successfully'
263
+ ),
264
+
265
+ { duration: 3000 }
266
+ )
267
+ } else {
268
+ throw new Error('activity.error.technical')
269
+ }
270
+ } catch (error) {
271
+ // Let the error bubble up to Plugin with proper error type
272
+ throw error
273
+ } finally {
274
+ this.loading = false
275
+ }
276
+ },
277
+ onPluginAppendBlock(activityData) {
278
+ this.loading = true
279
+ try {
280
+ // Process the activity data
281
+ if (
282
+ activityData &&
283
+ activityData.metadata &&
284
+ activityData.metadata.config &&
285
+ activityData.metadata.config.answer &&
286
+ Array.isArray(activityData.metadata.config.answer)
287
+ ) {
288
+ // Save new items
289
+ const newItems = activityData.metadata.config.answer
256
290
 
257
- if (activityData.metadata.config.instructions) {
258
- this.block.metadata.config.instructions = activityData.metadata.config.instructions
259
- }
291
+ // Merge mode: Add new items to existing ones
292
+ const hasOnlyEmptyAnswer =
293
+ this.block.metadata.config.answer.length === 1 &&
294
+ this.block.metadata.config.answer[0].value === ''
260
295
 
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
- }
296
+ if (hasOnlyEmptyAnswer) {
297
+ // If there's only one empty answer, replace it instead of merging
298
+ this.block.metadata.config.answer.splice(0, 1)
265
299
  }
266
300
 
301
+ let currentId = this.block.metadata.config.answer.length
302
+
303
+ newItems.forEach((item) => {
304
+ this.block.metadata.config.answer.push({
305
+ id: currentId,
306
+ value: item.value || '',
307
+ expand: false,
308
+ })
309
+ currentId += 1
310
+ })
311
+
267
312
  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'),
313
+ this.$t(
314
+ 'windward.games.components.settings.sorting_game.form.added_successfully'
315
+ ),
271
316
  { duration: 3000 }
272
317
  )
273
318
  } else {
274
319
  throw new Error('activity.error.technical')
275
320
  }
276
321
  } catch (error) {
277
- // Let the error bubble up to GenerateAIQuestionButton with proper error type
322
+ // Let the error bubble up to Plugin with proper error type
278
323
  throw error
279
324
  } finally {
280
325
  this.loading = false
@@ -119,13 +119,33 @@
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
+ <PluginRef
126
+ target="contentBlockSettingTool"
127
+ :attrs="{
128
+ value: block,
129
+ course: course,
130
+ content: currentContent,
131
+ }"
132
+ :on="{
133
+ input: onPluginSetBlock,
134
+ append: onPluginAppendBlock,
135
+ }"
136
+ ></PluginRef>
137
+ </v-col>
138
+ </v-row>
139
+ </v-container>
122
140
  </div>
123
141
  </template>
124
142
  <script>
125
143
  import _ from 'lodash'
144
+ import { mapGetters } from 'vuex'
126
145
  import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
127
146
  import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue'
128
147
  import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentBlockSettings.vue'
148
+ import PluginRef from '~/components/Core/PluginRef.vue'
129
149
 
130
150
  export default {
131
151
  name: 'WordJumbleSettings',
@@ -133,6 +153,7 @@ export default {
133
153
  components: {
134
154
  SortableExpansionPanel,
135
155
  BaseContentBlockSettings,
156
+ PluginRef,
136
157
  },
137
158
  beforeMount() {
138
159
  if (_.isEmpty(this.block)) {
@@ -173,8 +194,15 @@ export default {
173
194
  return {
174
195
  valid: true,
175
196
  loading: false,
197
+ replaceExisting: true,
176
198
  }
177
199
  },
200
+ computed: {
201
+ ...mapGetters({
202
+ course: 'course/get',
203
+ currentContent: 'content/get',
204
+ }),
205
+ },
178
206
  methods: {
179
207
  onBeforeSave() {
180
208
  this.block.metadata.config.words.forEach((element) => {
@@ -226,6 +254,135 @@ export default {
226
254
  this.block.metadata.config.currentWord =
227
255
  this.block.metadata.config.words.length - 1
228
256
  },
257
+ onPluginSetBlock(activityData) {
258
+ this.loading = true
259
+ try {
260
+ // Process the activity data
261
+ if (
262
+ activityData &&
263
+ activityData.metadata &&
264
+ activityData.metadata.config &&
265
+ activityData.metadata.config.words &&
266
+ Array.isArray(activityData.metadata.config.words)
267
+ ) {
268
+ const newWords = activityData.metadata.config.words
269
+
270
+ this.block.metadata.config.words.splice(
271
+ 0,
272
+ this.block.metadata.config.words.length
273
+ )
274
+ newWords.forEach((word) => {
275
+ this.block.metadata.config.words.push({
276
+ id:
277
+ word.id ||
278
+ this.block.metadata.config.words.length + 1,
279
+ value: word.value || '',
280
+ hint: word.hint || '',
281
+ shuffledWord: this.shuffle(word.value || ''),
282
+ })
283
+ })
284
+
285
+ if (activityData.metadata.config.title) {
286
+ this.block.metadata.config.title =
287
+ activityData.metadata.config.title
288
+ }
289
+
290
+ if (activityData.metadata.config.feedback_correct) {
291
+ this.block.metadata.config.feedback_correct =
292
+ activityData.metadata.config.feedback_correct
293
+ }
294
+
295
+ if (activityData.metadata.config.feedback_incorrect) {
296
+ this.block.metadata.config.feedback_incorrect =
297
+ activityData.metadata.config.feedback_incorrect
298
+ }
299
+
300
+ this.block.metadata.config.currentWord = 0
301
+
302
+ this.$toast.success(
303
+ this.$t(
304
+ 'windward.games.components.settings.word_jumble.form.replaced_successfully'
305
+ ),
306
+ { duration: 3000 }
307
+ )
308
+ } else {
309
+ this.$toast.error(
310
+ this.$t(
311
+ 'windward.games.components.settings.word_jumble.form.invalid_response'
312
+ ),
313
+ {
314
+ duration: 5000,
315
+ }
316
+ )
317
+ }
318
+ } catch (error) {
319
+ const errorMessage = error.message || 'Unknown error occurred'
320
+ this.$toast.error(
321
+ `${this.$t(
322
+ 'windward.games.components.settings.word_jumble.form.failed_to_process'
323
+ )}: ${errorMessage}`,
324
+ {
325
+ duration: 5000,
326
+ }
327
+ )
328
+ } finally {
329
+ this.loading = false
330
+ }
331
+ },
332
+ onPluginAppendBlock(activityData) {
333
+ this.loading = true
334
+ try {
335
+ // Process the activity data
336
+ if (
337
+ activityData &&
338
+ activityData.metadata &&
339
+ activityData.metadata.config &&
340
+ activityData.metadata.config.words &&
341
+ Array.isArray(activityData.metadata.config.words)
342
+ ) {
343
+ const newWords = activityData.metadata.config.words
344
+
345
+ newWords.forEach((word) => {
346
+ this.block.metadata.config.words.push({
347
+ id: this.block.metadata.config.words.length + 1,
348
+ value: word.value || '',
349
+ hint: word.hint || '',
350
+ shuffledWord: this.shuffle(word.value || ''),
351
+ })
352
+ })
353
+
354
+ this.block.metadata.config.currentWord = 0
355
+
356
+ this.$toast.success(
357
+ this.$t(
358
+ 'windward.games.components.settings.word_jumble.form.added_successfully'
359
+ ),
360
+ { duration: 3000 }
361
+ )
362
+ } else {
363
+ this.$toast.error(
364
+ this.$t(
365
+ 'windward.games.components.settings.word_jumble.form.invalid_response'
366
+ ),
367
+ {
368
+ duration: 5000,
369
+ }
370
+ )
371
+ }
372
+ } catch (error) {
373
+ const errorMessage = error.message || 'Unknown error occurred'
374
+ this.$toast.error(
375
+ `${this.$t(
376
+ 'windward.games.components.settings.word_jumble.form.failed_to_process'
377
+ )}: ${errorMessage}`,
378
+ {
379
+ duration: 5000,
380
+ }
381
+ )
382
+ } finally {
383
+ this.loading = false
384
+ }
385
+ },
229
386
  },
230
387
  }
231
388
  </script>
@@ -15,4 +15,8 @@ export default {
15
15
  add_answer: 'Add Answer Option',
16
16
  add_question: 'Add Question',
17
17
  answer: 'Answers',
18
+ added_successfully: 'Questions added successfully',
19
+ replaced_successfully: 'Questions replaced successfully',
20
+ invalid_response: 'Could not process AI response for multiple-choice.',
21
+ failed_to_process: 'Failed to process AI response',
18
22
  }
@@ -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
  }
@@ -15,4 +15,8 @@ export default {
15
15
  add_answer: 'Agregar opción de respuesta',
16
16
  add_question: 'Agregar pregunta',
17
17
  answer: 'Respuestas',
18
+ added_successfully: 'Preguntas agregadas correctamente',
19
+ replaced_successfully: 'Preguntas reemplazadas correctamente',
20
+ invalid_response: 'No se pudo procesar la respuesta de IA para opción múltiple.',
21
+ failed_to_process: 'No se pudo procesar la respuesta de IA',
18
22
  }
@@ -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
  }
@@ -14,4 +14,8 @@ export default {
14
14
  add_answer: 'Lägg till svarsalternativ',
15
15
  add_question: 'Lägg till fråga',
16
16
  answer: 'Svar',
17
+ added_successfully: 'Frågor har lagts till',
18
+ replaced_successfully: 'Frågor har ersatts',
19
+ invalid_response: 'Kunde inte bearbeta AI-svar för flervalsfrågor.',
20
+ failed_to_process: 'Misslyckades med att bearbeta AI-svar',
17
21
  }
@@ -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.21.0",
3
+ "version": "0.23.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.21.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",
package/plugin.js CHANGED
@@ -128,7 +128,7 @@ export default {
128
128
  },
129
129
  },
130
130
  ],
131
- settings: [
131
+ contentBlockSetting: [
132
132
  {
133
133
  tag: 'games-flashcard-settings',
134
134
  template: FlashCardSlidesManager,