@windward/core 0.29.0 → 0.30.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.
- package/CHANGELOG.md +13 -0
- package/components/Content/Blocks/Tab.vue +12 -6
- package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +17 -6
- package/components/Content/Blocks/UserUpload/SubmittedDisplayUserFilesTable.vue +161 -0
- package/components/Content/Blocks/UserUpload.vue +150 -117
- package/components/Settings/TextEditorSettings.vue +244 -1
- package/components/Settings/UserUploadSettings.vue +1 -1
- package/components/utils/glossary/CourseGlossaryForm.vue +3 -3
- package/i18n/en-US/components/content/blocks/user_upload.ts +7 -1
- package/i18n/en-US/components/settings/text_editor.ts +14 -0
- package/i18n/en-US/components/settings/user_upload.ts +1 -1
- package/i18n/es-ES/components/content/blocks/user_upload.ts +7 -1
- package/i18n/es-ES/components/settings/text_editor.ts +15 -0
- package/i18n/es-ES/components/settings/user_upload.ts +1 -1
- package/i18n/sv-SE/components/content/blocks/user_upload.ts +7 -1
- package/i18n/sv-SE/components/settings/text_editor.ts +15 -0
- package/i18n/sv-SE/components/settings/user_upload.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Release [0.30.0] - 2026-03-26
|
|
4
|
+
|
|
5
|
+
* Merged in feature/LE-2286-file-submission-block-submit-but (pull request #498)
|
|
6
|
+
* Merged in feature/LE-2277/generate-case-studies (pull request #484)
|
|
7
|
+
* Merged in bugfix/LE-2332-tab-text-color-in-dark-mode-with (pull request #497)
|
|
8
|
+
* Merged in feature/LE-2286-file-submission-block-submit-but (pull request #492)
|
|
9
|
+
* Merged in feature/LE-2321-increase-glossary-term-character (pull request #494)
|
|
10
|
+
* Merged in bugfix/LE-2332-tab-text-color-in-dark-mode-with (pull request #493)
|
|
11
|
+
* Merge remote-tracking branch 'origin/release/0.30.0' into feature/LE-2277/generate-case-studies
|
|
12
|
+
* Merged release/0.29.0 into feature/LE-2286-file-submission-block-submit-but
|
|
13
|
+
* Merged release/0.29.0 into feature/LE-2286-file-submission-block-submit-but
|
|
14
|
+
|
|
15
|
+
|
|
3
16
|
## Release [0.29.0] - 2026-03-11
|
|
4
17
|
|
|
5
18
|
* Merged in feature/LE-2319-preselect-cc-transcript-language (pull request #491)
|
|
@@ -37,11 +37,13 @@
|
|
|
37
37
|
v-for="(tab, tabIndex) in block.metadata.config.items"
|
|
38
38
|
:key="tabIndex"
|
|
39
39
|
>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
<div class="tab-headers">
|
|
41
|
+
{{
|
|
42
|
+
tab.tabHeader === '' || tab.tabHeader === null
|
|
43
|
+
? 'Item ' + (tabIndex + 1)
|
|
44
|
+
: tab.tabHeader
|
|
45
|
+
}}
|
|
46
|
+
</div>
|
|
45
47
|
</v-tab>
|
|
46
48
|
<v-tab-item
|
|
47
49
|
v-for="(tabContent, tabContentIndex) in block.metadata
|
|
@@ -55,6 +57,7 @@
|
|
|
55
57
|
<TextViewer
|
|
56
58
|
v-if="!tabContent.expand"
|
|
57
59
|
v-model="tabContent.content"
|
|
60
|
+
class="text-viewer"
|
|
58
61
|
text-viewer
|
|
59
62
|
></TextViewer>
|
|
60
63
|
<TextEditor
|
|
@@ -183,6 +186,9 @@ export default {
|
|
|
183
186
|
|
|
184
187
|
<style scoped>
|
|
185
188
|
.text-viewer {
|
|
186
|
-
color: var(--v-primary-base);
|
|
189
|
+
color: var(--v-primary-base) !important;
|
|
190
|
+
}
|
|
191
|
+
.tab-headers {
|
|
192
|
+
color: white !important;
|
|
187
193
|
}
|
|
188
194
|
</style>
|
|
@@ -3,18 +3,16 @@
|
|
|
3
3
|
<v-card-title
|
|
4
4
|
>{{
|
|
5
5
|
$t(
|
|
6
|
-
'windward.core.components.content.blocks.user_upload.
|
|
6
|
+
'windward.core.components.content.blocks.user_upload.review_current_uploads'
|
|
7
7
|
)
|
|
8
8
|
}}
|
|
9
|
-
-
|
|
10
|
-
{{ $d(new Date(userFileAsset.created_at), 'long') }}
|
|
11
9
|
</v-card-title>
|
|
12
10
|
<v-card-text>
|
|
13
|
-
<v-simple-table>
|
|
11
|
+
<v-simple-table class="simple-table" elevation="1">
|
|
14
12
|
<template #default>
|
|
15
13
|
<thead>
|
|
16
14
|
<tr>
|
|
17
|
-
<th>
|
|
15
|
+
<th style="width: 60%">
|
|
18
16
|
{{ $t('shared.file.name') }}
|
|
19
17
|
</th>
|
|
20
18
|
<th>
|
|
@@ -73,14 +71,24 @@
|
|
|
73
71
|
</tbody>
|
|
74
72
|
</template>
|
|
75
73
|
</v-simple-table>
|
|
74
|
+
<v-col class="d-flex justify-center">
|
|
75
|
+
<v-btn
|
|
76
|
+
color="success"
|
|
77
|
+
elevation="0"
|
|
78
|
+
class="text-center"
|
|
79
|
+
@click="onSubmit"
|
|
80
|
+
>
|
|
81
|
+
{{ $t('shared.forms.submit') }}
|
|
82
|
+
</v-btn>
|
|
83
|
+
</v-col>
|
|
76
84
|
</v-card-text>
|
|
77
85
|
</v-card>
|
|
78
86
|
</template>
|
|
79
87
|
|
|
80
88
|
<script>
|
|
89
|
+
import UserFileAsset from '../../../../models/UserFileAsset'
|
|
81
90
|
import Download from '~/helpers/Download'
|
|
82
91
|
|
|
83
|
-
import UserFileAsset from '../../../../models/UserFileAsset'
|
|
84
92
|
import FileAsset from '~/models/FileAsset'
|
|
85
93
|
|
|
86
94
|
import ContentBlock from '~/models/ContentBlock'
|
|
@@ -114,6 +122,9 @@ export default {
|
|
|
114
122
|
this.userFileAsset = this.value
|
|
115
123
|
},
|
|
116
124
|
methods: {
|
|
125
|
+
onSubmit() {
|
|
126
|
+
this.$emit('submit')
|
|
127
|
+
},
|
|
117
128
|
onConfirmDelete(file) {
|
|
118
129
|
const self = this
|
|
119
130
|
this.$toast.info(this.$t('shared.forms.confirm_delete_text'), {
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="userFileAsset.length">
|
|
3
|
+
<h5 class="mb-2">
|
|
4
|
+
{{
|
|
5
|
+
$t(
|
|
6
|
+
'windward.core.components.content.blocks.user_upload.submission_history'
|
|
7
|
+
)
|
|
8
|
+
}}
|
|
9
|
+
</h5>
|
|
10
|
+
<v-card v-for="(fileSubmission, index) in userFileAsset" :key="index">
|
|
11
|
+
<v-card-title class="pb-0">
|
|
12
|
+
<p class="mb-0">
|
|
13
|
+
{{
|
|
14
|
+
$t(
|
|
15
|
+
'windward.core.components.content.blocks.user_upload.submission'
|
|
16
|
+
)
|
|
17
|
+
}}
|
|
18
|
+
-
|
|
19
|
+
{{ $d(new Date(fileSubmission.created_at), 'long') }}
|
|
20
|
+
</p>
|
|
21
|
+
</v-card-title>
|
|
22
|
+
<v-card-text>
|
|
23
|
+
<v-simple-table>
|
|
24
|
+
<thead>
|
|
25
|
+
<tr>
|
|
26
|
+
<th style="width: 60%">
|
|
27
|
+
{{
|
|
28
|
+
$t(
|
|
29
|
+
'windward.core.components.content.blocks.user_upload.filename'
|
|
30
|
+
)
|
|
31
|
+
}}
|
|
32
|
+
</th>
|
|
33
|
+
<th style="width: 20%">
|
|
34
|
+
{{
|
|
35
|
+
$t(
|
|
36
|
+
'windward.core.components.content.blocks.user_upload.file_size'
|
|
37
|
+
)
|
|
38
|
+
}}
|
|
39
|
+
</th>
|
|
40
|
+
<th style="width: 20%">
|
|
41
|
+
{{
|
|
42
|
+
$t(
|
|
43
|
+
'windward.core.components.content.blocks.user_upload.download'
|
|
44
|
+
)
|
|
45
|
+
}}
|
|
46
|
+
</th>
|
|
47
|
+
</tr>
|
|
48
|
+
</thead>
|
|
49
|
+
<tbody>
|
|
50
|
+
<tr
|
|
51
|
+
v-for="file in fileSubmission.file_assets"
|
|
52
|
+
:key="file.id"
|
|
53
|
+
>
|
|
54
|
+
<td>{{ file.name }}</td>
|
|
55
|
+
<td>
|
|
56
|
+
{{ file.asset.metadata.size | humanFilesize }}
|
|
57
|
+
</td>
|
|
58
|
+
<td>
|
|
59
|
+
<v-btn
|
|
60
|
+
link
|
|
61
|
+
elevation="0"
|
|
62
|
+
@click="
|
|
63
|
+
Download.url(
|
|
64
|
+
file.asset.public_url,
|
|
65
|
+
file.name
|
|
66
|
+
)
|
|
67
|
+
"
|
|
68
|
+
>
|
|
69
|
+
<v-icon>mdi-download</v-icon>
|
|
70
|
+
<span class="sr-only">
|
|
71
|
+
{{ $t('shared.file.download') }}
|
|
72
|
+
</span>
|
|
73
|
+
</v-btn>
|
|
74
|
+
</td>
|
|
75
|
+
</tr>
|
|
76
|
+
</tbody>
|
|
77
|
+
</v-simple-table>
|
|
78
|
+
</v-card-text>
|
|
79
|
+
</v-card>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
import UserFileAsset from '../../../../models/UserFileAsset'
|
|
85
|
+
import Download from '~/helpers/Download'
|
|
86
|
+
|
|
87
|
+
import FileAsset from '~/models/FileAsset'
|
|
88
|
+
|
|
89
|
+
import ContentBlock from '~/models/ContentBlock'
|
|
90
|
+
import Enrollment from '~/models/Enrollment'
|
|
91
|
+
|
|
92
|
+
export default {
|
|
93
|
+
name: 'DisplayUserFilesTable',
|
|
94
|
+
components: {},
|
|
95
|
+
props: {
|
|
96
|
+
value: {
|
|
97
|
+
type: Array,
|
|
98
|
+
required: false,
|
|
99
|
+
default: () => {
|
|
100
|
+
return []
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
data() {
|
|
105
|
+
return {
|
|
106
|
+
Download,
|
|
107
|
+
userFileAsset: {},
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
watch: {
|
|
111
|
+
value(newVal) {
|
|
112
|
+
this.userFileAsset = newVal
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
mounted() {
|
|
116
|
+
this.userFileAsset = this.value
|
|
117
|
+
},
|
|
118
|
+
methods: {
|
|
119
|
+
onConfirmDelete(file) {
|
|
120
|
+
const self = this
|
|
121
|
+
this.$toast.info(this.$t('shared.forms.confirm_delete_text'), {
|
|
122
|
+
duration: null,
|
|
123
|
+
action: [
|
|
124
|
+
{
|
|
125
|
+
text: this.$t('shared.forms.cancel'),
|
|
126
|
+
onClick: (e, toastObject) => {
|
|
127
|
+
toastObject.goAway(0)
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
text: this.$t('shared.forms.confirm'),
|
|
132
|
+
onClick: (e, toastObject) => {
|
|
133
|
+
toastObject.goAway(0)
|
|
134
|
+
self.deleteFile(file)
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
})
|
|
139
|
+
},
|
|
140
|
+
async deleteFile(file) {
|
|
141
|
+
await new FileAsset({ id: file.file_asset_id })
|
|
142
|
+
.for(
|
|
143
|
+
new Enrollment(this.enrollment),
|
|
144
|
+
new ContentBlock({
|
|
145
|
+
id: this.userFileAsset.content_block_id,
|
|
146
|
+
}),
|
|
147
|
+
new UserFileAsset({ id: this.userFileAsset.id })
|
|
148
|
+
)
|
|
149
|
+
.delete()
|
|
150
|
+
|
|
151
|
+
// Remove the deleted file from the array for display reasons
|
|
152
|
+
this.userFileAsset.file_assets =
|
|
153
|
+
this.userFileAsset.file_assets.filter(function (f) {
|
|
154
|
+
return f.file_asset_id !== file.file_asset_id
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
this.$emit('input', this.userFileAsset)
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
</script>
|
|
@@ -1,109 +1,104 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-container
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
<v-container
|
|
3
|
+
:class="['pa-0', blockDirectionClasses]"
|
|
4
|
+
:dir="blockTextDirection"
|
|
5
|
+
>
|
|
6
|
+
<h2
|
|
7
|
+
v-if="
|
|
8
|
+
block.metadata.config.title &&
|
|
9
|
+
block.metadata.config.display_title
|
|
10
|
+
"
|
|
11
|
+
tabindex="0"
|
|
12
|
+
>
|
|
13
|
+
{{ block.metadata.config.title }}
|
|
14
|
+
</h2>
|
|
15
|
+
<v-row>
|
|
16
|
+
<v-col v-if="block.metadata.config.instructions" cols="12">
|
|
17
|
+
<p tabindex="0" class="pt-3">
|
|
18
|
+
{{ block.metadata.config.instructions }}
|
|
19
|
+
</p>
|
|
20
|
+
</v-col>
|
|
21
|
+
<v-col v-if="!blockExists" cols="12">
|
|
22
|
+
<v-alert type="warning">
|
|
23
|
+
<p>
|
|
24
|
+
{{
|
|
25
|
+
$t(
|
|
26
|
+
'windward.core.components.content.blocks.user_upload.must_save'
|
|
27
|
+
)
|
|
28
|
+
}}
|
|
17
29
|
</p>
|
|
18
|
-
</v-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<
|
|
35
|
-
v-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<v-container class="text-center">
|
|
59
|
-
<v-btn
|
|
60
|
-
:disabled="!canUpload || loading"
|
|
61
|
-
color="primary"
|
|
62
|
-
elevation="0"
|
|
63
|
-
class="text-center"
|
|
64
|
-
@click="handleUpload"
|
|
65
|
-
>
|
|
66
|
-
{{ $t('shared.forms.upload') }}
|
|
67
|
-
</v-btn>
|
|
68
|
-
</v-container>
|
|
69
|
-
</v-form>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
</v-col>
|
|
74
|
-
</v-row>
|
|
75
|
-
</div>
|
|
30
|
+
</v-alert>
|
|
31
|
+
</v-col>
|
|
32
|
+
</v-row>
|
|
33
|
+
<v-row v-if="render">
|
|
34
|
+
<v-col v-if="blockExists" cols="12">
|
|
35
|
+
<div class="upload-container" :class="uploadContainerClass">
|
|
36
|
+
<v-col>
|
|
37
|
+
<FileDropZone
|
|
38
|
+
v-model="uploadFiles"
|
|
39
|
+
:accept="
|
|
40
|
+
block.metadata.config.uploadSettings.accept
|
|
41
|
+
"
|
|
42
|
+
:multiple="
|
|
43
|
+
block.metadata.config.uploadSettings.multiple
|
|
44
|
+
"
|
|
45
|
+
></FileDropZone>
|
|
46
|
+
<v-col class="d-flex justify-center">
|
|
47
|
+
<v-btn
|
|
48
|
+
:disabled="!canUpload || loading"
|
|
49
|
+
color="primary"
|
|
50
|
+
elevation="0"
|
|
51
|
+
class="text-center"
|
|
52
|
+
@click="handleUpload"
|
|
53
|
+
>
|
|
54
|
+
{{ $t('shared.forms.upload') }}
|
|
55
|
+
</v-btn>
|
|
56
|
+
</v-col>
|
|
57
|
+
</v-col>
|
|
58
|
+
<DisplayUserFilesTable
|
|
59
|
+
v-model="uploadedUserFileAsset"
|
|
60
|
+
:enrollment="enrollment"
|
|
61
|
+
@submit="handleSubmit"
|
|
62
|
+
></DisplayUserFilesTable>
|
|
63
|
+
<v-divider class="my-4"></v-divider>
|
|
64
|
+
<SubmittedDisplayUserFilesTable
|
|
65
|
+
v-model="submittedUserFileAssets"
|
|
66
|
+
></SubmittedDisplayUserFilesTable>
|
|
67
|
+
</div>
|
|
68
|
+
</v-col>
|
|
69
|
+
</v-row>
|
|
76
70
|
</v-container>
|
|
77
71
|
</template>
|
|
78
72
|
|
|
79
73
|
<script>
|
|
80
74
|
import _ from 'lodash'
|
|
81
75
|
import { mapGetters } from 'vuex'
|
|
76
|
+
import UserFileAsset from '../../../models/UserFileAsset'
|
|
77
|
+
import DisplayUserFilesTable from './UserUpload/DisplayUserFilesTable.vue'
|
|
78
|
+
import SubmittedDisplayUserFilesTable from './UserUpload/SubmittedDisplayUserFilesTable.vue'
|
|
82
79
|
import Uuid from '~/helpers/Uuid'
|
|
83
80
|
import Download from '~/helpers/Download'
|
|
84
81
|
import Enrollment from '~/models/Enrollment'
|
|
85
82
|
import ContentBlock from '~/models/ContentBlock'
|
|
86
83
|
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
87
84
|
import FileDropZone from '~/components/Core/FileDropZone.vue'
|
|
88
|
-
import UserFileAsset from '../../../models/UserFileAsset'
|
|
89
|
-
|
|
90
|
-
import DisplayUserFilesTable from './UserUpload/DisplayUserFilesTable.vue'
|
|
91
85
|
|
|
92
86
|
export default {
|
|
93
87
|
name: 'UserUpload',
|
|
94
88
|
components: {
|
|
95
89
|
FileDropZone,
|
|
96
90
|
DisplayUserFilesTable,
|
|
91
|
+
SubmittedDisplayUserFilesTable,
|
|
97
92
|
},
|
|
98
93
|
extends: BaseContentBlock,
|
|
99
94
|
data() {
|
|
100
95
|
return {
|
|
101
96
|
Download,
|
|
102
97
|
saveState: false, // Override the base block to disable state saving
|
|
103
|
-
valid: true,
|
|
104
98
|
loading: false,
|
|
105
99
|
uploadFiles: [],
|
|
106
|
-
|
|
100
|
+
uploadedUserFileAsset: {},
|
|
101
|
+
submittedUserFileAssets: [],
|
|
107
102
|
studentUpload: null,
|
|
108
103
|
maxFileLimit: 10,
|
|
109
104
|
}
|
|
@@ -121,44 +116,48 @@ export default {
|
|
|
121
116
|
return Uuid.test(this.block.id)
|
|
122
117
|
},
|
|
123
118
|
canUpload() {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
119
|
+
const currentFiles = _.get(
|
|
120
|
+
this.uploadedUserFileAsset,
|
|
121
|
+
'file_assets',
|
|
122
|
+
[]
|
|
123
|
+
)
|
|
130
124
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
125
|
+
// Multiple disabled, single file selected
|
|
126
|
+
if (
|
|
127
|
+
!Array.isArray(this.uploadFiles) &&
|
|
128
|
+
!this.block.metadata.config.uploadSettings.multiple &&
|
|
129
|
+
this.uploadFiles &&
|
|
130
|
+
currentFiles.length === 0
|
|
131
|
+
) {
|
|
132
|
+
return true
|
|
133
|
+
} else if (
|
|
134
|
+
// Multi enabled, one or more files selected
|
|
135
|
+
this.block.metadata.config.uploadSettings.multiple &&
|
|
136
|
+
Array.isArray(this.uploadFiles) &&
|
|
137
|
+
this.uploadFiles.length > 0 &&
|
|
138
|
+
this.uploadFiles.length + currentFiles.length <=
|
|
139
|
+
this.maxFileLimit
|
|
140
|
+
) {
|
|
141
|
+
return true
|
|
142
|
+
} else {
|
|
143
|
+
// No files
|
|
144
|
+
return false
|
|
152
145
|
}
|
|
153
|
-
|
|
146
|
+
},
|
|
147
|
+
canSubmit() {
|
|
148
|
+
return !this.canUpload && !_.isEmpty(this.uploadedUserFileAsset)
|
|
154
149
|
},
|
|
155
150
|
showUpload() {
|
|
156
|
-
const currentFiles = _.get(
|
|
157
|
-
|
|
151
|
+
const currentFiles = _.get(
|
|
152
|
+
this.uploadedUserFileAsset,
|
|
153
|
+
'file_assets',
|
|
154
|
+
[]
|
|
155
|
+
)
|
|
158
156
|
// Multiple disabled, confirm there's no current files
|
|
159
157
|
if (
|
|
160
158
|
!this.block.metadata.config.uploadSettings.multiple &&
|
|
161
|
-
currentFiles.length === 0
|
|
159
|
+
currentFiles.length === 0 &&
|
|
160
|
+
this.submittedUserFileAssets.length === 0
|
|
162
161
|
) {
|
|
163
162
|
return true
|
|
164
163
|
} else if (
|
|
@@ -223,7 +222,6 @@ export default {
|
|
|
223
222
|
) {
|
|
224
223
|
uploadFiles = [uploadFiles]
|
|
225
224
|
}
|
|
226
|
-
|
|
227
225
|
// Create a new UserFileAsset if we don't have an instance yet
|
|
228
226
|
let userFileAssetRequest = new UserFileAsset({
|
|
229
227
|
file: uploadFiles,
|
|
@@ -233,31 +231,66 @@ export default {
|
|
|
233
231
|
)
|
|
234
232
|
|
|
235
233
|
// Apply the existing id if we already have an instance
|
|
236
|
-
if (Uuid.test(this.
|
|
237
|
-
userFileAssetRequest.id = this.
|
|
234
|
+
if (Uuid.test(this.uploadedUserFileAsset.id)) {
|
|
235
|
+
userFileAssetRequest.id = this.uploadedUserFileAsset.id
|
|
238
236
|
}
|
|
239
237
|
|
|
240
238
|
// Add our files to upload
|
|
241
239
|
userFileAssetRequest.file = uploadFiles
|
|
242
240
|
|
|
243
241
|
try {
|
|
244
|
-
// Apply the response back to the reactive this.
|
|
245
|
-
this.
|
|
242
|
+
// Apply the response back to the reactive this.uploadedUserFileAsset
|
|
243
|
+
this.uploadedUserFileAsset = await userFileAssetRequest.save()
|
|
246
244
|
} catch (e) {
|
|
247
245
|
this.$toast.error(this.$t('shared.forms.errors.unknown'))
|
|
248
|
-
console.
|
|
246
|
+
console.error(e)
|
|
249
247
|
}
|
|
250
248
|
this.loading = false
|
|
251
249
|
this.uploadFiles = []
|
|
252
250
|
},
|
|
251
|
+
async handleSubmit() {
|
|
252
|
+
if (this.canSubmit) {
|
|
253
|
+
this.loading = true
|
|
254
|
+
|
|
255
|
+
// Create an update request for the existing UserFileAsset
|
|
256
|
+
const userFileAssetRequest = new UserFileAsset({
|
|
257
|
+
status: 'submitted',
|
|
258
|
+
}).for(
|
|
259
|
+
new Enrollment(this.enrollment),
|
|
260
|
+
new ContentBlock({ id: this.block.id })
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
// Apply the existing id to update the specific record
|
|
264
|
+
if (Uuid.test(this.uploadedUserFileAsset.id)) {
|
|
265
|
+
userFileAssetRequest.id = this.uploadedUserFileAsset.id
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
// Save the update
|
|
269
|
+
const response = await userFileAssetRequest.save()
|
|
270
|
+
this.submittedUserFileAssets.unshift(response)
|
|
271
|
+
this.uploadedUserFileAsset = {}
|
|
272
|
+
} catch (e) {
|
|
273
|
+
this.$toast.error(this.$t('shared.forms.errors.unknown'))
|
|
274
|
+
console.error(e)
|
|
275
|
+
}
|
|
276
|
+
this.loading = false
|
|
277
|
+
}
|
|
278
|
+
},
|
|
253
279
|
async loadUserUploads() {
|
|
254
280
|
if (this.enrollment && this.block.id) {
|
|
255
|
-
|
|
281
|
+
const result = await new UserFileAsset()
|
|
256
282
|
.for(
|
|
257
283
|
new Enrollment(this.enrollment),
|
|
258
284
|
new ContentBlock({ id: this.block.id })
|
|
259
285
|
)
|
|
260
|
-
.
|
|
286
|
+
.get()
|
|
287
|
+
result.forEach((file) => {
|
|
288
|
+
if (file.status === 'submitted') {
|
|
289
|
+
this.submittedUserFileAssets.push(file)
|
|
290
|
+
} else {
|
|
291
|
+
this.uploadedUserFileAsset = file
|
|
292
|
+
}
|
|
293
|
+
})
|
|
261
294
|
}
|
|
262
295
|
},
|
|
263
296
|
},
|
|
@@ -49,13 +49,63 @@
|
|
|
49
49
|
v-bind="attrs"
|
|
50
50
|
type=" table-row-divider, list-item, divider, list-item, divider, image,image"
|
|
51
51
|
></v-skeleton-loader>
|
|
52
|
+
|
|
53
|
+
<div v-if="isTextBlock" class="case-study-controls mt-2">
|
|
54
|
+
<div class="text-caption mb-2">
|
|
55
|
+
{{
|
|
56
|
+
$t(
|
|
57
|
+
'windward.core.components.settings.text_editor.case_study_help'
|
|
58
|
+
)
|
|
59
|
+
}}
|
|
60
|
+
</div>
|
|
61
|
+
<v-autocomplete
|
|
62
|
+
v-model="selectedCaseStudyPages"
|
|
63
|
+
class="mb-2 case-study-page-selector"
|
|
64
|
+
:items="flattenedContent"
|
|
65
|
+
outlined
|
|
66
|
+
hide-details
|
|
67
|
+
multiple
|
|
68
|
+
chips
|
|
69
|
+
small-chips
|
|
70
|
+
deletable-chips
|
|
71
|
+
:disabled="render || isGeneratingCaseStudy"
|
|
72
|
+
:label="
|
|
73
|
+
$t(
|
|
74
|
+
'windward.core.components.settings.text_editor.case_study_selected_pages'
|
|
75
|
+
)
|
|
76
|
+
"
|
|
77
|
+
item-text="content.name"
|
|
78
|
+
return-object
|
|
79
|
+
></v-autocomplete>
|
|
80
|
+
<v-btn
|
|
81
|
+
elevation="0"
|
|
82
|
+
color="secondary"
|
|
83
|
+
block
|
|
84
|
+
:loading="isGeneratingCaseStudy"
|
|
85
|
+
:disabled="render || isGeneratingCaseStudy"
|
|
86
|
+
@click="onGenerateCaseStudy"
|
|
87
|
+
>
|
|
88
|
+
<v-icon v-if="!isGeneratingCaseStudy" class="pr-1">
|
|
89
|
+
mdi-magic-staff
|
|
90
|
+
</v-icon>
|
|
91
|
+
<span v-if="!isGeneratingCaseStudy">{{
|
|
92
|
+
$t(
|
|
93
|
+
'windward.core.components.settings.text_editor.generate_case_study'
|
|
94
|
+
)
|
|
95
|
+
}}</span>
|
|
96
|
+
</v-btn>
|
|
97
|
+
</div>
|
|
52
98
|
</div>
|
|
53
99
|
</template>
|
|
54
100
|
|
|
55
101
|
<script>
|
|
102
|
+
import _ from 'lodash'
|
|
103
|
+
import { mapGetters } from 'vuex'
|
|
56
104
|
import Crypto from '~/helpers/Crypto'
|
|
57
105
|
import BaseContentSettings from '~/components/Content/Settings/BaseContentSettings.js'
|
|
58
106
|
import TextEditor from '~/components/Text/TextEditor'
|
|
107
|
+
import Course from '~/models/Course'
|
|
108
|
+
import Organization from '~/models/Organization'
|
|
59
109
|
export default {
|
|
60
110
|
name: 'TextEditorSettings',
|
|
61
111
|
components: {
|
|
@@ -76,10 +126,42 @@ export default {
|
|
|
76
126
|
},
|
|
77
127
|
hideTextEditor: false,
|
|
78
128
|
updateKey: Crypto.id(),
|
|
129
|
+
isGeneratingCaseStudy: false,
|
|
130
|
+
selectedCaseStudyPages: [],
|
|
79
131
|
}
|
|
80
132
|
},
|
|
133
|
+
computed: {
|
|
134
|
+
...mapGetters({
|
|
135
|
+
organization: 'organization/get',
|
|
136
|
+
course: 'course/get',
|
|
137
|
+
currentContent: 'content/get',
|
|
138
|
+
}),
|
|
139
|
+
isTextBlock() {
|
|
140
|
+
return _.get(this.block, 'tag', '') === 'content-blocks-text'
|
|
141
|
+
},
|
|
142
|
+
flattenedContent() {
|
|
143
|
+
const flatTree = this.$ContentService.getFlatTree()
|
|
144
|
+
|
|
145
|
+
const homepage = this.$ContentService.getHomepage()
|
|
146
|
+
if (!_.isEmpty(homepage)) {
|
|
147
|
+
flatTree.unshift(homepage)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return flatTree
|
|
151
|
+
},
|
|
152
|
+
},
|
|
81
153
|
mounted() {
|
|
82
154
|
this.setConfig({ expand: false })
|
|
155
|
+
|
|
156
|
+
// Default selected pages to the current page
|
|
157
|
+
if (
|
|
158
|
+
this.isTextBlock &&
|
|
159
|
+
Array.isArray(this.selectedCaseStudyPages) &&
|
|
160
|
+
this.selectedCaseStudyPages.length === 0 &&
|
|
161
|
+
!_.isEmpty(this.currentContent)
|
|
162
|
+
) {
|
|
163
|
+
this.selectedCaseStudyPages = [_.cloneDeep(this.currentContent)]
|
|
164
|
+
}
|
|
83
165
|
},
|
|
84
166
|
methods: {
|
|
85
167
|
onExpand() {
|
|
@@ -87,8 +169,169 @@ export default {
|
|
|
87
169
|
this.block.metadata.config.expand = this.hideTextEditor
|
|
88
170
|
this.updateKey = Crypto.id()
|
|
89
171
|
},
|
|
172
|
+
blockHasMeaningfulText() {
|
|
173
|
+
const html = _.get(this.block, 'body', '') || ''
|
|
174
|
+
if (!html) {
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
// Strip tags and check for meaningful length
|
|
178
|
+
const text = String(html).replace(/<[^>]*>/g, '').trim()
|
|
179
|
+
return text.length > 0
|
|
180
|
+
},
|
|
181
|
+
async requestCaseStudyGeneration(contentIds) {
|
|
182
|
+
const organizationId = _.get(this.organization, 'id', null)
|
|
183
|
+
const courseId = _.get(this.course, 'id', null)
|
|
184
|
+
|
|
185
|
+
if (!organizationId || !courseId) {
|
|
186
|
+
throw new Error('missing_context')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const request = new Course()
|
|
190
|
+
request.custom(
|
|
191
|
+
new Organization({ id: organizationId }),
|
|
192
|
+
new Course({ id: courseId }),
|
|
193
|
+
'llm-case-study'
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
const resourcePath = request._customResource
|
|
197
|
+
if (!resourcePath) {
|
|
198
|
+
throw new Error('missing_resource')
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const payload = {
|
|
202
|
+
content_ids: contentIds,
|
|
203
|
+
language: this.$i18n?.locale,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const requestConfig = request._reqConfig(
|
|
207
|
+
{
|
|
208
|
+
method: 'POST',
|
|
209
|
+
url: `${request.baseURL()}/${resourcePath}`,
|
|
210
|
+
data: payload,
|
|
211
|
+
},
|
|
212
|
+
{ forceMethod: true }
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
const response = await request.request(requestConfig)
|
|
216
|
+
return response?.data ?? response
|
|
217
|
+
},
|
|
218
|
+
onGenerateCaseStudy(force = false) {
|
|
219
|
+
if (this.isGeneratingCaseStudy || this.render) {
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const contentIds = (this.selectedCaseStudyPages || [])
|
|
224
|
+
.map((c) => _.get(c, 'id', null))
|
|
225
|
+
.filter((id) => typeof id === 'string' && id.length > 0)
|
|
226
|
+
|
|
227
|
+
if (contentIds.length === 0) {
|
|
228
|
+
this.$toast?.error(
|
|
229
|
+
this.$t(
|
|
230
|
+
'windward.core.components.settings.text_editor.case_study_select_pages_error'
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!force && this.blockHasMeaningfulText()) {
|
|
237
|
+
const confirmText = this.$t(
|
|
238
|
+
'windward.core.components.settings.text_editor.case_study_replace_confirm'
|
|
239
|
+
)
|
|
240
|
+
this.$dialog.show(confirmText, {
|
|
241
|
+
icon: 'mdi-alert-outline',
|
|
242
|
+
duration: null,
|
|
243
|
+
action: [
|
|
244
|
+
{
|
|
245
|
+
text: this.$t('shared.forms.cancel'),
|
|
246
|
+
onClick: (_e, toastObject) => {
|
|
247
|
+
toastObject.goAway(0)
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
text: this.$t('shared.forms.confirm'),
|
|
252
|
+
onClick: (_e, toastObject) => {
|
|
253
|
+
toastObject.goAway(0)
|
|
254
|
+
this.onGenerateCaseStudy(true)
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
})
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.isGeneratingCaseStudy = true
|
|
263
|
+
|
|
264
|
+
this.requestCaseStudyGeneration(contentIds)
|
|
265
|
+
.then((data) => {
|
|
266
|
+
if (!data || typeof data.html !== 'string') {
|
|
267
|
+
throw new Error('invalid_response')
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.block.body = data.html
|
|
271
|
+
this.updateKey = Crypto.id()
|
|
272
|
+
|
|
273
|
+
this.$toast?.success(
|
|
274
|
+
this.$t(
|
|
275
|
+
'windward.core.components.settings.text_editor.case_study_generated'
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
})
|
|
279
|
+
.catch((error) => {
|
|
280
|
+
// eslint-disable-next-line no-console
|
|
281
|
+
console.error('Case study generation failed', error)
|
|
282
|
+
|
|
283
|
+
const messageKey = _.get(
|
|
284
|
+
error,
|
|
285
|
+
'response.data.error.message',
|
|
286
|
+
''
|
|
287
|
+
)
|
|
288
|
+
const details = _.get(
|
|
289
|
+
error,
|
|
290
|
+
'response.data.error.details',
|
|
291
|
+
{}
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
const errorSubtype = _.get(details, 'error_subtype', '')
|
|
295
|
+
const errorType = String(messageKey).split('.').pop()
|
|
296
|
+
|
|
297
|
+
if (errorSubtype === 'CHARACTER_LIMIT_EXCEEDED') {
|
|
298
|
+
this.$toast?.error(
|
|
299
|
+
this.$t(
|
|
300
|
+
'windward.core.components.settings.text_editor.case_study_character_limit'
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
} else if (
|
|
304
|
+
_.get(details, 'error_type', '') ===
|
|
305
|
+
'INSUFFICIENT_CONTENT' ||
|
|
306
|
+
errorType === 'insufficient_content'
|
|
307
|
+
) {
|
|
308
|
+
this.$toast?.error(
|
|
309
|
+
this.$t(
|
|
310
|
+
'windward.core.components.settings.text_editor.case_study_insufficient_content'
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
} else {
|
|
314
|
+
this.$toast?.error(
|
|
315
|
+
this.$t(
|
|
316
|
+
'windward.core.components.settings.text_editor.case_study_error'
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
.finally(() => {
|
|
322
|
+
this.isGeneratingCaseStudy = false
|
|
323
|
+
})
|
|
324
|
+
},
|
|
90
325
|
},
|
|
91
326
|
}
|
|
92
327
|
</script>
|
|
93
328
|
|
|
94
|
-
<style scoped
|
|
329
|
+
<style scoped>
|
|
330
|
+
.case-study-controls {
|
|
331
|
+
max-width: 100%;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.case-study-page-selector >>> .v-select__selections {
|
|
335
|
+
padding-top: 8px;
|
|
336
|
+
}
|
|
337
|
+
</style>
|
|
@@ -180,7 +180,7 @@ export default {
|
|
|
180
180
|
!Uuid.test(this.block.id)
|
|
181
181
|
) {
|
|
182
182
|
this.block.metadata.config.instructions = this.$t(
|
|
183
|
-
'windward.core.components.
|
|
183
|
+
'windward.core.components.content.blocks.user_upload.instructions'
|
|
184
184
|
)
|
|
185
185
|
}
|
|
186
186
|
if (_.isEmpty(this.block.metadata.config.uploadSettings)) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<v-text-field
|
|
4
4
|
id="glossary-form-term"
|
|
5
5
|
v-model="selectedTerm.term"
|
|
6
|
-
:counter="
|
|
6
|
+
:counter="75"
|
|
7
7
|
:label="$t('windward.core.pages.glossary.term')"
|
|
8
8
|
required
|
|
9
9
|
:rules="validation.termRules"
|
|
@@ -80,8 +80,8 @@ export default {
|
|
|
80
80
|
termRules: [
|
|
81
81
|
(v) => !!v || this.$t('shared.forms.errors.required'),
|
|
82
82
|
(v) =>
|
|
83
|
-
(v && v.length <=
|
|
84
|
-
this.$t('shared.forms.errors.text_lt', [
|
|
83
|
+
(v && v.length <= 75) ||
|
|
84
|
+
this.$t('shared.forms.errors.text_lt', [75]),
|
|
85
85
|
(v) =>
|
|
86
86
|
(v && this.termIsUnique(v)) ||
|
|
87
87
|
this.$t('shared.forms.errors.field_unique'),
|
|
@@ -2,10 +2,16 @@ export default {
|
|
|
2
2
|
title: 'File Submission',
|
|
3
3
|
user_uploads: 'User Uploads',
|
|
4
4
|
dialog_view: 'View Uploads',
|
|
5
|
-
|
|
5
|
+
instructions:
|
|
6
|
+
'Upload your files below. Then, review your current uploads, and select Submit when you are ready.',
|
|
6
7
|
must_save: 'You must save this block before users can make uploads.',
|
|
7
8
|
uploaded: 'Uploaded',
|
|
8
9
|
name: 'Name',
|
|
9
10
|
size: 'Size',
|
|
10
11
|
download: 'Download',
|
|
12
|
+
review_current_uploads: 'Review Current Uploads',
|
|
13
|
+
submission_history: 'Submission History',
|
|
14
|
+
submission: 'Submission',
|
|
15
|
+
filename: 'Filename',
|
|
16
|
+
file_size: 'File Size',
|
|
11
17
|
}
|
|
@@ -7,4 +7,18 @@ export default {
|
|
|
7
7
|
click_to_expand: 'Click to expand editor',
|
|
8
8
|
click_to_hide_editor: 'Click to hide editor',
|
|
9
9
|
click_to_hide_glossary: 'Click to hide glossary',
|
|
10
|
+
case_study_help:
|
|
11
|
+
'Select one or more pages to generate a fictional case study scenario based on their content.',
|
|
12
|
+
case_study_selected_pages: 'Source pages',
|
|
13
|
+
generate_case_study: 'Generate Case Study',
|
|
14
|
+
case_study_select_pages_error: 'Select at least one page to generate a case study.',
|
|
15
|
+
case_study_replace_confirm:
|
|
16
|
+
'This will replace the current text in this block. Continue?',
|
|
17
|
+
case_study_generated: 'Case study generated.',
|
|
18
|
+
case_study_insufficient_content:
|
|
19
|
+
'Not enough content on the selected pages. Add more content or select additional pages.',
|
|
20
|
+
case_study_character_limit:
|
|
21
|
+
'The selected pages contain too much content. Select fewer pages and try again.',
|
|
22
|
+
case_study_error:
|
|
23
|
+
'Unable to generate case study. Try selecting fewer pages or edit manually.',
|
|
10
24
|
}
|
|
@@ -2,11 +2,17 @@ export default {
|
|
|
2
2
|
title: 'Envío de archivos',
|
|
3
3
|
user_uploads: 'Cargas de usuarios',
|
|
4
4
|
dialog_view: 'Ver cargas',
|
|
5
|
-
|
|
5
|
+
instructions:
|
|
6
|
+
'Sube tus archivos a continuación. Luego, revisa tus archivos actuales y selecciona "Enviar" cuando estés listo.',
|
|
6
7
|
must_save:
|
|
7
8
|
'Debes guardar este bloque antes de que los usuarios puedan realizar cargas.',
|
|
8
9
|
uploaded: 'Subido',
|
|
9
10
|
name: 'Nombre',
|
|
10
11
|
size: 'Tamaño',
|
|
11
12
|
download: 'Descargar',
|
|
13
|
+
review_current_uploads: 'Revisar las cargas actuales',
|
|
14
|
+
submission_history: 'Historial de envíos',
|
|
15
|
+
submission: 'Envío',
|
|
16
|
+
filename: 'Nombre del archivo',
|
|
17
|
+
file_size: 'Tamaño de archivo',
|
|
12
18
|
}
|
|
@@ -7,4 +7,19 @@ export default {
|
|
|
7
7
|
click_to_expand: 'Haga Click ampliar el editor',
|
|
8
8
|
click_to_hide_editor: 'Haga click para esconder el editor',
|
|
9
9
|
click_to_hide_glossary: 'Haga click para esconder el glosario',
|
|
10
|
+
case_study_help:
|
|
11
|
+
'Seleccione una o más páginas para generar un caso práctico ficticio basado en su contenido.',
|
|
12
|
+
case_study_selected_pages: 'Páginas de origen',
|
|
13
|
+
generate_case_study: 'Generar caso práctico',
|
|
14
|
+
case_study_select_pages_error:
|
|
15
|
+
'Seleccione al menos una página para generar un caso práctico.',
|
|
16
|
+
case_study_replace_confirm:
|
|
17
|
+
'Esto reemplazará el texto actual de este bloque. ¿Continuar?',
|
|
18
|
+
case_study_generated: 'Caso práctico generado.',
|
|
19
|
+
case_study_insufficient_content:
|
|
20
|
+
'No hay suficiente contenido en las páginas seleccionadas. Agregue más contenido o seleccione páginas adicionales.',
|
|
21
|
+
case_study_character_limit:
|
|
22
|
+
'Las páginas seleccionadas contienen demasiado contenido. Seleccione menos páginas e inténtelo de nuevo.',
|
|
23
|
+
case_study_error:
|
|
24
|
+
'No se pudo generar el caso práctico. Intente seleccionar menos páginas o edite manualmente.',
|
|
10
25
|
}
|
|
@@ -2,11 +2,17 @@ export default {
|
|
|
2
2
|
title: 'Filinlämning',
|
|
3
3
|
user_uploads: 'Användaruppladdningar',
|
|
4
4
|
dialog_view: 'Visa uppladdningar',
|
|
5
|
-
|
|
5
|
+
instructions:
|
|
6
|
+
'Ladda upp dina filer nedan. Granska sedan dina aktuella uppladdningar och välj Skicka när du är redo.',
|
|
6
7
|
must_save:
|
|
7
8
|
'Du måste spara detta block innan användare kan göra uppladdningar.',
|
|
8
9
|
uploaded: 'Uppladdat',
|
|
9
10
|
name: 'Namn',
|
|
10
11
|
size: 'Storlek',
|
|
11
12
|
download: 'Ladda ner',
|
|
13
|
+
review_current_uploads: 'Granska aktuella uppladdningar',
|
|
14
|
+
submission_history: 'Inlämningshistorik',
|
|
15
|
+
submission: 'Underkastelse',
|
|
16
|
+
filename: 'Filnamn',
|
|
17
|
+
file_size: 'Fil-storlek',
|
|
12
18
|
}
|
|
@@ -7,4 +7,19 @@ export default {
|
|
|
7
7
|
click_to_expand: 'klicka för att förbruka',
|
|
8
8
|
click_to_hide_editor: 'klicka för att dölja editorn',
|
|
9
9
|
click_to_hide_glossary: 'klicka för att dölja ordlistan',
|
|
10
|
+
case_study_help:
|
|
11
|
+
'Välj en eller flera sidor för att generera ett fiktivt fallstudiescenario baserat på deras innehåll.',
|
|
12
|
+
case_study_selected_pages: 'Källsidor',
|
|
13
|
+
generate_case_study: 'Generera fallstudie',
|
|
14
|
+
case_study_select_pages_error:
|
|
15
|
+
'Välj minst en sida för att generera en fallstudie.',
|
|
16
|
+
case_study_replace_confirm:
|
|
17
|
+
'Detta kommer att ersätta den nuvarande texten i detta block. Fortsätt?',
|
|
18
|
+
case_study_generated: 'Fallstudie genererad.',
|
|
19
|
+
case_study_insufficient_content:
|
|
20
|
+
'Inte tillräckligt med innehåll på de valda sidorna. Lägg till mer innehåll eller välj fler sidor.',
|
|
21
|
+
case_study_character_limit:
|
|
22
|
+
'De valda sidorna innehåller för mycket innehåll. Välj färre sidor och försök igen.',
|
|
23
|
+
case_study_error:
|
|
24
|
+
'Kunde inte generera fallstudie. Försök välja färre sidor eller redigera manuellt.',
|
|
10
25
|
}
|