@windward/core 0.4.3 → 0.5.1

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 (54) hide show
  1. package/components/Content/Blocks/Accordion.vue +10 -6
  2. package/components/Content/Blocks/BlockQuote.vue +81 -98
  3. package/components/Content/Blocks/ClickableIcons.vue +6 -5
  4. package/components/Content/Blocks/Feedback.vue +1 -1
  5. package/components/Content/Blocks/FileDownload/FileLinks.vue +82 -0
  6. package/components/Content/Blocks/FileDownload/FileTable.vue +106 -0
  7. package/components/Content/Blocks/FileDownload.vue +145 -0
  8. package/components/Content/Blocks/GenerateAIQuestionButton.vue +61 -0
  9. package/components/Content/Blocks/ScenarioChoice.vue +2 -2
  10. package/components/Content/Blocks/UserUpload.vue +15 -36
  11. package/components/Content/Blocks/Video.vue +7 -57
  12. package/components/Settings/AccordionSettings.vue +3 -6
  13. package/components/Settings/BlockQuoteSettings.vue +44 -14
  14. package/components/Settings/EmailSettings.vue +2 -2
  15. package/components/Settings/FileDownloadSettings.vue +168 -0
  16. package/components/Settings/UserUploadSettings.vue +36 -39
  17. package/components/Settings/VideoSettings.vue +15 -2
  18. package/components/utils/ContentViewer.vue +1 -0
  19. package/components/utils/TinyMCEWrapper.vue +14 -16
  20. package/components/utils/assets/tinymce/content/global.scss +10 -0
  21. package/i18n/en-US/components/content/blocks/file_download.ts +5 -0
  22. package/i18n/en-US/components/content/blocks/generate_questions.ts +3 -0
  23. package/i18n/en-US/components/content/blocks/index.ts +4 -0
  24. package/i18n/en-US/components/content/blocks/user_upload.ts +0 -2
  25. package/i18n/en-US/components/settings/block_quote.ts +6 -0
  26. package/i18n/en-US/components/settings/file_download.ts +8 -0
  27. package/i18n/en-US/components/settings/index.ts +2 -0
  28. package/i18n/en-US/shared/content_blocks.ts +1 -0
  29. package/i18n/en-US/shared/settings.ts +1 -3
  30. package/i18n/es-ES/components/content/blocks/file_download.ts +5 -0
  31. package/i18n/es-ES/components/content/blocks/generate_questions.ts +3 -0
  32. package/i18n/es-ES/components/content/blocks/index.ts +6 -2
  33. package/i18n/es-ES/components/content/blocks/user_upload.ts +0 -2
  34. package/i18n/es-ES/components/settings/block_quote.ts +7 -1
  35. package/i18n/es-ES/components/settings/file_download.ts +8 -0
  36. package/i18n/es-ES/components/settings/index.ts +4 -2
  37. package/i18n/es-ES/shared/content_blocks.ts +1 -0
  38. package/i18n/es-ES/shared/settings.ts +1 -3
  39. package/i18n/sv-SE/components/content/blocks/file_download.ts +5 -0
  40. package/i18n/sv-SE/components/content/blocks/generate_questions.ts +3 -0
  41. package/i18n/sv-SE/components/content/blocks/index.ts +4 -0
  42. package/i18n/sv-SE/components/content/blocks/user_upload.ts +0 -2
  43. package/i18n/sv-SE/components/settings/block_quote.ts +7 -1
  44. package/i18n/sv-SE/components/settings/file_download.ts +8 -0
  45. package/i18n/sv-SE/components/settings/index.ts +4 -2
  46. package/i18n/sv-SE/shared/content_blocks.ts +1 -0
  47. package/i18n/sv-SE/shared/settings.ts +2 -4
  48. package/package.json +2 -2
  49. package/plugin.js +20 -0
  50. package/test/Components/Content/Blocks/FileDownload.spec.js +25 -0
  51. package/test/Components/Content/Blocks/Video.spec.js +0 -22
  52. package/test/Components/Settings/FileDownloadSettings.spec.js +20 -0
  53. package/test/Components/Settings/UserUploadSettings.spec.js +1 -1
  54. package/utils/index.js +2 -0
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <v-btn
3
+ elevation="0"
4
+ color="primary"
5
+ icon
6
+ @click="generateAIQuestion"
7
+ :loading="isLoading"
8
+ :disabled="isLoading"
9
+ >
10
+ <v-icon v-if="!isLoading">mdi-magic-staff</v-icon>
11
+ <template v-slot:loader>
12
+ <v-progress-circular indeterminate size="23"></v-progress-circular>
13
+ </template>
14
+ </v-btn>
15
+ </template>
16
+
17
+ <script>
18
+ import AssessmentQuestion from '~/models/AssessmentQuestion'
19
+ import Course from '~/models/Course'
20
+ import Assessment from '~/models/Assessment'
21
+ import Content from '~/models/Content'
22
+
23
+ export default {
24
+ name: 'GenerateAIQuestionButton',
25
+ props: {
26
+ course: { type: Object, required: true },
27
+ content: { type: Object, required: true },
28
+ block: { type: Object, required: true },
29
+ questionType: { type: String, required: true },
30
+ },
31
+ data() {
32
+ return {
33
+ isLoading: false,
34
+ }
35
+ },
36
+ methods: {
37
+ async generateAIQuestion() {
38
+ this.isLoading = true
39
+ try {
40
+ const response = await AssessmentQuestion.custom(
41
+ new Course(this.course),
42
+ new Content(this.content),
43
+ new Assessment({ id: this.block.id }),
44
+ new AssessmentQuestion(),
45
+ `suggest/${this.questionType}`
46
+ ).get()
47
+
48
+ if (response && response.length > 0) {
49
+ const generatedQuestion = response[0]
50
+ this.$emit('click:generate', generatedQuestion)
51
+ }
52
+ } catch (error) {
53
+ console.error(error)
54
+ this.$dialog.error(this.$t('windward.core.components.content.blocks.generate_questions.error'))
55
+ } finally {
56
+ this.isLoading = false
57
+ }
58
+ },
59
+ },
60
+ }
61
+ </script>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <v-container>
3
+ <v-container class="pa-0">
4
4
  <h2>{{ block.metadata.config.title }}</h2>
