@windward/core 0.26.0 → 0.28.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/components/Content/Blocks/Accordion.vue +9 -15
  3. package/components/Content/Blocks/BlockQuote.vue +29 -4
  4. package/components/Content/Blocks/ClickableIcons.vue +22 -9
  5. package/components/Content/Blocks/Email.vue +11 -4
  6. package/components/Content/Blocks/Feedback/FeedbackAnalytics.vue +179 -0
  7. package/components/Content/Blocks/Feedback.vue +115 -111
  8. package/components/Content/Blocks/FileDownload.vue +2 -2
  9. package/components/Content/Blocks/Image.vue +144 -0
  10. package/components/Content/Blocks/OpenResponse.vue +419 -5
  11. package/components/Content/Blocks/ScenarioChoice.vue +11 -2
  12. package/components/Content/Blocks/Tab.vue +16 -29
  13. package/components/Content/Blocks/UserUpload.vue +66 -38
  14. package/components/Content/Blocks/Video.vue +377 -28
  15. package/components/Settings/AccordionSettings.vue +3 -15
  16. package/components/Settings/BlockQuoteSettings.vue +6 -4
  17. package/components/Settings/ClickableIconsSettings.vue +24 -10
  18. package/components/Settings/EmailSettings.vue +3 -11
  19. package/components/Settings/FileDownloadSettings.vue +8 -2
  20. package/components/Settings/ImageSettings.vue +26 -0
  21. package/components/Settings/OpenResponseCollateSettings.vue +10 -0
  22. package/components/Settings/OpenResponseSettings.vue +67 -7
  23. package/components/Settings/ScenarioChoiceSettings.vue +11 -5
  24. package/components/Settings/TabSettings.vue +3 -18
  25. package/components/Settings/UserUploadSettings.vue +16 -8
  26. package/components/Settings/VideoSettings/SourcePicker.vue +55 -21
  27. package/components/Settings/VideoSettings.vue +18 -2
  28. package/components/utils/ContentViewer.vue +180 -1
  29. package/components/utils/glossary/GlossaryToolTip.vue +4 -23
  30. package/helpers/GlossaryHelper.ts +4 -7
  31. package/i18n/en-US/components/content/blocks/accordion.ts +3 -0
  32. package/i18n/en-US/components/content/blocks/block_quote.ts +3 -1
  33. package/i18n/en-US/components/content/blocks/feedback.ts +2 -0
  34. package/i18n/en-US/components/content/blocks/file_download.ts +2 -1
  35. package/i18n/en-US/components/content/blocks/index.ts +2 -0
  36. package/i18n/en-US/components/content/blocks/open_response.ts +19 -1
  37. package/i18n/en-US/components/content/blocks/open_response_collate.ts +1 -1
  38. package/i18n/en-US/components/content/blocks/scenario_choice.ts +2 -0
  39. package/i18n/en-US/components/content/blocks/user_upload.ts +2 -1
  40. package/i18n/en-US/components/settings/accordion.ts +2 -1
  41. package/i18n/en-US/components/settings/block_quote.ts +1 -1
  42. package/i18n/en-US/components/settings/clickable_icon.ts +5 -0
  43. package/i18n/en-US/components/settings/email.ts +2 -1
  44. package/i18n/en-US/components/settings/file_download.ts +2 -2
  45. package/i18n/en-US/components/settings/image.ts +1 -0
  46. package/i18n/en-US/components/settings/open_response.ts +8 -0
  47. package/i18n/en-US/components/settings/open_response_collate.ts +3 -0
  48. package/i18n/en-US/components/settings/scenario_choice.ts +3 -1
  49. package/i18n/en-US/components/settings/tab.ts +4 -3
  50. package/i18n/en-US/components/settings/user_upload.ts +1 -0
  51. package/i18n/en-US/components/settings/video.ts +3 -1
  52. package/i18n/en-US/shared/content_blocks.ts +1 -1
  53. package/i18n/es-ES/components/content/blocks/accordion.ts +3 -0
  54. package/i18n/es-ES/components/content/blocks/block_quote.ts +3 -1
  55. package/i18n/es-ES/components/content/blocks/feedback.ts +2 -0
  56. package/i18n/es-ES/components/content/blocks/file_download.ts +2 -1
  57. package/i18n/es-ES/components/content/blocks/index.ts +2 -0
  58. package/i18n/es-ES/components/content/blocks/open_response.ts +19 -2
  59. package/i18n/es-ES/components/content/blocks/open_response_collate.ts +1 -1
  60. package/i18n/es-ES/components/content/blocks/scenario_choice.ts +2 -0
  61. package/i18n/es-ES/components/content/blocks/user_upload.ts +2 -1
  62. package/i18n/es-ES/components/settings/accordion.ts +4 -2
  63. package/i18n/es-ES/components/settings/block_quote.ts +1 -1
  64. package/i18n/es-ES/components/settings/clickable_icon.ts +7 -0
  65. package/i18n/es-ES/components/settings/email.ts +2 -1
  66. package/i18n/es-ES/components/settings/image.ts +1 -0
  67. package/i18n/es-ES/components/settings/open_response.ts +8 -0
  68. package/i18n/es-ES/components/settings/open_response_collate.ts +3 -0
  69. package/i18n/es-ES/components/settings/scenario_choice.ts +3 -1
  70. package/i18n/es-ES/components/settings/tab.ts +3 -2
  71. package/i18n/es-ES/components/settings/user_upload.ts +1 -0
  72. package/i18n/es-ES/components/settings/video.ts +3 -1
  73. package/i18n/es-ES/shared/content_blocks.ts +1 -1
  74. package/i18n/sv-SE/components/content/blocks/accordion.ts +3 -0
  75. package/i18n/sv-SE/components/content/blocks/block_quote.ts +3 -1
  76. package/i18n/sv-SE/components/content/blocks/feedback.ts +2 -0
  77. package/i18n/sv-SE/components/content/blocks/file_download.ts +2 -1
  78. package/i18n/sv-SE/components/content/blocks/index.ts +2 -0
  79. package/i18n/sv-SE/components/content/blocks/open_response.ts +19 -2
  80. package/i18n/sv-SE/components/content/blocks/open_response_collate.ts +1 -1
  81. package/i18n/sv-SE/components/content/blocks/scenario_choice.ts +2 -0
  82. package/i18n/sv-SE/components/content/blocks/user_upload.ts +2 -1
  83. package/i18n/sv-SE/components/settings/accordion.ts +2 -1
  84. package/i18n/sv-SE/components/settings/block_quote.ts +1 -1
  85. package/i18n/sv-SE/components/settings/clickable_icon.ts +6 -0
  86. package/i18n/sv-SE/components/settings/email.ts +2 -1
  87. package/i18n/sv-SE/components/settings/image.ts +1 -0
  88. package/i18n/sv-SE/components/settings/open_response.ts +8 -0
  89. package/i18n/sv-SE/components/settings/open_response_collate.ts +3 -0
  90. package/i18n/sv-SE/components/settings/scenario_choice.ts +3 -1
  91. package/i18n/sv-SE/components/settings/tab.ts +5 -3
  92. package/i18n/sv-SE/components/settings/user_upload.ts +1 -0
  93. package/i18n/sv-SE/components/settings/video.ts +3 -1
  94. package/i18n/sv-SE/shared/content_blocks.ts +1 -1
  95. package/models/SurveyResultMetric.ts +8 -0
  96. package/package.json +2 -2
  97. package/plugin.js +8 -0
  98. package/test/Components/Content/Blocks/Feedback/FeedbackTemplates/FeedbackAnalytics.spec.js +23 -0
  99. package/test/Components/Content/Blocks/{FeedbackTemplates → Feedback/FeedbackTemplates}/FeedbackQuestionLikert.spec.js +1 -1
  100. package/test/Components/Content/Blocks/{FeedbackTemplates → Feedback/FeedbackTemplates}/FeedbackQuestionOpenResponse.spec.js +1 -1
  101. package/test/Components/Content/Blocks/{FeedbackTemplates → Feedback/FeedbackTemplates}/FeedbackQuestionTrueFalse.spec.js +1 -1
  102. package/test/Components/Settings/AccordionSettings.spec.js +0 -13
  103. package/test/Components/Settings/ClickableIconsSettings.spec.js +1 -12
  104. package/test/Components/Settings/EmailSettings.spec.js +0 -9
  105. package/test/Components/Settings/TabSettings.spec.js +0 -13
  106. package/test/helpers/GlossaryHelper.spec.js +8 -8
  107. package/components/Content/Blocks/{FeedbackTemplates → Feedback/FeedbackTemplates}/FeedbackQuestionLikert.vue +1 -1
  108. package/components/Content/Blocks/{FeedbackTemplates → Feedback/FeedbackTemplates}/FeedbackQuestionOpenResponse.vue +1 -1
  109. /package/components/Content/Blocks/{FeedbackTemplates → Feedback/FeedbackTemplates}/FeedbackQuestionTrueFalse.vue +0 -0
