@windward/core 0.0.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 (113) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintrc.js +11 -0
  3. package/.prettierrc +4 -0
  4. package/README.md +86 -0
  5. package/babel.config.js +1 -0
  6. package/components/Content/Blocks/Accordion.vue +133 -0
  7. package/components/Content/Blocks/ClickableIcons.vue +98 -0
  8. package/components/Content/Blocks/Feedback.vue +323 -0
  9. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.vue +95 -0
  10. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.vue +45 -0
  11. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.vue +55 -0
  12. package/components/Content/Blocks/Image.vue +116 -0
  13. package/components/Content/Blocks/Math.vue +87 -0
  14. package/components/Content/Blocks/Tab.vue +89 -0
  15. package/components/Content/Blocks/Table.vue +68 -0
  16. package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +157 -0
  17. package/components/Content/Blocks/UserUpload/ManageDataTableUserFiles.vue +68 -0
  18. package/components/Content/Blocks/UserUpload.vue +240 -0
  19. package/components/Content/Blocks/Video.vue +245 -0
  20. package/components/Navigation/Items/CourseGlossaryToolNav.vue +39 -0
  21. package/components/Navigation/Items/GlossaryNav.vue +32 -0
  22. package/components/Navigation/Items/UserUploadNav.vue +35 -0
  23. package/components/Settings/AccordionSettings.vue +153 -0
  24. package/components/Settings/ClickableIconsSettings.vue +189 -0
  25. package/components/Settings/FeedbackSettings.vue +92 -0
  26. package/components/Settings/ImageSettings.vue +124 -0
  27. package/components/Settings/MathSettings.vue +81 -0
  28. package/components/Settings/TabSettings.vue +139 -0
  29. package/components/Settings/TextEditorSettings.vue +249 -0
  30. package/components/Settings/UserUploadSettings.vue +151 -0
  31. package/components/Settings/VideoSettings.vue +699 -0
  32. package/components/utils/ContentViewer.vue +69 -0
  33. package/components/utils/MathExpressionEditor.vue +294 -0
  34. package/components/utils/MathLiveWrapper.vue +86 -0
  35. package/components/utils/TinyMCEWrapper.vue +103 -0
  36. package/components/utils/glossary/CourseGlossary.vue +284 -0
  37. package/components/utils/glossary/CourseGlossaryForm.vue +106 -0
  38. package/components/utils/glossary/GlossaryToolTip.vue +87 -0
  39. package/config/tinymce.config.js +136 -0
  40. package/helpers/GlossaryHelper.ts +137 -0
  41. package/helpers/GlossaryTerm.ts +31 -0
  42. package/helpers/MathHelper.ts +236 -0
  43. package/helpers/tinymce/plugin.ts +83 -0
  44. package/i18n/en-US/components/content/blocks/feedback.ts +28 -0
  45. package/i18n/en-US/components/content/blocks/image.ts +5 -0
  46. package/i18n/en-US/components/content/blocks/index.ts +15 -0
  47. package/i18n/en-US/components/content/blocks/tab.ts +4 -0
  48. package/i18n/en-US/components/content/blocks/table.ts +4 -0
  49. package/i18n/en-US/components/content/blocks/user_upload.ts +12 -0
  50. package/i18n/en-US/components/content/blocks/video.ts +53 -0
  51. package/i18n/en-US/components/content/index.ts +5 -0
  52. package/i18n/en-US/components/index.ts +12 -0
  53. package/i18n/en-US/components/navigation/image.ts +4 -0
  54. package/i18n/en-US/components/navigation/index.ts +7 -0
  55. package/i18n/en-US/components/navigation/user_upload.ts +3 -0
  56. package/i18n/en-US/components/settings/clickable_icon.ts +9 -0
  57. package/i18n/en-US/components/settings/image.ts +1 -0
  58. package/i18n/en-US/components/settings/index.ts +13 -0
  59. package/i18n/en-US/components/settings/text_editor.ts +7 -0
  60. package/i18n/en-US/components/settings/user_upload.ts +11 -0
  61. package/i18n/en-US/components/settings/video.ts +13 -0
  62. package/i18n/en-US/components/utils/index.ts +5 -0
  63. package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +7 -0
  64. package/i18n/en-US/index.ts +16 -0
  65. package/i18n/en-US/modules/index.ts +5 -0
  66. package/i18n/en-US/pages/glossary.ts +7 -0
  67. package/i18n/en-US/pages/index.ts +7 -0
  68. package/i18n/en-US/pages/user_upload.ts +3 -0
  69. package/i18n/en-US/shared/content_blocks.ts +20 -0
  70. package/i18n/en-US/shared/index.ts +11 -0
  71. package/i18n/en-US/shared/menu.ts +3 -0
  72. package/i18n/en-US/shared/permission.ts +15 -0
  73. package/i18n/en-US/shared/settings.ts +15 -0
  74. package/index.js +15 -0
  75. package/jest.config.js +15 -0
  76. package/models/SurveyResult.ts +8 -0
  77. package/models/SurveyTemplate.ts +8 -0
  78. package/models/UserFileAsset.ts +12 -0
  79. package/package.json +48 -0
  80. package/pages/glossary.vue +31 -0
  81. package/pages/userUpload.vue +204 -0
  82. package/plugin.js +299 -0
  83. package/test/Components/Content/Blocks/Accordion.spec.js +21 -0
  84. package/test/Components/Content/Blocks/ClickableIcons.spec.js +21 -0
  85. package/test/Components/Content/Blocks/Feedback.spec.js +31 -0
  86. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.spec.js +23 -0
  87. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.spec.js +23 -0
  88. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.spec.js +23 -0
  89. package/test/Components/Content/Blocks/Image.spec.js +34 -0
  90. package/test/Components/Content/Blocks/Math.spec.js +34 -0
  91. package/test/Components/Content/Blocks/Tab.spec.js +20 -0
  92. package/test/Components/Content/Blocks/Table.spec.js +21 -0
  93. package/test/Components/Content/Blocks/UserUpload.spec.js +25 -0
  94. package/test/Components/Content/Blocks/Video.spec.js +112 -0
  95. package/test/Components/Settings/AccordionSettings.spec.js +45 -0
  96. package/test/Components/Settings/ClickableIconsSettings.spec.js +20 -0
  97. package/test/Components/Settings/FeedbackSettings.spec.js +20 -0
  98. package/test/Components/Settings/ImageSettings.spec.js +20 -0
  99. package/test/Components/Settings/MathSettings.spec.js +20 -0
  100. package/test/Components/Settings/TabSettings.spec.js +45 -0
  101. package/test/Components/Settings/UserUploadSettings.spec.js +20 -0
  102. package/test/__mocks__/componentsMock.js +83 -0
  103. package/test/__mocks__/contentBlockMock.js +119 -0
  104. package/test/__mocks__/contentSettingsMock.js +54 -0
  105. package/test/__mocks__/fileMock.js +1 -0
  106. package/test/__mocks__/helpersMock.js +53 -0
  107. package/test/__mocks__/modelMock.js +82 -0
  108. package/test/__mocks__/styleMock.js +1 -0
  109. package/test/helpers/GlossaryHelper.spec.js +227 -0
  110. package/test/helpers/MathHelper.spec.js +128 -0
  111. package/test/mocks.js +140 -0
  112. package/tsconfig.json +15 -0
  113. package/utils/index.js +18 -0
