@windward/core 0.13.0 → 0.15.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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/bitbucket-pipelines.yml +8 -0
  3. package/components/Content/Blocks/UserUpload/ManageDataTableUserFiles.vue +2 -11
  4. package/components/Content/Blocks/Video.vue +1 -1
  5. package/components/Glossary/GlossaryVerification.vue +240 -0
  6. package/components/Navigation/Items/AskTheExpert.vue +1 -37
  7. package/components/Navigation/Items/GlossaryNav.vue +8 -2
  8. package/components/Navigation/Items/UserUploadNav.vue +10 -2
  9. package/components/Settings/AccordionSettings.vue +37 -27
  10. package/components/Settings/ClickableIconsSettings.vue +34 -7
  11. package/components/Settings/ImageSettings.vue +5 -12
  12. package/components/Settings/TabSettings.vue +33 -23
  13. package/components/Settings/TextEditorSettings.vue +16 -245
  14. package/components/Settings/VideoSettings.vue +1 -1
  15. package/components/utils/ContentViewer.vue +3 -3
  16. package/components/utils/TinyMCEWrapper.vue +24 -14
  17. package/components/utils/glossary/CourseGlossary.vue +66 -91
  18. package/components/utils/glossary/CourseGlossaryForm.vue +42 -13
  19. package/components/utils/glossary/GlossaryToolTip.vue +7 -11
  20. package/helpers/GlossaryHelper.ts +18 -16
  21. package/i18n/en-US/pages/glossary.ts +3 -1
  22. package/i18n/en-US/shared/notification.ts +5 -0
  23. package/i18n/en-US/shared/permission.ts +4 -0
  24. package/i18n/es-ES/pages/glossary.ts +3 -1
  25. package/i18n/es-ES/shared/notification.ts +5 -0
  26. package/i18n/es-ES/shared/permission.ts +8 -4
  27. package/i18n/sv-SE/pages/glossary.ts +3 -1
  28. package/i18n/sv-SE/shared/notification.ts +4 -0
  29. package/i18n/sv-SE/shared/permission.ts +8 -4
  30. package/jest.config.js +3 -0
  31. package/models/BaseModel.ts +16 -0
  32. package/models/CourseGlossaryTerm.ts +22 -0
  33. package/models/UserFileAsset.ts +1 -0
  34. package/package.json +4 -3
  35. package/pages/glossary.vue +11 -3
  36. package/pages/userUpload.vue +1 -1
  37. package/plugin.js +32 -22
  38. package/store/glossary.js +57 -0
  39. package/test/Components/Content/Blocks/Feedback.spec.js +3 -1
  40. package/test/Components/Content/Blocks/Video.spec.js +2 -2
  41. package/test/Components/Glossary/GlossaryVerification.spec.js +19 -0
  42. package/test/Components/utils/glossary/CourseGlossary.spec.js +19 -0
  43. package/test/Components/utils/glossary/CourseGlossaryForm.spec.js +28 -0
  44. package/test/__mocks__/helpersMock.js +0 -24
  45. package/test/__mocks__/modelMock.js +5 -0
  46. package/test/helpers/GlossaryHelper.spec.js +32 -14
  47. package/test/mocks.js +11 -0
  48. package/test/setup/before.js +15 -0
  49. package/helpers/GlossaryTerm.ts +0 -31
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ### Release [0.15.0] created - 2025-04-09
4
+
5
+
6
+ ### Release [0.14.0] created - 2025-03-25
7
+
8
+
3
9
  ### Release [0.16.0] created - 2025-03-11
4
10
 
5
11
 
@@ -1,6 +1,14 @@
1
1
  image: atlassian/default-image:3
2
2
 
3
3
  pipelines:
4
+ pull-requests:
5
+ '**':
6
+ - step:
7
+ name: Build and test
8
+ image: node:16.20.2
9
+ script:
10
+ - npm install
11
+ - npm run test
4
12
  custom:
5
13
  create-release:
6
14
  import: infrastructure-automation:master:create-release
@@ -1,9 +1,9 @@
1
1
  <template>
2
- <div class="text-center">
2
+ <div>
3
3
  <DialogBox
4
4
  v-bind="$attrs"
5
5
  transition="dialog-bottom-transition"
6
- :color="color"
6
+ color="primary"
7
7
  v-if="value.file_assets && value.file_assets.length > 0"
8
8
  >
9
9
  <template #title>{{
@@ -45,15 +45,6 @@ export default {
45
45
  userUploads: {},
46
46
  }
47
47
  },