5
5
  <h4>{{ block.metadata.config.description }}</h4>
6
6
  <p>
@@ -23,7 +23,7 @@
23
23
  </v-btn>
24
24
  </div>
25
25
  </v-container>
26
- <v-container>
26
+ <v-container class="pa-0">
27
27
  <v-row
28
28
  v-for="(item, itemIndex) in block.metadata.config.items"
29
29
  :key="itemIndex"
@@ -1,26 +1,22 @@
1
1
  <template>
2
- <v-container>
3
- <div v-if="render || !block.metadata.config.expand">
2
+ <v-container class="pa-0">
3
+ <div>
4
+ <h2 v-if="block.metadata.config.title">
5
+ {{ block.metadata.config.title }}
6
+ </h2>
4
7
  <v-row>
5
8
  <v-col cols="12">
6
- {{
7
- $t(
8
- 'windward.core.components.content.blocks.user_upload.instructions'
9
- )
10
- }}
11
- <p v-if="!block.metadata.config.instructions">
12
- {{
13
- $t(
14
- 'windward.core.components.content.blocks.user_upload.instructions_none'
15
- )
16
- }}
17
- </p>
18
9
  <TextViewer
10
+ v-if="render || !block.__expandInstructions"
19
11
  v-model="block.metadata.config.instructions"
20
12
  ></TextViewer>
13
+ <TextEditor
14
+ v-if="!render && block.__expandInstructions"
15
+ v-model="block.metadata.config.instructions"
16
+ />
21
17
  </v-col>
22
- <v-col cols="12">
23
- <v-alert v-if="!blockExists" color="warning">
18
+ <v-col v-if="!blockExists" cols="12">
19
+ <v-alert color="warning">
24
20
  <p>