@@ -8,6 +8,9 @@
8
8
  </v-container>
9
9
  <v-divider class="my-4 primary"></v-divider>
10
10
  <v-container class="pa-0">
11
+ <p class="pt-4 mb-2">
12
+ {{ $t('windward.core.components.settings.block_quote.body') }}
13
+ </p>
11
14
  <TextEditor
12
15
  v-model="block.metadata.config.block_quote.quote"
13
16
  :rules="$Validation.getRule('longInput')"
@@ -15,9 +18,6 @@
15
18
  outlined
16
19
  auto-grow
17
20
  :height="200"
18
- :label="
19
- $t('windward.core.components.settings.block_quote.body')
20
- "
21
21
  :disabled="render"
22
22
  ></TextEditor>
23
23
  </v-container>
@@ -150,7 +150,9 @@ export default {
150
150
  this.block.metadata.config = {}
151
151
  }
152
152
  if (_.isEmpty(this.block.metadata.config.title)) {
153
- this.block.metadata.config.title = ''
153
+ this.block.metadata.config.title = this.$t(
154
+ 'windward.core.components.content.blocks.block_quote.title'
155
+ )
154
156
  }
155
157
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
156
158
  this.$set(this.block.metadata.config, 'display_title', true)
@@ -64,6 +64,13 @@
64
64
  :disabled="render"