48
- computed: {
49
- color() {
50
- if (this.value.file_assets && this.value.file_assets.length > 0) {
51
- return 'success'
52
- } else {
53
- return ''
54
- }
55
- },
56
- },
57
48
  watch: {
58
49
  value(newVal) {
59
50
  this.userUploads = newVal
@@ -332,7 +332,7 @@ export default {
332
332
  captionsmenu: true, // Show the captions below the video
333
333
  playlistmenu: true, // Show the playlist menu if there's multiple videos
334
334
  playlistautoadvance: true, // Play the next source group
335
- playbackrates: [0.5, 1, 1.5, 2], // Default playback speeds
335
+ playbackrates: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], // Default playback speeds
336
336
  },
337
337
  playlist: [
338
338
  /*{
@@ -0,0 +1,240 @@
1
+ <template>
2
+ <div>
3
+ <v-card elevation="0">
4
+ <v-btn-toggle v-model="settingSelector" multiple borderless>
5
+ <v-tooltip top>
6
+ <template #activator="{ on, attrs }">
7
+ <v-btn
8
+ elevation="0"
9
+ v-bind="attrs"
10
+ text
11
+ :disabled="render"
12
+ v-on="on"
13
+ >
14
+ <v-icon>mdi-comment-text-multiple</v-icon>
15
+ {{
16
+ $t(
17
+ 'windward.core.components.settings.text_editor.glossary'
18
+ )
19
+ }}
20
+ </v-btn>
21
+ </template>
22
+ <span>
23
+ {{
24
+ $t(
25
+ 'windward.core.components.settings.text_editor.click_to_hide_glossary'
26
+ )
27
+ }}</span
28
+ >
29
+ </v-tooltip>
30
+
31
+ <slot name="actions-append"></slot>
32
+ </v-btn-toggle>
33
+ <br />
34
+ </v-card>
35
+ <div v-if="settingSelector.includes(0)" class="pt-4">
36
+ <br />
37
+ <v-card v-if="verifiedTerms.length > 0" elevation="0" outlined>
38
+ <v-card-title outlined class="text-capitalize">
39
+ {{
40
+ $t(
41
+ 'windward.core.components.settings.text_editor.verified_terms'
42
+ )
43
+ }}
44
+ </v-card-title>
45
+ <v-card-text>
46
+ <v-chip
47
+ v-for="(verified, index) in verifiedTerms"
48
+ :key="index"
49
+ class="ma-2"
50
+ :close="
51
+ $PermissionService.userHasAccessTo(
52
+ 'plugin.windward.core.organization.course.glossary',
53
+ 'writable'
54
+ )
55
+ "
56
+ color="success"
57
+ close-icon="mdi-pencil"
58
+ :disabled="render"
59
+ @click:close="editTerm(verified)"
60
+ >
61
+ <ContentViewer v-model="verified.term"></ContentViewer>
62
+ </v-chip>
63
+ </v-card-text>
64
+ </v-card>
65
+ <v-card v-if="unVerifiedTerms.length > 0" elevation="0" outlined>
66
+ <v-card-title outlined class="text-capitalize">
67
+ {{
68
+ $t(
69
+ 'windward.core.components.settings.text_editor.unverified_terms'
70
+ )
71
+ }}
72
+ </v-card-title>
73
+ <v-card-text>
74
+ <v-chip
75
+ v-for="(unVerified, index) in unVerifiedTerms"
76
+ :key="index"
77
+ class="ma-2"
78
+ close
79
+ color="error"
80
+ close-icon="mdi-content-save-plus-outline"
81
+ :disabled="render"
82
+ @click:close="addGlossaryTerm(unVerified)"
83
+ >
84
+ <ContentViewer
85
+ v-model="unVerified.term"
86
+ ></ContentViewer>
87
+ </v-chip>
88
+ </v-card-text>
89
+ </v-card>
90
+ <v-card
91
+ v-if="
92
+ verifiedTerms.length === 0 && unVerifiedTerms.length === 0
93
+ "
94
+ elevation="0"
95
+ outlined
96
+ ><v-card-text class="text-capitalize font-weight-bold">
97
+ {{
98
+ $t(
99
+ 'windward.core.components.settings.text_editor.no_glossary'
100
+ )
101
+ }}
102
+ </v-card-text></v-card
103
+ >
104
+ </div>
105
+ <DialogBox
106
+ v-model="dialog"
107
+ color="primary"
108
+ max-width="600px"
109
+ action-save
110
+ :trigger="false"
111
+ :disabled="
112
+ render ||
113
+ !$PermissionService.userHasAccessTo(
114
+ 'plugin.windward.core.organization.course.glossary',
115
+ 'writable'
116
+ )
117
+ "
118
+ @click:save="save"
119
+ @click:outside="close"
120
+ @click:close="close"
121
+ @keydown.esc="close"
122
+ >
123
+ <template #title> {{ formTitle }} </template>
124
+ <template #form="{ on, attrs }">
125
+ <CourseGlossaryForm
126
+ v-model="selectedTerm"
127
+ :glossary="glossaryTerms"
128
+ :edit-mode="glossaryEdit"
129
+ :disabled="
130
+ !$PermissionService.userHasAccessTo(
131
+ 'plugin.windward.core.organization.course.glossary',
132
+ 'writable'
133
+ )
134
+ "
135
+ v-bind="attrs"
136
+ v-on="on"
137
+ ></CourseGlossaryForm>
138
+ </template>
139
+ </DialogBox>
140
+ </div>
141
+ </template>
142
+ <script>
143
+ import _ from 'lodash'
144
+ import { mapGetters, mapMutations, mapActions } from 'vuex'
145
+ import DialogBox from '~/components/Core/DialogBox.vue'
146
+ import Course from '~/models/Course'
147
+ import CourseGlossaryTerm from '../../models/CourseGlossaryTerm'
148
+ import CourseGlossaryForm from '../utils/glossary/CourseGlossaryForm'
149
+ import ContentViewer from '../utils/ContentViewer.vue'
150
+ import GlossaryHelper from '../../helpers/GlossaryHelper'
151
+ export default {
152
+ name: 'GlossaryVerification',
153
+ components: {
154
+ ContentViewer,
155
+ DialogBox,
156
+ CourseGlossaryForm,
157
+ },
158
+ props: {
159
+ value: {
160
+ type: String,
161
+ required: false,
162
+ default: '',
163
+ },
164
+ render: {
165
+ type: Boolean,
166
+ required: false,
167
+ default: false,
168
+ },
169
+ },
170
+ data() {
171
+ return {
172
+ dialog: false,
173
+ selectedTerm: {},
174
+ glossaryEdit: false,
175
+ formTitle: '',
176
+ settingSelector: [0, 1],
177
+ }
178
+ },
179
+ computed: {
180
+ ...mapGetters({
181
+ course: 'course/get',
182
+ glossaryTerms: 'glossary/getTerms',
183
+ }),
184
+ ...mapMutations({
185
+ saveCourse: 'course/set',
186
+ }),
187
+ verifiedTerms() {
188
+ return GlossaryHelper.getContentVerifiedGlossaryTerms(
189
+ this.value,
190
+ this.glossaryTerms
191
+ )
192
+ },
193
+ unVerifiedTerms() {
194
+ return GlossaryHelper.getContentUnVerifiedGlossaryTerms(
195
+ this.value,
196
+ this.glossaryTerms
197
+ )
198
+ },
199
+ },
200
+ methods: {
201
+ ...mapActions({
202
+ reloadGlossary: 'glossary/load',
203
+ }),
204
+ async save() {
205
+ try {
206
+ // Save the term
207
+ await new CourseGlossaryTerm(_.cloneDeep(this.selectedTerm))
208
+ .for(new Course(this.course))
209
+ .save()
210
+
211
+ // And update our local list
212
+ this.reloadGlossary(this.course)
213
+
214
+ this.$toast.success(this.$t('shared.forms.saved'))
215
+ } catch (e) {
216
+ console.error('Could not save glossary term', e)
217
+ this.$toast.error(this.$t('shared.forms.errors.unknown'))
218
+ }
219
+ },
220
+ editTerm(term) {
221
+ this.selectedTerm = term
222
+
223
+ this.formTitle = this.$t('windward.core.pages.glossary.edit_term')
224
+ this.glossaryEdit = true
225
+ this.dialog = true
226
+ },
227
+ addGlossaryTerm(term) {
228
+ this.selectedTerm = term
229
+
230
+ this.formTitle = this.$t('windward.core.pages.glossary.add_term')
231
+ this.glossaryEdit = false
232
+ this.dialog = true
233
+ },
234
+ close() {
235
+ this.glossaryEdit = false
236
+ this.dialog = false
237
+ },
238
+ },
239
+ }
240
+ </script>
@@ -37,43 +37,7 @@
37
37
  }}
38
38
  </h3>
39
39
  </v-col>
40
- <v-col
41
- v-if="
42
- $PermissionService.userHasAccessTo(
43
- 'windward.global.course,windward.organization.course.contentBlock',
44
- 'writable'
45
- )
46
- "
47
- cols="12"
48
- >
49
- <h3 class="pb-4">
50
- {{
51
- $t(
52
- 'windward.core.components.navigation.ask_the_expert.config'
53
- )
54
- }}
55
- </h3>
56
- <v-text-field
57
- v-model="courseAndContentName"
58
- disabled
59
- outlined
60
- :label="
61
- $t(
62
- 'windward.core.components.navigation.ask_the_expert.course_info'
63
- )
64
- "
65
- :rules="$Validation.getRule('exists')"
66
- ></v-text-field>
67
- </v-col>
68
- <v-col
69
- v-if="
70
- !$PermissionService.userHasAccessTo(
71
- 'windward.global.course,windward.organization.course.contentBlock',
72
- 'writable'
73
- )
74
- "
75
- cols="12"
76
- >
40
+ <v-col cols="12">
77
41
  <v-text-field
78
42
  v-model="subject"
79
43
  outlined
@@ -3,7 +3,13 @@
3
3
  <template #activator="{ on, attrs }">
4
4
  <v-list-item
5
5
  :class="color"
6
- :to="'/course/' + course.id + '/glossary'"
6
+ :to="
7
+ '/course/' +
8
+ course.id +
9
+ '/section/' +
10
+ enrollment.course_section_id +
11
+ '/glossary'
12
+ "
7
13
  v-bind="attrs"
8
14
  v-on="on"
9
15
  >
@@ -28,7 +34,6 @@ import { mapGetters } from 'vuex'
28
34
 
29
35
  export default {
30
36
  name: 'GlossaryNav',
31
- layout: 'course',
32
37
  middleware: ['auth'],
33
38
  props: {
34
39
  color: { type: String, required: false, default: '' },
@@ -39,6 +44,7 @@ export default {
39
44
  computed: {
40
45
  ...mapGetters({
41
46
  course: 'course/get',
47
+ enrollment: 'enrollment/get',
42
48
  }),
43
49
  },
44
50
  }
@@ -1,5 +1,13 @@
1
1
  <template>
2
- <v-list-item :to="'/course/' + course.id + '/user-uploads'">
2
+ <v-list-item
3
+ :to="
4
+ '/course/' +
5
+ course.id +
6
+ '/section/' +
7
+ enrollment.course_section_id +
8
+ '/user-uploads'
9
+ "
10
+ >
3
11
  <v-list-item-action>
4
12
  <v-icon>mdi-cloud-upload</v-icon>
5
13
  </v-list-item-action>
@@ -18,7 +26,6 @@ import { mapGetters } from 'vuex'
18
26
 
19
27
  export default {
20
28
  components: {},
21
- layout: 'course',
22
29
  middleware: ['auth'],
23
30
  data() {
24
31
  return {}
@@ -26,6 +33,7 @@ export default {
26
33
  computed: {
27
34
  ...mapGetters({
28
35
  course: 'course/get',
36
+ enrollment: 'enrollment/get',
29
37
  }),
30
38
  },
31
39
  created() {},
@@ -55,41 +55,51 @@
55
55
  "
56
56
  :disabled="render"
57
57
  ></v-text-field>
58
- <v-btn
59
- text
60
- elevation="0"
61
- class="mb-3"
62
- :disabled="render"
63
- @click="
64
- onToggleExpand(
65
- block.metadata.config.items[index]
66
- )
67
- "
68
- >
69
- <v-icon
70
- v-if="
71
- !block.metadata.config.items[index].expand
72
- "
73
- color="primary"
74
- >mdi-arrow-expand-all</v-icon
75
- >
76
- <v-icon
77
- v-if="block.metadata.config.items[index].expand"
78
- color="primary"
79
- >
80
- mdi-arrow-collapse-all
81
- </v-icon>
82
- </v-btn>
83
58
  <TextEditor
84
- v-show="!block.metadata.config.items[index].expand"
85
59
  v-model="block.metadata.config.items[index].content"
60
+ :hide-text-editor="
61
+ block.metadata.config.items[index].expand
62
+ "
86
63
  :label="
87
64
  $t(
88
65
  'windward.core.components.settings.accordion.label'
89
66
  )
90
67
  "
91
68
  :disabled="render"
92
- ></TextEditor>
69
+ show-glossary
70
+ >
71
+ <template #glossary-actions-append>
72
+ <v-btn
73
+ text
74
+ elevation="0"
75
+ class="mb-3"
76
+ :disabled="render"
77
+ @click="
78
+ onToggleExpand(
79
+ block.metadata.config.items[index]
80
+ )
81
+ "
82
+ >
83
+ <v-icon
84
+ v-if="
85
+ !block.metadata.config.items[index]
86
+ .expand
87
+ "
88
+ color="primary"
89
+ >mdi-arrow-expand-all</v-icon
90
+ >
91
+ <v-icon
92
+ v-if="
93
+ block.metadata.config.items[index]
94
+ .expand
95
+ "
96
+ color="primary"
97
+ >
98
+ mdi-arrow-collapse-all
99
+ </v-icon>
100
+ </v-btn>
101
+ </template>
102
+ </TextEditor>
93
103
  <v-container class="pa-0 pt-3">
94
104
  <h4>
95
105
  {{
@@ -87,25 +87,43 @@
87
87
  @click:close="onRemoveElement"
88
88
  >
89
89
  <template #header="{ item }">
90
- <v-avatar v-if="item.iconImage && item.fileConfig">
91
- <v-img :src="getImagePublicUrl(item.fileConfig.asset)" />
92
- </v-avatar>
93
- <v-icon
90
+ <div
91
+ v-if="item.iconImage && item.fileConfig"
92
+ class="d-flex align-center"
93
+ >
94
+ <v-avatar>
95
+ <v-img
96
+ :src="
97
+ getImagePublicUrl(item.fileConfig.asset)
98
+ "
99
+ />
100
+ </v-avatar>
101
+ <span class="expansion-panel-title pl-3">{{
102
+ item.title
103
+ }}</span>
104
+ </div>
105
+ <div
94
106
  v-else-if="
95
107
  !item.iconImage &&
96
108
  item.icon &&
97
109
  isIcon(item.icon)
98
110
  "
99
- >{{ item.icon }}</v-icon
111
+ class="d-flex align-center"
100
112
  >
113
+ <v-icon>{{ item.icon }}</v-icon>
114
+ <span class="expansion-panel-title pl-2">{{
115
+ item.title
116
+ }}</span>
117
+ </div>
101
118
  <span
102
119
  v-else-if="
103
120
  !item.iconImage &&
104
121
  item.icon &&
105
122
  !isIcon(item.icon)
106
123
  "
107
- >{{ item.icon }}</span
108
- >
124
+ >{{ item.icon }}
125
+ </span>
126
+ <span v-else-if="item.title">{{ item.title }} </span>
109
127
  <span v-else class="font-italic">
110
128
  {{
111
129
  $t(
@@ -155,6 +173,7 @@
155
173
  .fileConfig
156
174
  "
157
175
  :assets.sync="block.assets"
176
+ :disabled="render"
158
177
  hide-background
159
178
  hide-decorative
160
179
  hide-modal
@@ -343,3 +362,11 @@ export default {
343
362
  },
344
363
  }
345
364
  </script>
365
+ <style scoped>
366
+ .expansion-panel-title {
367
+ white-space: nowrap;
368
+ overflow: hidden;
369
+ text-overflow: ellipsis;
370
+ display: block;
371
+ }
372
+ </style>
@@ -3,6 +3,7 @@
3
3
  <ImageAssetSettings
4
4
  v-model="block.metadata.config"
5
5
  :assets.sync="block.assets"
6
+ :disabled="render"
6
7
  >
7
8
  </ImageAssetSettings>
8
9
  </v-container>
@@ -10,26 +11,22 @@
10
11
 
11
12
  <script>
12
13
  import _ from 'lodash'
13
- import TextEditor from '~/components/Text/TextEditor'
14
- import TextViewer from '~/components/Text/TextViewer'
15
14
  import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
16
- import ContentBlockAsset from '~/components/Content/ContentBlockAsset.vue'
17
15
  import ImageAssetSettings from '~/components/Content/Settings/ImageAssetSettings.vue'
18
16
 
19
17
  export default {
20
18
  name: 'ImageSettings',
21
- extends: BaseContentSettings,
22
19
  components: {
23
20
  ImageAssetSettings,
24
- ContentBlockAsset,
25
- TextEditor,
26
- TextViewer,
27
21
  },
22
+ extends: BaseContentSettings,
28
23
  props: {
29
24
  settings: { type: Object, required: false, default: null },
30
25
  context: { type: String, required: false, default: 'block' },
31
26
  },
32
- computed: {},
27
+ data() {
28
+ return {}
29
+ },
33
30
  beforeMount() {
34
31
  if (_.isEmpty(this.block)) {
35
32
  this.block = {}
@@ -65,10 +62,6 @@ export default {
65
62
  this.block.metadata.config.ariaDescribedBy = ''
66
63
  }
67
64
  },
68
- data() {
69
- return {}
70
- },
71
- watch: {},
72
65
  mounted() {},
73
66
  methods: {},
74
67
  }