@windward/integrations 0.16.0 → 0.18.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 +21 -0
- package/components/Integration/AiAgentIntegration/ChatWindow.vue +916 -0
- package/components/Integration/Driver/LoginSamlButton.vue +119 -0
- package/components/Integration/Driver/ManageSaml.vue +327 -0
- package/components/LLM/BloomTaxonomySelector.vue +120 -0
- package/components/LLM/ContentSelector.vue +66 -0
- package/components/LLM/GenerateContent/AssessmentQuestionGenerateButton.vue +285 -0
- package/components/LLM/GenerateContent/BlockQuestionGenerateButton.vue +514 -0
- package/components/LLM/GenerateContent/FakeTextStream.vue +67 -0
- package/components/SecretField.vue +62 -5
- package/config/integration.config.js +2 -0
- package/helpers/Driver/SamlSso.ts +12 -0
- package/i18n/en-US/components/ai_agent/chat.ts +20 -0
- package/i18n/en-US/components/ai_agent/index.ts +5 -0
- package/i18n/en-US/components/index.ts +4 -0
- package/i18n/en-US/components/integration/driver.ts +23 -0
- package/i18n/en-US/components/llm/blooms.ts +15 -0
- package/i18n/en-US/components/llm/content_selector.ts +3 -0
- package/i18n/en-US/components/llm/generate_content/fake_text_stream.ts +62 -0
- package/i18n/en-US/components/llm/generate_content/generate_questions.ts +81 -0
- package/i18n/en-US/components/llm/generate_content/index.ts +7 -0
- package/i18n/en-US/components/llm/index.ts +10 -0
- package/i18n/en-US/pages/login/index.ts +2 -0
- package/i18n/en-US/pages/login/saml.ts +7 -0
- package/i18n/en-US/shared/permission.ts +10 -0
- package/i18n/en-US/shared/settings.ts +2 -1
- package/i18n/es-ES/components/ai_agent/chat.ts +20 -0
- package/i18n/es-ES/components/ai_agent/index.ts +5 -0
- package/i18n/es-ES/components/index.ts +4 -0
- package/i18n/es-ES/components/integration/driver.ts +23 -0
- package/i18n/es-ES/components/llm/blooms.ts +15 -0
- package/i18n/es-ES/components/llm/content_selector.ts +3 -0
- package/i18n/es-ES/components/llm/generate_content/fake_text_stream.ts +62 -0
- package/i18n/es-ES/components/llm/generate_content/generate_questions.ts +85 -0
- package/i18n/es-ES/components/llm/generate_content/index.ts +7 -0
- package/i18n/es-ES/components/llm/index.ts +10 -0
- package/i18n/es-ES/pages/login/index.ts +2 -0
- package/i18n/es-ES/pages/login/saml.ts +7 -0
- package/i18n/es-ES/shared/permission.ts +10 -0
- package/i18n/es-ES/shared/settings.ts +2 -1
- package/i18n/sv-SE/components/ai_agent/chat.ts +19 -0
- package/i18n/sv-SE/components/ai_agent/index.ts +5 -0
- package/i18n/sv-SE/components/index.ts +4 -0
- package/i18n/sv-SE/components/integration/driver.ts +23 -0
- package/i18n/sv-SE/components/llm/blooms.ts +15 -0
- package/i18n/sv-SE/components/llm/content_selector.ts +3 -0
- package/i18n/sv-SE/components/llm/generate_content/fake_text_stream.ts +62 -0
- package/i18n/sv-SE/components/llm/generate_content/generate_questions.ts +82 -0
- package/i18n/sv-SE/components/llm/generate_content/index.ts +7 -0
- package/i18n/sv-SE/components/llm/index.ts +10 -0
- package/i18n/sv-SE/pages/login/index.ts +2 -0
- package/i18n/sv-SE/pages/login/saml.ts +7 -0
- package/i18n/sv-SE/shared/permission.ts +10 -0
- package/i18n/sv-SE/shared/settings.ts +1 -0
- package/jest.config.js +3 -0
- package/models/Activity.ts +8 -0
- package/models/AgentChat.ts +12 -0
- package/models/AgentChatMessage.ts +12 -0
- package/models/Auth/Saml.ts +21 -0
- package/package.json +2 -1
- package/plugin.js +51 -1
- package/test/__mocks__/componentsMock.js +81 -1
- package/test/setup.js +1 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="samlEnabled">
|
|
3
|
+
<v-btn
|
|
4
|
+
color="primary"
|
|
5
|
+
elevation="0"
|
|
6
|
+
large
|
|
7
|
+
block
|
|
8
|
+
@click="loginWithSaml"
|
|
9
|
+
:loading="ssoLoading || loading"
|
|
10
|
+
:disabled="loading"
|
|
11
|
+
>
|
|
12
|
+
<v-icon class="pr-2">{{ samlButtonIcon }}</v-icon>
|
|
13
|
+
{{ samlButtonLabel }}
|
|
14
|
+
</v-btn>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script>
|
|
19
|
+
import Saml from '../../../models/Auth/Saml'
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
name: 'LoginSamlButton',
|
|
23
|
+
props: {
|
|
24
|
+
loading: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
default: false,
|
|
27
|
+
},
|
|
28
|
+
valid: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
default: true,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
data() {
|
|
34
|
+
return {
|
|
35
|
+
ssoLoading: false,
|
|
36
|
+
samlEnabled: false,
|
|
37
|
+
samlButtonLabel: 'Sign in with SSO',
|
|
38
|
+
samlButtonIcon: 'mdi-login',
|
|
39
|
+
organizationId: null,
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
mounted() {
|
|
43
|
+
this.checkSamlStatus()
|
|
44
|
+
},
|
|
45
|
+
methods: {
|
|
46
|
+
async checkSamlStatus() {
|
|
47
|
+
try {
|
|
48
|
+
// Get organization from store or context
|
|
49
|
+
const organization = this.$store.getters['organization/get']
|
|
50
|
+
|
|
51
|
+
// If no organization in store, try to use the hostname
|
|
52
|
+
let orgIdentifier = null
|
|
53
|
+
|
|
54
|
+
if (organization && organization.id) {
|
|
55
|
+
// Prefer using the organization ID if available
|
|
56
|
+
orgIdentifier = organization.id
|
|
57
|
+
} else {
|
|
58
|
+
// Fallback to using the hostname (full base_url)
|
|
59
|
+
orgIdentifier = window.location.hostname
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!orgIdentifier) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check if SAML is enabled for this organization
|
|
67
|
+
const response = await Saml.custom('saml/status').get()
|
|
68
|
+
|
|
69
|
+
// The API returns an array, so we need to get the first element
|
|
70
|
+
const samlData = Array.isArray(response)
|
|
71
|
+
? response[0]
|
|
72
|
+
: response
|
|
73
|
+
|
|
74
|
+
if (samlData && samlData.enabled && samlData.configured) {
|
|
75
|
+
this.samlEnabled = true
|
|
76
|
+
this.organizationId = samlData.organization_id
|
|
77
|
+
|
|
78
|
+
// Use button settings from the integration configuration
|
|
79
|
+
this.samlButtonLabel =
|
|
80
|
+
samlData.button_label || 'Sign in with SSO'
|
|
81
|
+
this.samlButtonIcon = samlData.button_icon || 'mdi-login'
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// SAML not configured or error checking status
|
|
85
|
+
// Silently fail - SSO button won't show
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
async loginWithSaml() {
|
|
89
|
+
this.ssoLoading = true
|
|
90
|
+
try {
|
|
91
|
+
// Get the SSO URL from the backend
|
|
92
|
+
const response = await Saml.custom('users/saml-login').get()
|
|
93
|
+
|
|
94
|
+
// The API returns an array, so we need to get the first element
|
|
95
|
+
const loginData = Array.isArray(response)
|
|
96
|
+
? response[0]
|
|
97
|
+
: response
|
|
98
|
+
|
|
99
|
+
if (loginData && loginData.sso_url) {
|
|
100
|
+
// Redirect to the IdP for authentication
|
|
101
|
+
window.location.href = loginData.sso_url
|
|
102
|
+
} else {
|
|
103
|
+
this.$dialog.error('Unable to initiate SSO login')
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// Only log error in development mode
|
|
107
|
+
if (process.env.NODE_ENV === 'development') {
|
|
108
|
+
console.error('SSO login failed:', error)
|
|
109
|
+
}
|
|
110
|
+
this.$dialog.error(
|
|
111
|
+
'SSO login failed. Please try again or use standard login.'
|
|
112
|
+
)
|
|
113
|
+
} finally {
|
|
114
|
+
this.ssoLoading = false
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
</script>
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="!render" class="integration-loading">
|
|
4
|
+
<v-progress-circular size="128" indeterminate />
|
|
5
|
+
</div>
|
|
6
|
+
<div v-if="render">
|
|
7
|
+
<v-row justify="center" align="center" class="mt-5">
|
|
8
|
+
<v-col cols="12">
|
|
9
|
+
<v-switch
|
|
10
|
+
v-model="integration.enabled"
|
|
11
|
+
:label="
|
|
12
|
+
$t(
|
|
13
|
+
'windward.integrations.components.integration.driver.enabled'
|
|
14
|
+
)
|
|
15
|
+
"
|
|
16
|
+
/>
|
|
17
|
+
|
|
18
|
+
<v-text-field
|
|
19
|
+
id="saml-entity-id"
|
|
20
|
+
v-model="integration.metadata.config.idp_entity_id"
|
|
21
|
+
:label="
|
|
22
|
+
$t(
|
|
23
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_entity_id'
|
|
24
|
+
)
|
|
25
|
+
"
|
|
26
|
+
:hint="
|
|
27
|
+
$t(
|
|
28
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_entity_id_hint'
|
|
29
|
+
)
|
|
30
|
+
"
|
|
31
|
+
:rules="$Validation.getRule('exists')"
|
|
32
|
+
></v-text-field>
|
|
33
|
+
|
|
34
|
+
<v-text-field
|
|
35
|
+
id="saml-sso-url"
|
|
36
|
+
v-model="integration.metadata.config.idp_sso_url"
|
|
37
|
+
:label="
|
|
38
|
+
$t(
|
|
39
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_sso_url'
|
|
40
|
+
)
|
|
41
|
+
"
|
|
42
|
+
:hint="
|
|
43
|
+
$t(
|
|
44
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_sso_url_hint'
|
|
45
|
+
)
|
|
46
|
+
"
|
|
47
|
+
:rules="$Validation.getRule('url')"
|
|
48
|
+
></v-text-field>
|
|
49
|
+
|
|
50
|
+
<v-text-field
|
|
51
|
+
id="saml-slo-url"
|
|
52
|
+
v-model="integration.metadata.config.idp_slo_url"
|
|
53
|
+
autocomplete="off"
|
|
54
|
+
:label="
|
|
55
|
+
$t(
|
|
56
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_slo_url'
|
|
57
|
+
)
|
|
58
|
+
"
|
|
59
|
+
:hint="
|
|
60
|
+
$t(
|
|
61
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_slo_url_hint'
|
|
62
|
+
)
|
|
63
|
+
"
|
|
64
|
+
:rules="$Validation.getRule('url')"
|
|
65
|
+
></v-text-field>
|
|
66
|
+
|
|
67
|
+
<SecretField
|
|
68
|
+
id="saml-certificate"
|
|
69
|
+
v-model="integration.metadata.config.idp_x509_cert"
|
|
70
|
+
tag="v-textarea"
|
|
71
|
+
autocomplete="off"
|
|
72
|
+
:label="
|
|
73
|
+
$t(
|
|
74
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_x509_cert'
|
|
75
|
+
)
|
|
76
|
+
"
|
|
77
|
+
:hint="
|
|
78
|
+
$t(
|
|
79
|
+
'windward.integrations.components.integration.driver.saml_sso.idp_x509_cert_hint'
|
|
80
|
+
)
|
|
81
|
+
"
|
|
82
|
+
:append-icon="
|
|
83
|
+
showCertificate ? 'mdi-eye' : 'mdi-eye-off'
|
|
84
|
+
"
|
|
85
|
+
rows="4"
|
|
86
|
+
:rules="$Validation.getRule('exists')"
|
|
87
|
+
:readonly="!showCertificate"
|
|
88
|
+
:hidden="!showCertificate"
|
|
89
|
+
:type="showCertificate ? 'text' : 'password'"
|
|
90
|
+
@click:append="showCertificate = !showCertificate"
|
|
91
|
+
></SecretField>
|
|
92
|
+
|
|
93
|
+
<v-alert
|
|
94
|
+
v-if="integration.metadata.config.idp_x509_cert"
|
|
95
|
+
type="success"
|
|
96
|
+
text
|
|
97
|
+
dense
|
|
98
|
+
class="mt-2"
|
|
99
|
+
>
|
|
100
|
+
<v-icon small left>mdi-lock</v-icon>
|
|
101
|
+
{{
|
|
102
|
+
$t(
|
|
103
|
+
'windward.integrations.components.integration.driver.saml_sso.cert_stored'
|
|
104
|
+
)
|
|
105
|
+
}}
|
|
106
|
+
</v-alert>
|
|
107
|
+
|
|
108
|
+
<v-divider class="my-4" />
|
|
109
|
+
|
|
110
|
+
<h3 class="mb-3">
|
|
111
|
+
{{
|
|
112
|
+
$t(
|
|
113
|
+
'windward.integrations.components.integration.driver.saml_sso.button_settings'
|
|
114
|
+
)
|
|
115
|
+
}}
|
|
116
|
+
</h3>
|
|
117
|
+
|
|
118
|
+
<v-text-field
|
|
119
|
+
id="saml-button-label"
|
|
120
|
+
v-model="integration.metadata.config.button_label"
|
|
121
|
+
:label="
|
|
122
|
+
$t(
|
|
123
|
+
'windward.integrations.components.integration.driver.saml_sso.button_label'
|
|
124
|
+
)
|
|
125
|
+
"
|
|
126
|
+
:hint="
|
|
127
|
+
$t(
|
|
128
|
+
'windward.integrations.components.integration.driver.saml_sso.button_label_hint'
|
|
129
|
+
)
|
|
130
|
+
"
|
|
131
|
+
:placeholder="
|
|
132
|
+
$t(
|
|
133
|
+
'windward.integrations.components.integration.driver.saml_sso.button_label_default'
|
|
134
|
+
)
|
|
135
|
+
"
|
|
136
|
+
></v-text-field>
|
|
137
|
+
|
|
138
|
+
<v-text-field
|
|
139
|
+
id="saml-button-icon"
|
|
140
|
+
v-model="integration.metadata.config.button_icon"
|
|
141
|
+
:label="
|
|
142
|
+
$t(
|
|
143
|
+
'windward.integrations.components.integration.driver.saml_sso.button_icon'
|
|
144
|
+
)
|
|
145
|
+
"
|
|
146
|
+
:hint="
|
|
147
|
+
$t(
|
|
148
|
+
'windward.integrations.components.integration.driver.saml_sso.button_icon_hint'
|
|
149
|
+
)
|
|
150
|
+
"
|
|
151
|
+
placeholder="mdi-login"
|
|
152
|
+
>
|
|
153
|
+
<template #prepend>
|
|
154
|
+
<v-icon>{{
|
|
155
|
+
integration.metadata.config.button_icon ||
|
|
156
|
+
'mdi-login'
|
|
157
|
+
}}</v-icon>
|
|
158
|
+
</template>
|
|
159
|
+
</v-text-field>
|
|
160
|
+
|
|
161
|
+
<v-divider class="my-4" />
|
|
162
|
+
|
|
163
|
+
<v-alert type="info" outlined>
|
|
164
|
+
<div class="font-weight-bold mb-2">
|
|
165
|
+
{{
|
|
166
|
+
$t(
|
|
167
|
+
'windward.integrations.components.integration.driver.saml_sso.sp_details'
|
|
168
|
+
)
|
|
169
|
+
}}
|
|
170
|
+
</div>
|
|
171
|
+
<div class="text--secondary">
|
|
172
|
+
{{
|
|
173
|
+
$t(
|
|
174
|
+
'windward.integrations.components.integration.driver.saml_sso.sp_details_description'
|
|
175
|
+
)
|
|
176
|
+
}}
|
|
177
|
+
</div>
|
|
178
|
+
<div class="mt-3">
|
|
179
|
+
<div>
|
|
180
|
+
<strong
|
|
181
|
+
>{{
|
|
182
|
+
$t(
|
|
183
|
+
'windward.integrations.components.integration.driver.saml_sso.sp_entity_id_label'
|
|
184
|
+
)
|
|
185
|
+
}}:</strong
|
|
186
|
+
>
|
|
187
|
+
{{ spEntityId }}
|
|
188
|
+
</div>
|
|
189
|
+
<div>
|
|
190
|
+
<strong
|
|
191
|
+
>{{
|
|
192
|
+
$t(
|
|
193
|
+
'windward.integrations.components.integration.driver.saml_sso.acs_url_label'
|
|
194
|
+
)
|
|
195
|
+
}}:</strong
|
|
196
|
+
>
|
|
197
|
+
{{ acsUrl }}
|
|
198
|
+
</div>
|
|
199
|
+
<div>
|
|
200
|
+
<strong
|
|
201
|
+
>{{
|
|
202
|
+
$t(
|
|
203
|
+
'windward.integrations.components.integration.driver.saml_sso.slo_url_label'
|
|
204
|
+
)
|
|
205
|
+
}}:</strong
|
|
206
|
+
>
|
|
207
|
+
{{ sloUrl }}
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</v-alert>
|
|
211
|
+
|
|
212
|
+
<TestConnection
|
|
213
|
+
:disabled="
|
|
214
|
+
!integration.metadata.config.idp_entity_id ||
|
|
215
|
+
!integration.metadata.config.idp_sso_url ||
|
|
216
|
+
!integration.metadata.config.idp_slo_url ||
|
|
217
|
+
!integration.metadata.config.idp_x509_cert
|
|
218
|
+
"
|
|
219
|
+
:loading="testConnectionLoading"
|
|
220
|
+
:errors="errorMessage"
|
|
221
|
+
@click="onTestConnection"
|
|
222
|
+
></TestConnection>
|
|
223
|
+
</v-col>
|
|
224
|
+
</v-row>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</template>
|
|
228
|
+
|
|
229
|
+
<script>
|
|
230
|
+
import _ from 'lodash'
|
|
231
|
+
import TestConnection from '../TestConnection.vue'
|
|
232
|
+
import ManageBaseVue from './ManageBase.vue'
|
|
233
|
+
import SecretField from '../../SecretField.vue'
|
|
234
|
+
import Uuid from '~/helpers/Uuid'
|
|
235
|
+
|
|
236
|
+
export default {
|
|
237
|
+
name: 'ManageSamlDriver',
|
|
238
|
+
components: { TestConnection, SecretField },
|
|
239
|
+
extends: ManageBaseVue,
|
|
240
|
+
data() {
|
|
241
|
+
return {
|
|
242
|
+
// formValid: true|false If this form is "complete" and passed validation on THIS component. Defined and watched in ManageBase.vue
|
|
243
|
+
// render: true|false If we should show the form aka when validation has passed. Defined and managed in ManageBase.vue
|
|
244
|
+
// integration: { metadata: {...} } The integration object to write to. Defined and loaded in ManageBase.vue
|
|
245
|
+
showCertificate: false,
|
|
246
|
+
errorMessage: '',
|
|
247
|
+
testConnectionLoading: false,
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
computed: {
|
|
251
|
+
spEntityId() {
|
|
252
|
+
return `${process.env.BASE_URL}/saml/metadata`
|
|
253
|
+
},
|
|
254
|
+
acsUrl() {
|
|
255
|
+
return `${process.env.BASE_URL}/saml/acs`
|
|
256
|
+
},
|
|
257
|
+
sloUrl() {
|
|
258
|
+
return `${process.env.BASE_URL}/saml/slo`
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
methods: {
|
|
262
|
+
/**
|
|
263
|
+
* Lifecycle event called from ManageBase.vue when async fetch() completes.
|
|
264
|
+
* Once called this.integration will be available containing the integration model (or a new one)
|
|
265
|
+
*/
|
|
266
|
+
onIntegrationLoaded() {
|
|
267
|
+
// Initialize SAML config if not exists
|
|
268
|
+
if (!this.integration.metadata.config) {
|
|
269
|
+
this.integration.metadata.config = {
|
|
270
|
+
idp_entity_id: '',
|
|
271
|
+
idp_sso_url: '',
|
|
272
|
+
idp_slo_url: '',
|
|
273
|
+
idp_x509_cert: '',
|
|
274
|
+
button_label: '',
|
|
275
|
+
button_icon: 'mdi-login',
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Set defaults for button settings if not present
|
|
280
|
+
if (!this.integration.metadata.config.button_label) {
|
|
281
|
+
this.integration.metadata.config.button_label = ''
|
|
282
|
+
}
|
|
283
|
+
if (!this.integration.metadata.config.button_icon) {
|
|
284
|
+
this.integration.metadata.config.button_icon = 'mdi-login'
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Show the certificate by default if this is a new integration without a cert
|
|
288
|
+
this.showCertificate = _.isEmpty(
|
|
289
|
+
_.get(this.integration, 'metadata.config.idp_x509_cert', null)
|
|
290
|
+
)
|
|
291
|
+
},
|
|
292
|
+
async onTestConnection() {
|
|
293
|
+
this.testConnectionLoading = true
|
|
294
|
+
let response = { result: false }
|
|
295
|
+
try {
|
|
296
|
+
response = await this.testConnection(this.integration.metadata)
|
|
297
|
+
|
|
298
|
+
if (response.result) {
|
|
299
|
+
this.errorMessage = ''
|
|
300
|
+
this.$dialog.success(
|
|
301
|
+
this.$t(
|
|
302
|
+
'windward.integrations.shared.error.connect_success'
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
} else {
|
|
306
|
+
this.errorMessage = response.message
|
|
307
|
+
this.$dialog.error(
|
|
308
|
+
this.$t(
|
|
309
|
+
'windward.integrations.shared.error.connect_fail'
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
} catch (e) {
|
|
314
|
+
console.error(e)
|
|
315
|
+
this.$dialog.error(
|
|
316
|
+
this.$t('windward.integrations.shared.error.unknown')
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// We will indirectly validate the form via connection tests
|
|
321
|
+
// That way we can 100% confirm that the integration is valid
|
|
322
|
+
this.formValid = response.result
|
|
323
|
+
this.testConnectionLoading = false
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
}
|
|
327
|
+
</script>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-select
|
|
3
|
+
v-model="selectedDifficulty"
|
|
4
|
+
:items="taxonomyLevels"
|
|
5
|
+
item-text="text"
|
|
6
|
+
outlined
|
|
7
|
+
:hide-details="hideDetails"
|
|
8
|
+
dense
|
|
9
|
+
:disabled="disabled"
|
|
10
|
+
:label="
|
|
11
|
+
$t('windward.integrations.components.llm.blooms.blooms_taxonomy')
|
|
12
|
+
"
|
|
13
|
+
@input="$emit('input', selectedDifficulty)"
|
|
14
|
+
></v-select>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
import _ from 'lodash'
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
name: 'LLMBloomTaxonomySelector',
|
|
22
|
+
props: {
|
|
23
|
+
// The assessment question
|
|
24
|
+
value: {
|
|
25
|
+
type: [String, null],
|
|
26
|
+
required: false,
|
|
27
|
+
default: 'None',
|
|
28
|
+
},
|
|
29
|
+
levels: {
|
|
30
|
+
type: Array,
|
|
31
|
+
required: false,
|
|
32
|
+
default: () => {
|
|
33
|
+
return []
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
advancedLevels: { type: Boolean, required: false, default: false },
|
|
37
|
+
hideDetails: { type: Boolean, required: false, default: true },
|
|
38
|
+
disabled: { type: Boolean, required: false, default: false },
|
|
39
|
+
},
|
|
40
|
+
data() {
|
|
41
|
+
return {
|
|
42
|
+
selectedDifficulty: 'None',
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
computed: {
|
|
46
|
+
taxonomyLevels() {
|
|
47
|
+
let levels = []
|
|
48
|
+
|
|
49
|
+
// Basic Bloom's taxonomy levels available to all question types
|
|
50
|
+
const basicBloomTaxonomy = [
|
|
51
|
+
{
|
|
52
|
+
value: 'None',
|
|
53
|
+
text: this.$t(
|
|
54
|
+
'windward.integrations.components.llm.blooms.none'
|
|
55
|
+
),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
value: 'Remember',
|
|
59
|
+
text: this.$t(
|
|
60
|
+
'windward.integrations.components.llm.blooms.remember'
|
|
61
|
+
),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
value: 'Understand',
|
|
65
|
+
text: this.$t(
|
|
66
|
+
'windward.integrations.components.llm.blooms.understand'
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
value: 'Apply',
|
|
71
|
+
text: this.$t(
|
|
72
|
+
'windward.integrations.components.llm.blooms.apply'
|
|
73
|
+
),
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
const advancedBlooms = [
|
|
78
|
+
{
|
|
79
|
+
value: 'Analyze',
|
|
80
|
+
text: this.$t(
|
|
81
|
+
'windward.integrations.components.llm.blooms.analyze'
|
|
82
|
+
),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
value: 'Evaluate',
|
|
86
|
+
text: this.$t(
|
|
87
|
+
'windward.integrations.components.llm.blooms.evaluate'
|
|
88
|
+
),
|
|
89
|
+
},
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
// Manually defined levels
|
|
93
|
+
if (this.levels.length > 0) {
|
|
94
|
+
// All blooms for filtering
|
|
95
|
+
levels = levels.concat(basicBloomTaxonomy, advancedBlooms)
|
|
96
|
+
|
|
97
|
+
levels = levels.filter((v) => {
|
|
98
|
+
return this.levels.includes(v.value)
|
|
99
|
+
})
|
|
100
|
+
} else {
|
|
101
|
+
// Show all levels and omit advanced if not enabled
|
|
102
|
+
levels = levels.concat(basicBloomTaxonomy)
|
|
103
|
+
|
|
104
|
+
if (this.advancedLevels) {
|
|
105
|
+
levels = levels.concat(advancedBlooms)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return levels
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
mounted() {
|
|
112
|
+
if (_.isEmpty(this.value)) {
|
|
113
|
+
// Emit the default of 'None' as the initial value if none is set
|
|
114
|
+
this.$emit('input', this.selectedDifficulty)
|
|
115
|
+
} else {
|
|
116
|
+
this.selectedDifficulty = _.cloneDeep(this.value)
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
</script>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-select
|
|
3
|
+
v-model="selectedContent"
|
|
4
|
+
:items="flattenedContent"
|
|
5
|
+
outlined
|
|
6
|
+
hide-details
|
|
7
|
+
dense
|
|
8
|
+
:disabled="disabled"
|
|
9
|
+
:label="
|
|
10
|
+
$t(
|
|
11
|
+
'windward.integrations.components.llm.content_selector.selected_pages'
|
|
12
|
+
)
|
|
13
|
+
"
|
|
14
|
+
item-text="content.name"
|
|
15
|
+
return-object
|
|
16
|
+
@input="$emit('input', selectedContent)"
|
|
17
|
+
></v-select>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
import _ from 'lodash'
|
|
22
|
+
import { mapGetters } from 'vuex'
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
name: 'LLMContentSelector',
|
|
26
|
+
props: {
|
|
27
|
+
value: {
|
|
28
|
+
type: [Object, null],
|
|
29
|
+
required: false,
|
|
30
|
+
default: null,
|
|
31
|
+
},
|
|
32
|
+
disabled: { type: Boolean, required: false, default: false },
|
|
33
|
+
},
|
|
34
|
+
data() {
|
|
35
|
+
return {
|
|
36
|
+
selectedContent: null,
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
computed: {
|
|
40
|
+
...mapGetters({
|
|
41
|
+
content: 'content/get',
|
|
42
|
+
contentTree: 'content/getTree',
|
|
43
|
+
}),
|
|
44
|
+
flattenedContent() {
|
|
45
|
+
const flatTree = this.$ContentService.getFlatTree()
|
|
46
|
+
|
|
47
|
+
const homepage = this.$ContentService.getHomepage()
|
|
48
|
+
if (!_.isEmpty(homepage)) {
|
|
49
|
+
flatTree.unshift(homepage)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return flatTree
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
mounted() {
|
|
56
|
+
if (_.isEmpty(this.value)) {
|
|
57
|
+
this.selectedContent = _.cloneDeep(this.content)
|
|
58
|
+
|
|
59
|
+
// Emit the current page as selected if none was set
|
|
60
|
+
this.$emit('input', this.selectedContent)
|
|
61
|
+
} else {
|
|
62
|
+
this.selectedContent = _.cloneDeep(this.value)
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
</script>
|