65
65
  ></v-switch>
66
66
  </v-container>
67
+ <p>
68
+ {{
69
+ $t(
70
+ 'windward.core.components.settings.clickable_icon.item_header'
71
+ )
72
+ }}
73
+ </p>
67
74
  <v-col class="pa-0">
68
75
  <SortableExpansionPanel
69
76
  v-model="block.metadata.config.items"
@@ -217,7 +224,11 @@
217
224
  :disabled="render"
218
225
  @click="onAddElement"
219
226
  ><v-icon>mdi-plus</v-icon
220
- >{{ $t('shared.forms.add') }}</v-btn
227
+ >{{
228
+ $t(
229
+ 'windward.core.components.settings.clickable_icon.add_item'
230
+ )
231
+ }}</v-btn
221
232
  >
222
233
  </v-row>
223
234
  </v-container>
@@ -285,15 +296,22 @@ export default {
285
296
  this.block.metadata.config = {}
286
297
  }
287
298
  if (_.isEmpty(this.block.metadata.config.title)) {
288
- this.block.metadata.config.title = ''
299
+ this.block.metadata.config.title = this.$t(
300
+ 'windward.core.components.settings.clickable_icon.clickable_icon_title'
301
+ )
302
+ }
303
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
304
+ this.block.metadata.config.instructions = this.$t(
305
+ 'windward.core.components.settings.clickable_icon.instructions'
306
+ )
289
307
  }
290
308
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
291
309
  this.$set(this.block.metadata.config, 'display_title', true)
292
310
  }