@@ -0,0 +1,157 @@
1
+ <template>
2
+ <v-card v-if="userFileAsset.id && userFileAsset.file_assets.length">
3
+ <v-card-title
4
+ >{{
5
+ $t(
6
+ 'windward.core.components.content.blocks.user_upload.user_uploads'
7
+ )
8
+ }}
9
+ -
10
+ {{ $d(new Date(userFileAsset.created_at), 'long') }}
11
+ </v-card-title>
12
+ <v-card-text>
13
+ <v-simple-table>
14
+ <template #default>
15
+ <thead>
16
+ <tr>
17
+ <th>
18
+ {{ $t('shared.file.name') }}
19
+ </th>
20
+ <th>
21
+ {{ $t('shared.file.size') }}
22
+ </th>
23
+ <th>
24
+ {{ $t('shared.file.download') }}
25
+ </th>
26
+ <th>
27
+ {{ $t('shared.file.delete') }}
28
+ </th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <tr
33
+ v-for="file in userFileAsset.file_assets"
34
+ :key="file.id"
35
+ >
36
+ <td>
37
+ {{ file.name }}
38
+ </td>
39
+ <td>
40
+ {{ file.asset.metadata.size | humanFilesize }}
41
+ </td>
42
+ <td>
43
+ <v-btn
44
+ link
45
+ @click="
46
+ Download.url(
47
+ file.asset.public_url,
48
+ file.name
49
+ )
50
+ "
51
+ >
52
+ <v-icon>mdi-download</v-icon>
53
+ <span class="sr-only">
54
+ {{ $t('shared.file.download') }}
55
+ </span>
56
+ </v-btn>
57
+ </td>
58
+ <td>
59
+ <v-btn
60
+ link
61
+ color="error"
62
+ @click="onConfirmDelete(file)"
63
+ >
64
+ <v-icon>mdi-delete</v-icon>
65
+ <span class="sr-only">
66
+ {{ $t('shared.file.delete') }}
67
+ </span>
68
+ </v-btn>
69
+ </td>
70
+ </tr>
71
+ </tbody>
72
+ </template>
73
+ </v-simple-table>
74
+ </v-card-text>
75
+ </v-card>
76
+ </template>
77
+
78
+ <script>
79
+ import Download from '~/helpers/Download'
80
+
81
+ import UserFileAsset from '../../../../models/UserFileAsset'
82
+ import FileAsset from '~/models/FileAsset'
83
+
84
+ import ContentBlock from '~/models/ContentBlock'
85
+ import Enrollment from '~/models/Enrollment'
86
+
87
+ export default {
88
+ name: 'DisplayUserFilesTable',
89
+ components: {},
90
+ props: {
91
+ value: {
92
+ type: Object,
93
+ required: false,
94
+ default: () => {
95
+ return {}
96
+ },
97
+ },
98
+ enrollment: { type: Object, required: true },
99
+ },
100
+ data() {
101
+ return {
102
+ Download,
103
+ userFileAsset: {},
104
+ }
105
+ },
106
+ watch: {
107
+ value(newVal) {
108
+ this.userFileAsset = newVal
109
+ },
110
+ },
111
+ mounted() {
112
+ this.userFileAsset = this.value
113
+ },
114
+ methods: {
115
+ onConfirmDelete(file) {
116
+ const self = this
117
+ this.$toast.info(this.$t('shared.forms.confirm_delete_text'), {
118
+ duration: null,
119
+ action: [
120
+ {
121
+ text: this.$t('shared.forms.cancel'),
122
+ onClick: (e, toastObject) => {
123
+ toastObject.goAway(0)
124
+ },
125
+ },
126
+ {
127
+ text: this.$t('shared.forms.confirm'),
128
+ onClick: (e, toastObject) => {
129
+ toastObject.goAway(0)
130
+ self.deleteFile(file)
131
+ },
132
+ },
133
+ ],
134
+ })
135
+ },
136
+ async deleteFile(file) {
137
+ await new FileAsset({ id: file.file_asset_id })
138
+ .for(
139
+ new Enrollment(this.enrollment),
140
+ new ContentBlock({
141
+ id: this.userFileAsset.content_block_id,
142
+ }),
143
+ new UserFileAsset({ id: this.userFileAsset.id })
144
+ )
145
+ .delete()
146
+
147
+ // Remove the deleted file from the array for display reasons
148
+ this.userFileAsset.file_assets =
149
+ this.userFileAsset.file_assets.filter(function (f) {
150
+ return f.file_asset_id !== file.file_asset_id
151
+ })
152
+
153
+ this.$emit('input', this.userFileAsset)
154
+ },
155
+ },
156
+ }
157
+ </script>
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <div class="text-center">
3
+ <Dialog
4
+ v-bind="$attrs"
5
+ transition="dialog-bottom-transition"
6
+ :color="color"
7
+ v-if="value.file_assets && value.file_assets.length > 0"
8
+ >
9
+ <template #title>{{
10
+ $t(
11
+ 'windward.core.components.content.blocks.user_upload.dialog_view'
12
+ )
13
+ }}</template>
14
+ <template #trigger>
15
+ {{ $tc('shared.file.view_files', value.file_assets.length) }}
16
+ </template>
17
+ <template #form="{ on, attrs }">
18
+ <DisplayUserFilesTable
19
+ v-bind="attrs"
20
+ v-on="on"
21
+ v-model="userUploads"
22
+ :enrollment="enrollment"
23
+ ></DisplayUserFilesTable>
24
+ </template>
25
+ </Dialog>
26
+ <span v-else>---</span>
27
+ </div>
28
+ </template>
29
+
30
+ <script>
31
+ import _ from 'lodash'
32
+ import Dialog from '~/components/Dialog'
33
+
34
+ import DisplayUserFilesTable from './DisplayUserFilesTable.vue'
35
+
36
+ export default {
37
+ name: 'ManageDataTableUserFiles',
38
+ components: { Dialog, DisplayUserFilesTable },
39
+ props: {
40
+ value: { type: Object, required: true },
41
+ enrollment: { type: Object, required: true },
42
+ },
43
+ data() {
44
+ return {
45
+ userUploads: {},
46
+ }
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
+ watch: {
58
+ value(newVal) {
59
+ this.userUploads = newVal
60
+ },
61
+ },
62
+ created() {
63
+ this.userUploads = this.value
64
+ },
65
+ mounted() {},
66
+ methods: {},
67
+ }
68
+ </script>
@@ -0,0 +1,240 @@
1
+ <template>
2
+ <v-container>
3
+ <div v-if="render || !block.metadata.config.expand">
4
+ <v-row>
5
+ <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
+ <TextViewer
19
+ v-model="block.metadata.config.instructions"
20
+ ></TextViewer>
21
+ </v-col>
22
+ <v-col cols="12">
23
+ <v-alert v-if="!blockExists" color="warning">
24
+ <p>
25
+ {{
26
+ $t(
27
+ 'windward.core.components.content.blocks.user_upload.must_save'
28
+ )
29
+ }}
30
+ </p>
31
+ </v-alert>
32
+ </v-col>
33
+ </v-row>
34
+ <v-row>
35
+ <v-col cols="12">
36
+ <DisplayUserFilesTable
37
+ v-model="userFileAsset"
38
+ :enrollment="enrollment"
39
+ ></DisplayUserFilesTable>
40
+ </v-col>
41
+ <v-col cols="12">
42
+ <div v-if="blockExists">
43
+ <v-form
44
+ ref="form"
45
+ v-model="valid"
46
+ lazy-validation
47
+ class="pb-0"
48
+ >
49
+ <FileDropZone
50
+ v-model="uploadFiles"
51
+ :accept="
52
+ block.metadata.config.uploadSettings.accept
53
+ "
54
+ :multiple="
55
+ block.metadata.config.uploadSettings
56
+ .multiple
57
+ "
58
+ ></FileDropZone>
59
+
60
+ <v-container class="primary lighten-1 text-center">
61
+ <v-btn
62
+ :disabled="!canUpload || loading"
63
+ color="success"
64
+ class="text-center"
65
+ @click="handleUpload"
66
+ >
67
+ {{ $t('shared.forms.upload') }}
68
+ </v-btn>
69
+ </v-container>
70
+ </v-form>
71
+ </div>
72
+ </v-col>
73
+ </v-row>
74
+ </div>
75
+ <div v-if="!render && block.metadata.config.expand">
76
+ <v-row>
77
+ <v-col cols="12">
78
+ <h3 class="pb-4">
79
+ {{
80
+ $t(
81
+ 'windward.core.components.content.blocks.user_upload.instructions_title'
82
+ )
83
+ }}
84
+ </h3>
85
+ <TextEditor v-model="block.metadata.config.instructions" />
86
+ </v-col>
87
+ </v-row>
88
+ </div>
89
+ </v-container>
90
+ </template>
91
+
92
+ <script>
93
+ import _ from 'lodash'
94
+ import { mapGetters } from 'vuex'
95
+ import TextViewer from '~/components/Text/TextViewer.vue'
96
+ import TextEditor from '~/components/Text/TextEditor.vue'
97
+ import Uuid from '~/helpers/Uuid'
98
+ import Download from '~/helpers/Download'
99
+
100
+ import UserFileAsset from '../../../models/UserFileAsset'
101
+ import Enrollment from '~/models/Enrollment'
102
+ import ContentBlock from '~/models/ContentBlock'
103
+ import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
104
+ import FileDropZone from '~/components/FileDropZone.vue'
105
+
106
+ import DisplayUserFilesTable from './UserUpload/DisplayUserFilesTable.vue'
107
+
108
+ export default {
109
+ name: 'UserUpload',
110
+ components: {
111
+ TextEditor,
112
+ TextViewer,
113
+ FileDropZone,
114
+ DisplayUserFilesTable,
115
+ },
116
+ extends: BaseContentBlock,
117
+ beforeMount() {
118
+ if (_.isEmpty(this.block)) {
119
+ this.block = {}
120
+ }
121
+ if (_.isEmpty(this.block.body)) {
122
+ this.block.body = 'user uplaod'
123
+ }
124
+ if (_.isEmpty(this.block.metadata)) {
125
+ this.block.metadata = {}
126
+ }
127
+ if (_.isEmpty(this.block.metadata.config)) {
128
+ this.block.metadata.config = {}
129
+ }
130
+ if (_.isEmpty(this.block.metadata.config.expand)) {
131
+ this.block.metadata.config.expand = false
132
+ }
133
+ if (_.isEmpty(this.block.metadata.config.instructions)) {
134
+ this.block.metadata.config.instructions = ''
135
+ }
136
+ if (_.isEmpty(this.block.metadata.config.uploadSettings)) {
137
+ this.block.metadata.config.uploadSettings = {
138
+ multiple: false,
139
+ accept: '',
140
+ }
141
+ }
142
+ },
143
+ data() {
144
+ return {
145
+ Download,
146
+ saveState: false, // Override the base block to disable state saving
147
+ valid: true,
148
+ loading: false,
149
+ uploadFiles: [],
150
+ userFileAsset: {},
151
+ studentUpload: null,
152
+ }
153
+ },
154
+ computed: {
155
+ ...mapGetters({
156
+ enrollment: 'enrollment/get',
157
+ }),
158
+ blockExists() {
159
+ return Uuid.test(this.block.id)
160
+ },
161
+ canUpload() {
162
+ if (this.valid) {
163
+ // Multiple disabled, single file selected
164
+ if (
165
+ !Array.isArray(this.uploadFiles) &&
166
+ !this.block.metadata.config.uploadSettings.multiple &&
167
+ this.uploadFiles
168
+ ) {
169
+ return true
170
+ } else if (
171
+ // Multi enabled, one or more files selected
172
+ Array.isArray(this.uploadFiles) &&
173
+ this.uploadFiles.length > 0
174
+ ) {
175
+ return true
176
+ } else {
177
+ // No files
178
+ return false
179
+ }
180
+ }
181
+ return false
182
+ },
183
+ },
184
+ mounted() {
185
+ if (this.blockExists) {
186
+ this.loadUserUploads()
187
+ }
188
+ },
189
+ methods: {
190
+ async handleUpload() {
191
+ this.loading = true
192
+
193
+ let uploadFiles = this.uploadFiles
194
+
195
+ // If multiple is disabled pack the file in a single array
196
+ // The API wants an array of files as an input
197
+ if (
198
+ !Array.isArray(this.uploadFiles) &&
199
+ !this.block.metadata.config.uploadSettings.multiple
200
+ ) {
201
+ uploadFiles = [uploadFiles]
202
+ }
203
+
204
+ // Create a new UserFileAsset if we don't have an instance yet
205
+ if (!Uuid.test(this.userFileAsset.id)) {
206
+ this.userFileAsset = new UserFileAsset({
207
+ file: uploadFiles,
208
+ }).for(
209
+ new Enrollment(this.enrollment),
210
+ new ContentBlock({ id: this.block.id })
211
+ )
212
+ }
213
+
214
+ this.userFileAsset.file = uploadFiles
215
+
216
+ try {
217
+ this.userFileAsset = await this.userFileAsset.save()
218
+ } catch (e) {
219
+ this.$toast.error(this.$t('shared.forms.errors.unknown'))
220
+ console.log(e)
221
+ }
222
+ this.loading = false
223
+ this.uploadFiles = []
224
+ },
225
+ async loadUserUploads() {
226
+ if (this.enrollment && this.block.id) {
227
+ this.userFileAsset = await new UserFileAsset()
228
+ .for(
229
+ new Enrollment(this.enrollment),
230
+ new ContentBlock({ id: this.block.id })
231
+ )
232
+ .first()
233
+ }
234
+ },
235
+ async onBeforeSave() {
236
+ return (this.block.metadata.config.expand = false)
237
+ },
238
+ },
239
+ }
240
+ </script>