25
21
  {{
26
22
  $t(
@@ -73,20 +69,6 @@
73
69
  </v-col>
74
70
  </v-row>
75
71
  </div>
76
- <div v-if="!render && block.metadata.config.expand">
77
- <v-row>
78
- <v-col cols="12">
79
- <h3 class="pb-4">
80
- {{
81
- $t(
82
- 'windward.core.components.content.blocks.user_upload.instructions_title'
83
- )
84
- }}
85
- </h3>
86
- <TextEditor v-model="block.metadata.config.instructions" />
87
- </v-col>
88
- </v-row>
89
- </div>
90
72
  </v-container>
91
73
  </template>
92
74
 
@@ -119,6 +101,9 @@ export default {
119
101
  if (_.isEmpty(this.block)) {
120
102
  this.block = {}
121
103
  }
104
+ // Default the instructions to collapsed
105
+ this.block.__expandInstructions = false
106
+
122
107
  if (_.isEmpty(this.block.body)) {
123
108
  this.block.body = this.$t(
124
109
  'windward.core.shared.content_blocks.title.user_upload'
@@ -130,9 +115,6 @@ export default {
130
115
  if (_.isEmpty(this.block.metadata.config)) {
131
116
  this.block.metadata.config = {}
132
117
  }
133
- if (_.isEmpty(this.block.metadata.config.expand)) {
134
- this.block.metadata.config.expand = false
135
- }
136
118
  if (_.isEmpty(this.block.metadata.config.instructions)) {
137
119
  this.block.metadata.config.instructions = ''
138
120
  }
@@ -271,9 +253,6 @@ export default {
271
253
  .first()
272
254
  }
273
255
  },
274
- async onBeforeSave() {
275
- return (this.block.metadata.config.expand = false)
276
- },
277
256
  },
278
257
  }
279
258
  </script>
@@ -21,9 +21,12 @@
21
21
  type="image, image, list-item-avatar"
22
22
  class="reload-skeleton"
23
23
  ></v-skeleton-loader>
24
- <h2 v-if="block.metadata.config.title" class="pl-4">
24
+ <h2 v-if="block.metadata.config.title">
25
25
  {{ block.metadata.config.title }}
26
26
  </h2>
27
+ <p v-if="block.metadata.config.description" class="pb-0 mb-0">
28
+ {{ block.metadata.config.description }}
29
+ </p>
27
30
  <VuetifyPlayer
28
31
  v-if="hasSource"
29
32
  :language="$i18n && $i18n.locale ? $i18n.locale : 'en-US'"
@@ -158,12 +161,11 @@ export default {
158
161
  data() {
159
162
  return {
160
163
  saveState: false,
161
-
162
164
  fileTab: null,
163
-
164
- saveKey: null, // Used for checking changes in settings
165
165
  // Default config settings
166
166
  defaultConfig: {
167
+ title: '',
168
+ description: '',
167
169
  // Default settings for new blocks
168
170
  // This will be overridden in beforeMount()
169
171
  type: 'video', // Allowed video|audio. In audio mode the player has a max-height of 40px
@@ -247,63 +249,11 @@ export default {
247
249
  this.$set(this.block, 'assets', [])
248
250
  }
249
251
  },
250
- mounted() {
251
- this.saveKey = this.settingsKey()
252
- },
252
+ mounted() {},
253
253
  methods: {
254
- /**
255
- * Hash out the settings to see if there's any changes
256
- */
257
- settingsKey() {
258
- const str = JSON.stringify(this.block.metadata.config)
259
- return str.split('').reduce(function (a, b) {
260
- a = (a << 5) - a + b.charCodeAt(0)
261
- return a & a
262
- }, 0)
263
- },
264
- async onActionPanelEdit() {
265
- // Push any setting changes up
266
- await this.onBeforeSave
267
-
268
- // We're moving from edit mode to render mode
269
- // Prompt about any unsaved changes
270
- if (!this.render && this.settingsKey() !== this.saveKey) {
271
- const self = this
272
- this.$toast.info(this.$t('shared.forms.unsaved'), {
273
- icon: 'mdi-help',
274
- duration: null,
275
- action: [
276
- {
277
- text: this.$t('shared.forms.cancel'),
278
- onClick: (e, toastObject) => {
279
- toastObject.goAway(0)
280
- self.render = true
281
- },
282
- },
283
- {
284
- text: this.$t('shared.forms.confirm'),
285
- // router navigation
286
- onClick: async (e, toastObject) => {
287
- await this.onBeforeSave()
288
- // Saved, rehash the setting changes key
289
- this.saveKey = this.settingsKey()
290
-
291
- self.render = true
292
- toastObject.goAway(0)
293
- },
294
- },
295
- ],
296
- })
297
- } else {
298
- this.render = !this.render
299
- }
300
- },
301
254
  async onBeforeSave() {
302
255
  this.block.body = 'video'
303
256
  },
304
- onAfterSave() {
305
- this.saveKey = this.settingsKey()
306
- },
307
257
  },
308
258
  }
309
259
  </script>
@@ -18,9 +18,7 @@
18
18
  <template #header="{ item }">{{
19
19
  item.header
20
20
  ? item.header
21
- : $t(
22
- 'windward.core.shared.settings.title.placeholder'
23
- )
21
+ : $t('components.content.settings.base.placeholder')
24
22
  }}</template>
25
23
  <template #body="{ index }">
26
24
  <v-container :key="expansionPanelKey">
@@ -31,9 +29,7 @@
31
29
  :autofocus="true"
32
30
  outlined
33
31
  :label="
34
- $t(
35
- 'windward.core.shared.settings.title.title'
36
- )
32
+ $t('components.content.settings.base.title')
37
33
  "
38
34
  :disabled="render"
39
35
  @focus="onTextAreaFocus"
@@ -41,6 +37,7 @@
41
37
  <v-btn
42
38
  text
43
39
  elevation="0"
40
+ class="mb-3"
44
41
  :disabled="render"
45
42
  @click="
46
43
  onToggleExpand(
@@ -7,7 +7,7 @@
7
7
  outlined
8
8
  :counter="50"
9
9
  :autofocus="true"
10
- :label="$t('windward.core.shared.settings.title.title')"
10
+ :label="$t('components.content.settings.base.title')"
11
11
  ref="title"
12
12
  :disabled="render"
13
13
  ></v-text-field>
@@ -16,22 +16,20 @@
16
16
  outlined
17
17
  auto-grow
18
18
  :counter="255"
19
- :label="$t('windward.core.shared.settings.title.instructions')"
19
+ :label="$t('components.content.settings.base.instructions')"
20
20
  :disabled="render"
21
21
  ></v-textarea>
22
22
  </v-container>
23
23
  <v-divider class="my-4 primary"></v-divider>
24
24
  <v-container class="pa-0">
25
- <v-textarea
25
+ <TextEditor
26
26
  v-model="block.metadata.config.block_quote.quote"
27
- outlined
28
- auto-grow
29
- :counter="255"
27
+ :height="200"
30
28
  :label="
31
29
  $t('windward.core.components.settings.block_quote.body')
32
30
  "
33
31
  :disabled="render"
34
- ></v-textarea>
32
+ ></TextEditor>
35
33
  </v-container>
36
34
  <v-divider class="my-4 primary"></v-divider>
37
35
  <v-container class="pa-0">
@@ -80,6 +78,21 @@
80
78
  "
81
79
  :disabled="render"
82
80
  ></v-select>
81
+ <v-text-field
82
+ v-if="
83
+ block.metadata.config.block_quote.source_type ===
84
+ 'online_journal'
85
+ "
86
+ v-model="block.metadata.config.block_quote.source_url"
87
+ outlined
88
+ auto-grow
89
+ :label="
90
+ $t(
91
+ 'windward.core.components.settings.block_quote.source_url'
92
+ )
93
+ "
94
+ :disabled="render"
95
+ ></v-text-field>
83
96
  </v-container>
84
97
  <div v-if="loading" class="text-center">
85
98
  <v-progress-circular
@@ -93,11 +106,13 @@
93
106
  </template>
94
107
  <script>
95
108
  import _ from 'lodash'
109
+ import TextEditor from '~/components/Text/TextEditor.vue'
96
110
  import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
97
111
 
98
112
  export default {
99
113
  name: 'BlockQuoteSettings',
100
114
  extends: BaseContentSettings,
115
+ components: { TextEditor },
101
116
  beforeMount() {
102
117
  if (_.isEmpty(this.block)) {
103
118
  this.block = {}
@@ -122,6 +137,7 @@ export default {
122
137
  organization: '',
123
138
  source_title: '',
124
139
  source_type: '',
140
+ source_url: '',
125
141
  }
126
142
  }
127
143
  this.block.body = this.$t(
@@ -132,15 +148,29 @@ export default {
132
148
  return {
133
149
  valid: true,
134
150
  loading: false,
135
- sourceTypes: ['None', 'Book', 'Online Journal'],
136
- }
137
- },
138
- beforeDestroy() {
139
- if (this.debouncer) {
140
- clearTimeout(this.debouncer)
151
+ sourceTypes: [
152
+ {
153
+ text: this.$t(
154
+ 'windward.core.components.settings.block_quote.source_types.none'
155
+ ),
156
+ value: '',
157
+ },
158
+
159
+ {
160
+ text: this.$t(
161
+ 'windward.core.components.settings.block_quote.source_types.book'
162
+ ),
163
+ value: 'book',
164
+ },
165
+ {
166
+ text: this.$t(
167
+ 'windward.core.components.settings.block_quote.source_types.online_journal'
168
+ ),
169
+ value: 'online_journal',
170
+ },
171
+ ],
141
172
  }
142
173
  },
143
-
144
174
  methods: {},
145
175
  }
146
176
  </script>
@@ -8,7 +8,7 @@
8
8
  outlined
9
9
  :counter="50"
10
10
  :autofocus="true"
11
- :label="$t('windward.core.shared.settings.title.title')"
11
+ :label="$t('components.content.settings.base.title')"
12
12
  :disabled="render"
13
13
  ></v-text-field>
14
14
  <v-textarea
@@ -16,7 +16,7 @@
16
16
  outlined
17
17
  auto-grow
18
18
  :counter="255"
19
- :label="$t('windward.core.shared.settings.title.instructions')"
19
+ :label="$t('components.content.settings.base.instructions')"
20
20
  :disabled="render"
21
21
  ></v-textarea>
22
22
  </v-container>
@@ -0,0 +1,168 @@
1
+ <template>
2
+ <div>
3
+ <v-text-field
4
+ v-model="block.metadata.config.title"
5
+ outlined
6
+ :counter="50"
7
+ :autofocus="true"
8
+ :label="$t('components.content.settings.base.title_optional')"
9
+ :disabled="render"
10
+ ></v-text-field>
11
+ <TextEditor
12
+ v-model="block.metadata.config.instructions"
13
+ :disabled="render"
14
+ :label="
15
+ $t('components.content.settings.base.instructions_optional')
16
+ "
17
+ />
18
+ <v-switch
19
+ v-model="block.metadata.config.display_detailed"
20
+ :label="
21
+ $t(
22
+ 'windward.core.components.settings.file_download.display_detailed'
23
+ )
24
+ "
25
+ :disabled="render"
26
+ ></v-switch>
27
+ <v-spacer class="my-6" />
28
+
29
+ <SortableExpansionPanel
30
+ v-model="block.metadata.config.items"
31
+ :disabled="render"
32
+ @click:close="onRemoveElement($event)"
33
+ >
34
+ <template #header="{ item }">{{ filename(item) }}</template>
35
+ <template #body="{ index }">
36
+ <v-container>
37
+ <v-text-field
38
+ v-model="block.metadata.config.items[index].name"
39
+ :autofocus="true"
40
+ outlined
41
+ :label="
42
+ $t(
43
+ 'windward.core.components.settings.file_download.filename_optional'
44
+ )
45
+ "
46
+ :disabled="render"
47
+ ></v-text-field>
48
+ <v-container class="pa-0 pt-3">
49
+ <ContentBlockAsset
50
+ v-model="block.metadata.config.items[index].file"
51
+ :assets.sync="block.assets"
52
+ :label="$t('shared.file.file')"
53
+ :disabled="render"
54
+ >
55
+ <template #title>
56
+ {{
57
+ $t(
58
+ 'windward.core.components.settings.file_download.place_file'
59
+ )
60
+ }}
61
+ </template>
62
+ </ContentBlockAsset>
63
+ </v-container>
64
+ </v-container>
65
+ </template>
66
+ </SortableExpansionPanel>
67
+
68
+ <v-container class="pa-0">
69
+ <v-row justify="center" class="my-4">
70
+ <v-btn
71
+ color="primary"
72
+ elevation="0"
73
+ :disabled="render"
74
+ @click="onAddElement"
75
+ ><v-icon>mdi-plus</v-icon
76
+ >{{
77
+ $t(
78
+ 'windward.core.components.settings.file_download.add'
79
+ )
80
+ }}</v-btn
81
+ >
82
+ </v-row>
83
+ </v-container>
84
+ </div>
85
+ </template>
86
+ <script>
87
+ import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
88
+ import TextEditor from '~/components/Text/TextEditor'
89
+ import _ from 'lodash'
90
+ import SortableExpansionPanel from '~/components/Core/SortableExpansionPanel.vue'
91
+
92
+ export default {
93
+ name: 'FileDownloadSettings',
94
+ extends: BaseContentSettings,
95
+ components: { TextEditor, SortableExpansionPanel },
96
+ data() {
97
+ return {}
98
+ },
99
+ computed: {
100
+ filename() {
101
+ return (item) => {
102
+ let filename = item.name
103
+
104
+ if (!filename && item.file) {
105
+ const asset = this.resolveAsset(item.file)
106
+ filename =
107
+ _.get(asset, 'name', '') ||
108
+ _.get(asset, 'asset.name', '')
109
+ }
110
+
111
+ if (!filename && item.file) {
112
+ filename = this.$t(
113
+ 'windward.core.components.settings.file_download.no_file_selected'
114
+ )
115
+ }
116
+ return filename
117
+ }
118
+ },
119
+ },
120
+ beforeMount() {
121
+ if (_.isEmpty(this.block)) {
122
+ this.block = {}
123
+ }
124
+ if (_.isEmpty(this.block.body)) {
125
+ this.block.body = this.$t(
126
+ 'windward.core.shared.content_blocks.title.file_download'
127
+ )
128
+ }
129
+ if (_.isEmpty(this.block.metadata)) {
130
+ this.block.metadata = {}
131
+ }
132
+ if (_.isEmpty(this.block.metadata.config)) {
133
+ this.block.metadata.config = {}
134
+ }
135
+ if (_.isEmpty(this.block.metadata.config.title)) {
136
+ this.block.metadata.config.title = ''
137
+ }
138
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
139
+ this.block.metadata.config.instructions = ''
140
+ }
141
+ if (!_.isBoolean(this.block.metadata.config.display_detailed)) {
142
+ this.block.metadata.config.display_detailed = false
143
+ }
144
+
145
+ if (_.isEmpty(this.block.metadata.config.items)) {
146
+ this.block.metadata.config.items = []
147
+ }
148
+ },
149
+ mounted() {},
150
+ methods: {
151
+ onAddElement() {
152
+ const defaultItem = {
153
+ name: '',
154
+ file: null,
155
+ }
156
+ this.block.metadata.config.items.push(defaultItem)
157
+ },
158
+ onRemoveElement(index) {
159
+ this.block.metadata.config.items.splice(index, 1)
160
+ },
161
+ },
162
+ }
163
+ </script>
164
+ <style scoped>
165
+ .v-progress-circular {
166
+ margin: 1rem;
167
+ }
168
+ </style>