293
311
  if (_.isEmpty(this.block.metadata.config.display)) {
294
312
  this.block.metadata.config.display = {
295
- show_title: false,
296
- show_background: false,
313
+ show_title: true,
314
+ show_background: true,
297
315
  round_icon: false,
298
316
  italic_icon: false,
299
317
  large_icon: false,
@@ -304,15 +322,11 @@ export default {
304
322
  this.block.metadata.config.items = []
305
323
  }
306
324
  },
307
- mounted() {
308
- if (this.block.metadata.config.items.length <= 0) {
309
- this.onAddElement()
310
- }
311
- },
325
+ mounted() {},
312
326
  methods: {
313
327
  onAddElement() {
314
328
  const defaultObject = {
315
- icon: '',
329
+ icon: 'mdi-star',
316
330
  fileConfig: {},
317
331
  iconImage: false,
318
332
  title: '',
@@ -180,7 +180,9 @@ export default {
180
180
  this.block.metadata.config = {}
181
181
  }
182
182
  if (_.isEmpty(this.block.metadata.config.title)) {
183
- this.block.metadata.config.title = ''
183
+ this.block.metadata.config.title = this.$t(
184
+ 'windward.core.components.content.blocks.email.email'
185
+ )
184
186
  }
185
187
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
186
188
  this.$set(this.block.metadata.config, 'display_title', true)
@@ -193,16 +195,6 @@ export default {
193
195
  }
194
196
  if (_.isEmpty(this.block.metadata.config.emails)) {
195
197
  this.block.metadata.config.emails = []
196
- const defaultObject = {
197
- from: '',
198
- to: '',
199
- cc: '',
200
- subject: '',
201
- body: '',
202
- tinymce_expand: false,
203
- initials: '',
204
- }
205
- this.block.metadata.config.emails.push(defaultObject)
206
198
  }
207
199
  this.block.body = this.$t(
208
200
  'windward.core.components.content.blocks.email.title'
@@ -15,8 +15,14 @@
15
15
  "
16
16
  :disabled="render"
17
17
  ></v-switch>
18
- <v-spacer class="my-6" />
19
-
18
+ <v-divider class="my-4 primary"></v-divider>
19
+ <p>
20
+ {{
21
+ $t(
22
+ 'windward.core.components.content.blocks.file_download.items_header'
23
+ )
24
+ }}
25
+ </p>
20
26
  <SortableExpansionPanel
21
27
  v-model="block.metadata.config.items"
22
28
  :disabled="render"
@@ -1,5 +1,10 @@
1
1
  <template>
2
2
  <v-container>
3
+ <BaseContentBlockSettings
4
+ v-model="block.metadata.config"
5
+ :disabled="render"
6
+ ></BaseContentBlockSettings>
7
+ <v-divider class="my-4 primary"></v-divider>
3
8
  <ImageAssetSettings
4
9
  v-model="block.metadata.config"
5
10
  :assets.sync="block.assets"
@@ -13,11 +18,14 @@
13
18
  import _ from 'lodash'
14
19
  import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
15
20
  import ImageAssetSettings from '~/components/Content/Settings/ImageAssetSettings.vue'
21
+ import BaseContentBlockSettings from '~/components/Content/Settings/BaseContentBlockSettings.vue'
22
+ import Uuid from '~/helpers/Uuid'
16
23
 
17
24
  export default {
18
25
  name: 'ImageSettings',
19
26
  components: {
20
27
  ImageAssetSettings,
28
+ BaseContentBlockSettings,
21
29
  },
22
30
  extends: BaseContentSettings,
23
31
  props: {
@@ -40,6 +48,24 @@ export default {
40
48
  if (_.isEmpty(this.block.metadata.config)) {
41
49
  this.block.metadata.config = {}
42
50
  }
51
+ // If the block is brand new and the title is missing then pre-set the word 'Image' to it
52
+ if (
53
+ !Uuid.test(this.block.id) &&
54
+ _.isEmpty(this.block.metadata.config.title)
55
+ ) {
56
+ this.block.metadata.config.title = this.$t(
57
+ 'windward.core.shared.content_blocks.title.image'
58
+ )
59
+ } else if (_.isEmpty(this.block.metadata.config.title)) {
60
+ // Otherwise make sure the title key at least exists
61
+ this.block.metadata.config.title = ''
62
+ }
63
+ if (!_.isBoolean(this.block.metadata.config.display_title)) {
64
+ this.$set(this.block.metadata.config, 'display_title', true)
65
+ }
66
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
67
+ this.block.metadata.config.instructions = ''
68
+ }
43
69
  if (_.isEmpty(this.block.metadata.config.asset)) {
44
70
  this.block.metadata.config.asset = null
45
71
  }
@@ -151,6 +151,16 @@ export default {
151
151
  if (_.isEmpty(this.block.metadata.config)) {
152
152
  this.block.metadata.config = {}
153
153
  }
154
+ if (_.isEmpty(this.block.metadata.config.title)) {
155
+ this.block.metadata.config.title = this.$t(
156
+ 'windward.core.components.settings.open_response_collate.title'
157
+ )
158
+ }
159
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
160
+ this.block.metadata.config.instructions = this.$t(
161
+ 'windward.core.components.settings.open_response_collate.block_instructions'
162
+ )
163
+ }
154
164
  if (_.isEmpty(this.block.metadata.config.linked)) {
155
165
  this.block.metadata.config.linked = []
156
166
  }
@@ -7,9 +7,7 @@
7
7
  ></BaseContentBlockSettings>
8
8
  </v-row>
9
9
  <p class="pt-4 mb-2">
10
- {{
11
- $t('windward.core.components.settings.open_response.question')
12
- }}
10
+ {{ $t('windward.core.components.settings.open_response.question') }}
13
11
  </p>
14
12
  <TextEditor
15
13
  id="block-settings-body"
@@ -26,10 +24,25 @@
26
24
  </p>
27
25
  <TextEditor
28
26
  id="block-settings-sample-response"
27
+ ref="sampleResponseEditor"
29
28
  v-model="block.metadata.config.sample_response"
30
29
  :height="200"
30
+ :rules="sampleResponseRules"
31
31
  :disabled="render"
32
32
  ></TextEditor>
33
+ <v-row class="pt-4">
34
+ <v-col cols="12">
35
+ <v-switch
36
+ v-model="block.metadata.config.ai_mode_for_student"
37
+ :label="
38
+ $t(
39
+ 'windward.core.components.settings.open_response.ai_mode_for_student'
40
+ )
41
+ "
42
+ :disabled="render"
43
+ ></v-switch>
44
+ </v-col>
45
+ </v-row>
33
46
  <p class="pt-4">
34
47
  {{
35
48
  $t(
@@ -86,6 +99,34 @@ export default {
86
99
  course: 'course/get',
87
100
  currentContent: 'content/get',
88
101
  }),
102
+ sampleResponseRules() {
103
+ return [
104
+ (value) => {
105
+ if (!this.block?.metadata?.config?.ai_mode_for_student) {
106
+ return true
107
+ }
108
+
109
+ const text = this.stripHtmlTags(value)
110
+ return (
111
+ !_.isEmpty(text) ||
112
+ this.$t(
113
+ 'windward.core.components.settings.open_response.validation.sample_response_required_ai_mode'
114
+ )
115
+ )
116
+ },
117
+ ]
118
+ },
119
+ },
120
+ watch: {
121
+ 'block.metadata.config.ai_mode_for_student'(newValue, oldValue) {
122
+ if (newValue === oldValue) {
123
+ return
124
+ }
125
+
126
+ this.$nextTick(() => {
127
+ this.$refs.sampleResponseEditor?.validate?.()
128
+ })
129
+ },
89
130
  },
90
131
  beforeMount() {
91
132
  if (_.isEmpty(this.block)) {
@@ -101,13 +142,17 @@ export default {
101
142
  this.block.metadata.config = {}
102
143
  }
103
144
  if (_.isEmpty(this.block.metadata.config.title)) {
104
- this.block.metadata.config.title = ''
145
+ this.block.metadata.config.title = this.$t(
146
+ 'windward.core.components.settings.open_response.title'
147
+ )
105
148
  }
106
149
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
107
150
  this.$set(this.block.metadata.config, 'display_title', true)
108
151
  }
109
152
  if (_.isEmpty(this.block.metadata.config.instructions)) {
110
- this.block.metadata.config.instructions = ''
153
+ this.block.metadata.config.instructions = this.$t(
154
+ 'windward.core.components.settings.open_response.instructions'
155
+ )
111
156
  }
112
157
  if (_.isEmpty(this.block.metadata.config.sample_response)) {
113
158
  this.block.metadata.config.sample_response = ''
@@ -115,9 +160,21 @@ export default {
115
160
  if (_.isEmpty(this.block.metadata.config.starting_text)) {
116
161
  this.block.metadata.config.starting_text = ''
117
162
  }
163
+ if (!_.isBoolean(this.block.metadata.config.ai_mode_for_student)) {
164
+ this.$set(this.block.metadata.config, 'ai_mode_for_student', false)
165
+ }
118
166
  },
119
167
  mounted() {},
120
168
  methods: {
169
+ stripHtmlTags(body) {
170
+ if (typeof body !== 'string') {
171
+ return ''
172
+ }
173
+
174
+ let text = body.replace(/&nbsp;/gi, ' ')
175
+ text = text.replace(/\u00A0/g, ' ')
176
+ return text.replace(/(<([^>]+)>)/gi, '').trim()
177
+ },
121
178
  onApplyGeneratedBlock(payload = {}) {
122
179
  if (_.isEmpty(payload)) {
123
180
  return
@@ -127,14 +184,17 @@ export default {
127
184
  const config = _.get(generatedBlock, 'metadata.config', {})
128
185
 
129
186
  this.block.body = _.get(generatedBlock, 'body', '') ?? ''
130
- this.block.metadata.config.title =
131
- _.get(config, 'title', '') ?? ''
187
+ this.block.metadata.config.title = _.get(config, 'title', '') ?? ''
132
188
  this.block.metadata.config.instructions =
133
189
  _.get(config, 'instructions', '') ?? ''
134
190
  this.block.metadata.config.sample_response =
135
191
  _.get(config, 'sample_response', '') ?? ''
136
192
  this.block.metadata.config.starting_text =
137
193
  _.get(config, 'starting_text', '') ?? ''
194
+ if (_.has(config, 'ai_mode_for_student')) {
195
+ this.block.metadata.config.ai_mode_for_student =
196
+ config.ai_mode_for_student
197
+ }
138
198
 
139
199
  if (_.has(config, 'display_title')) {
140
200
  this.block.metadata.config.display_title = config.display_title
@@ -8,7 +8,6 @@
8
8
  ></BaseContentBlockSettings>
9
9
  </v-col>
10
10
  <v-col class="pa-0">
11
- <v-divider class="my-4 primary"></v-divider>
12
11
  <v-select
13
12
  v-model="block.metadata.config.display_style"
14
13
  :items="displayStyles"
@@ -40,7 +39,15 @@
40
39
  :disabled="render"
41
40
  ></v-switch>
42
41
  </v-col>
42
+ <v-divider class="my-4 primary"></v-divider>
43
43
  <v-col class="pa-0">
44
+ <p>
45
+ {{
46
+ $t(
47
+ 'windward.core.components.settings.scenario_choice.choices'
48
+ )
49
+ }}
50
+ </p>
44
51
  <SortableExpansionPanel
45
52
  v-model="block.metadata.config.items"
46
53
  :disabled="render"
@@ -280,7 +287,9 @@ export default {
280
287
  this.block.metadata.config = {}
281
288
  }
282
289
  if (_.isEmpty(this.block.metadata.config.title)) {
283
- this.block.metadata.config.title = ''
290
+ this.block.metadata.config.title = this.$t(
291
+ 'windward.core.components.settings.scenario_choice.scenario_title'
292
+ )
284
293
  }
285
294
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
286
295
  this.$set(this.block.metadata.config, 'display_title', true)
@@ -311,9 +320,6 @@ export default {
311
320
  }
312
321
  },
313
322
  mounted() {
314
- if (this.block.metadata.config.items.length <= 0) {
315
- this.onAddElement()
316
- }
317
323
  this.isLinked =
318
324
  !_.isEmpty(this.block.metadata.config.link_content_id) &&
319
325
  !_.isEmpty(this.block.metadata.config.link_text)
@@ -152,7 +152,9 @@ export default {
152
152
  this.block.metadata.config = {}
153
153
  }
154
154
  if (_.isEmpty(this.block.metadata.config.title)) {
155
- this.block.metadata.config.title = ''
155
+ this.block.metadata.config.title = this.$t(
156
+ 'windward.core.shared.content_blocks.title.tab'
157
+ )
156
158
  }
157
159
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
158
160
  this.$set(this.block.metadata.config, 'display_title', true)
@@ -170,27 +172,10 @@ export default {
170
172
  this.block.metadata.config.currentTab = 0
171
173
  }
172
174
  if (_.isEmpty(this.block.metadata.config.items)) {
173
- const defaultObject = {
174
- tabHeader: '',
175
- expand: false,
176
- content: '',
177
- imageAsset: {
178
- display: {
179
- width: 100,
180
- margin: '',
181
- padding: '',
182
- },
183
- hideBackground: true,
184
- },
185
- }
186
175
  this.block.metadata.config.items = []
187
- this.block.metadata.config.items.push(defaultObject)
188
176
  }
189
177
  },
190
178
  mounted() {
191
- if (this.block.metadata.config.items.length <= 0) {
192
- this.onAddElement()
193
- }
194
179
  this.checkForCustomSpacing()
195
180
  },
196
181
  beforeDestroy() {
@@ -5,14 +5,20 @@
5
5
  v-model="block.metadata.config"
6
6
  :disabled="render"
7
7
  ></BaseContentBlockSettings>
8
- <p>
9
- {{
10
- $t(
11
- 'windward.core.components.settings.user_upload.max_file_size'
12
- )
13
- }}
14
- </p>
15
8
  </v-container>
9
+ <v-divider class="my-4 primary"></v-divider>
10
+ <p>
11
+ {{
12
+ $t('windward.core.components.settings.user_upload.files_header')
13
+ }}
14
+ </p>
15
+ <p>
16
+ {{
17
+ $t(
18
+ 'windward.core.components.settings.user_upload.max_file_size'
19
+ )
20
+ }}
21
+ </p>
16
22
  <v-switch
17
23
  v-model="block.metadata.config.uploadSettings.multiple"
18
24
  :label="
@@ -161,7 +167,9 @@ export default {
161
167
  this.block.metadata.config = {}
162
168
  }
163
169
  if (_.isEmpty(this.block.metadata.config.title)) {
164
- this.block.metadata.config.title = ''
170
+ this.block.metadata.config.title = this.$t(
171
+ 'windward.core.components.content.blocks.user_upload.title'
172
+ )
165
173
  }
166
174
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
167
175
  this.$set(this.block.metadata.config, 'display_title', true)
@@ -74,12 +74,17 @@
74
74
  }}
75
75
  </v-alert>
76
76
 
77
- <ContentBlockAsset
78
- v-if="sourceInherit && hasLinkedCaptions"
79
- :value="linkedCaptions"
80
- :assets="assets"
81
- disabled
82
- ></ContentBlockAsset>
77
+ <!-- Display ALL linked captions when inheriting -->
78
+ <div v-if="sourceInherit && hasLinkedCaptions">
79
+ <div v-for="(caption, index) in linkedCaptions" :key="caption.file_asset_id || index" class="mb-2">
80
+ <ContentBlockAsset
81
+ :value="caption"
82
+ :assets="assets"
83
+ :label="getCaptionLabel(caption)"
84
+ disabled
85
+ ></ContentBlockAsset>
86
+ </div>
87
+ </div>
83
88
 
84
89
  <ContentBlockAsset
85
90
  v-if="!sourceInherit"
@@ -169,19 +174,12 @@ export default {
169
174
  data() {
170
175
  return {
171
176
  sourceInherit: true,
172
- linkedCaptions: null,
177
+ linkedCaptions: [],
173
178
  }
174
179
  },
175
180
  computed: {
176
181
  hasLinkedCaptions() {
177
- return this.linkedCaptions !== null
178
- },
179
- linkedCaptionsName() {
180
- return _.get(
181
- this.linkedCaptions,
182
- 'asset.name',
183
- _.get(this.linkedCaptions, 'asset.public_url', '???')
184
- )
182
+ return this.linkedCaptions && this.linkedCaptions.length > 0
185
183
  },
186
184
  isFromUrl() {
187
185
  if (
@@ -196,7 +194,26 @@ export default {
196
194
  return false
197
195
  },
198
196
  },
199
- watch: {},
197
+ watch: {
198
+ // If the underlying assets change (e.g., new transcript linked),
199
+ // recompute the linked captions so the UI chip reflects the latest VTT.
200
+ assets: {
201
+ deep: true,
202
+ handler() {
203
+ if (this.source) {
204
+ this.linkedCaptions = this.getLinkedCaptions(this.source)
205
+ }
206
+ },
207
+ },
208
+ source: {
209
+ deep: true,
210
+ handler(newSource) {
211
+ if (newSource) {
212
+ this.linkedCaptions = this.getLinkedCaptions(newSource)
213
+ }
214
+ },
215
+ },
216
+ },
200
217
  beforeMount() {},
201
218
  mounted() {
202
219
  this.linkedCaptions = this.getLinkedCaptions(this.source)
@@ -206,7 +223,7 @@ export default {
206
223
  methods: {
207
224
  onSourceChange(file, rawFile) {
208
225
  this.linkedCaptions = this.getLinkedCaptions(rawFile)
209
- this.sourceInherit = this.linkedCaptions !== null
226
+ this.sourceInherit = this.hasLinkedCaptions
210
227
 
211
228
  this.$emit('change:source', file, rawFile)
212
229
  },
@@ -219,19 +236,36 @@ export default {
219
236
  onUpdateAssets(assets) {
220
237
  this.$emit('update:assets', assets)
221
238
  },
239
+ /**
240
+ * Get ALL linked captions (not just the first one)
241
+ * @param {Object} file - The video file object
242
+ * @returns {Array} Array of VTT caption files
243
+ */
222
244
  getLinkedCaptions(file) {
223
245
  if (!file) {
224
- return null
246
+ return []
225
247
  }
226
- // Check to see if the video source has a linked asset and it's a vtt file
227
- const linkedCaption = _.find(
248
+ // Get ALL VTT files from linked_assets (not just the first one)
249
+ const linkedCaptions = _.filter(
228
250
  _.get(file, 'asset.linked_assets', []),
229
251
  function (f) {
230
252
  return _.get(f, 'asset.metadata.extension', '') === 'vtt'
231
253
  }
232
254
  )
233
255
 
234
- return linkedCaption || null
256
+ return linkedCaptions || []
257
+ },
258
+ /**
259
+ * Get the label with locale in parentheses for a caption
260
+ * Format: "(EN-US)" - if no locale, assume English
261
+ * @param {Object} caption - The caption file object
262
+ * @returns {string} Label with locale code
263
+ */
264
+ getCaptionLabel(caption) {
265
+ const localeCode = _.get(caption, 'locale.code') ||
266
+ _.get(caption, 'asset.metadata.locale') ||
267
+ 'EN-US'
268
+ return `(${localeCode})`
235
269
  },
236
270
  },
237
271
  }
@@ -14,7 +14,7 @@
14
14
  )
15
15
  "
16
16
  ></BaseContentBlockSettings>
17
-
17
+ <v-divider class="my-4 primary"></v-divider>
18
18
  <h4 class="mb-3">
19
19
  {{ $t('windward.core.components.settings.video.sources') }}
20
20
  </h4>
@@ -423,7 +423,18 @@ export default {
423
423
  return this.playlistPaginator - 1
424
424
  },
425
425
  },
426
- watch: {},
426
+ watch: {
427
+ // When the underlying block assets change (for example, when a new
428
+ // transcript VTT is linked to the source video), rebuild the local
429
+ // media model so the settings sidebar immediately reflects the
430
+ // latest captions without requiring a save or reload.
431
+ 'block.assets': {
432
+ deep: true,
433
+ handler(newAssets) {
434
+ this.reloadMedia()
435
+ },
436
+ },
437
+ },
427
438
  beforeMount() {
428
439
  // Create structure for first playlist source
429
440
  if (_.isEmpty(_.get(this.block.metadata.config, `playlist`, null))) {
@@ -433,6 +444,11 @@ export default {
433
444
  if (!_.isBoolean(this.block.metadata.config.display_title)) {
434
445
  this.$set(this.block.metadata.config, 'display_title', true)
435
446
  }
447
+ if (_.isEmpty(this.block.metadata.config.title)) {
448
+ this.block.metadata.config.title = this.$t(
449
+ 'windward.core.components.settings.video.prebuilt_title'
450
+ )
451
+ }
436
452
  },
437
453
  mounted() {
438
454
  this.reloadMedia()