@cat-factory/app 0.46.10 → 0.46.12
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/app/components/auth/AuthGate.vue +2 -1
- package/app/components/auth/LoginScreen.vue +41 -31
- package/app/components/auth/ResetPasswordScreen.vue +16 -13
- package/app/components/auth/UserMenu.vue +3 -2
- package/app/components/layout/AccountDeploymentSettings.vue +75 -32
- package/app/components/layout/AccountFragmentSettings.vue +2 -3
- package/app/components/layout/AccountTeamSettings.vue +75 -45
- package/app/components/layout/AiProvidersBanner.vue +11 -11
- package/app/components/layout/BoardSwitcher.vue +63 -29
- package/app/components/layout/CommandBar.vue +68 -54
- package/app/components/layout/GitHubPatBanner.vue +13 -6
- package/app/components/layout/IntegrationBackTitle.vue +4 -1
- package/app/components/layout/IntegrationsHub.vue +67 -49
- package/app/components/layout/NotificationsInbox.vue +48 -19
- package/app/components/layout/PersonalSetupModal.vue +26 -16
- package/app/components/layout/ProviderConfigBanner.vue +14 -9
- package/app/components/layout/SideBar.vue +20 -18
- package/app/components/layout/SpendWarningBanner.vue +16 -16
- package/app/components/observability/StepMetricsBar.vue +31 -11
- package/app/components/observability/StepModelActivity.vue +3 -2
- package/app/components/panels/AgentStepDetail.vue +52 -39
- package/app/components/panels/DecisionModal.vue +12 -5
- package/app/components/panels/GenericStructuredResultView.vue +16 -6
- package/app/components/panels/InspectorPanel.vue +35 -28
- package/app/components/panels/ObservabilityPanel.vue +92 -49
- package/app/components/panels/StepMetadataCard.vue +87 -30
- package/app/components/panels/StepRestartControl.vue +4 -3
- package/app/components/panels/StepRunMeta.vue +23 -10
- package/app/components/panels/StepTestReport.vue +14 -11
- package/app/components/panels/inspector/ContainerSummary.vue +25 -14
- package/app/components/panels/inspector/EpicChildren.vue +7 -4
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +60 -19
- package/app/components/panels/inspector/ServiceFragments.vue +3 -2
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +18 -13
- package/app/components/panels/inspector/ServiceTestConfig.vue +50 -39
- package/app/components/panels/inspector/TaskAgentConfig.vue +6 -5
- package/app/components/panels/inspector/TaskDependencies.vue +9 -4
- package/app/components/panels/inspector/TaskEstimateBadge.vue +14 -13
- package/app/components/panels/inspector/TaskExecution.vue +65 -31
- package/app/components/panels/inspector/TaskRunSettings.vue +93 -56
- package/app/components/panels/inspector/TaskStructure.vue +5 -4
- package/app/components/providers/PersonalSubscriptionSection.vue +1 -2
- package/i18n/locales/en.json +814 -0
- package/i18n/locales/es.json +811 -0
- package/i18n/locales/fr.json +811 -0
- package/i18n/locales/pl.json +811 -0
- package/i18n/locales/uk.json +811 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import LoginScreen from '~/components/auth/LoginScreen.vue'
|
|
|
7
7
|
// inside the default slot, so it only fires once the user is allowed in.
|
|
8
8
|
const auth = useAuthStore()
|
|
9
9
|
const route = useRoute()
|
|
10
|
+
const { t } = useI18n()
|
|
10
11
|
|
|
11
12
|
// The password-reset page is public: a recipient of an emailed reset link is signed
|
|
12
13
|
// out, so it must render even when auth is required and there's no user.
|
|
@@ -21,7 +22,7 @@ onMounted(() => auth.bootstrap())
|
|
|
21
22
|
class="flex h-screen w-screen flex-col items-center justify-center gap-3 bg-slate-950 text-slate-400"
|
|
22
23
|
>
|
|
23
24
|
<UIcon name="i-lucide-loader" class="h-8 w-8 animate-spin" />
|
|
24
|
-
<span class="text-sm">
|
|
25
|
+
<span class="text-sm">{{ t('auth.gate.loading') }}</span>
|
|
25
26
|
</div>
|
|
26
27
|
|
|
27
28
|
<slot v-else-if="isPublicRoute" />
|
|
@@ -3,6 +3,7 @@ import { computed, ref } from 'vue'
|
|
|
3
3
|
import { apiErrorEnvelope } from '~/composables/api/errors'
|
|
4
4
|
|
|
5
5
|
const auth = useAuthStore()
|
|
6
|
+
const { t } = useI18n()
|
|
6
7
|
|
|
7
8
|
// An invite token may ride in on the URL (?invite=…) — it flows through the OAuth
|
|
8
9
|
// redirect and the password signup so a brand-new user can join the org on first login.
|
|
@@ -41,8 +42,7 @@ async function submitPassword() {
|
|
|
41
42
|
// Reload so the app boots with the new session.
|
|
42
43
|
if (typeof window !== 'undefined') window.location.assign(window.location.pathname)
|
|
43
44
|
} catch (e) {
|
|
44
|
-
error.value =
|
|
45
|
-
apiErrorEnvelope(e)?.message ?? 'Sign-in failed. Check your details and try again.'
|
|
45
|
+
error.value = apiErrorEnvelope(e)?.message ?? t('auth.login.signInFailed')
|
|
46
46
|
} finally {
|
|
47
47
|
busy.value = false
|
|
48
48
|
}
|
|
@@ -56,7 +56,7 @@ async function submitForgot() {
|
|
|
56
56
|
forgotSent.value = true
|
|
57
57
|
} catch {
|
|
58
58
|
// The request endpoint is generic by design; only an infra error lands here.
|
|
59
|
-
error.value = '
|
|
59
|
+
error.value = t('auth.login.genericError')
|
|
60
60
|
} finally {
|
|
61
61
|
busy.value = false
|
|
62
62
|
}
|
|
@@ -81,11 +81,11 @@ const showOAuthDivider = computed(
|
|
|
81
81
|
>
|
|
82
82
|
<div class="mb-6 text-center">
|
|
83
83
|
<UIcon name="i-lucide-layout-dashboard" class="mx-auto mb-3 h-10 w-10 text-indigo-400" />
|
|
84
|
-
<h1 class="mb-1 text-lg font-semibold text-white">
|
|
84
|
+
<h1 class="mb-1 text-lg font-semibold text-white">{{ t('auth.login.appTitle') }}</h1>
|
|
85
85
|
<p class="text-sm text-slate-400">
|
|
86
|
-
<template v-if="mode === 'forgot'">
|
|
86
|
+
<template v-if="mode === 'forgot'">{{ t('auth.login.forgotSubtitle') }}</template>
|
|
87
87
|
<template v-else>{{
|
|
88
|
-
invite ? '
|
|
88
|
+
invite ? t('auth.login.inviteSubtitle') : t('auth.login.subtitle')
|
|
89
89
|
}}</template>
|
|
90
90
|
</p>
|
|
91
91
|
</div>
|
|
@@ -100,7 +100,7 @@ const showOAuthDivider = computed(
|
|
|
100
100
|
icon="i-lucide-github"
|
|
101
101
|
@click="auth.login(invite)"
|
|
102
102
|
>
|
|
103
|
-
|
|
103
|
+
{{ t('auth.login.continueWithGithub') }}
|
|
104
104
|
</UButton>
|
|
105
105
|
<UButton
|
|
106
106
|
v-if="auth.providers.google"
|
|
@@ -111,7 +111,7 @@ const showOAuthDivider = computed(
|
|
|
111
111
|
icon="i-lucide-mail"
|
|
112
112
|
@click="auth.loginWithGoogle(invite)"
|
|
113
113
|
>
|
|
114
|
-
|
|
114
|
+
{{ t('auth.login.continueWithGoogle') }}
|
|
115
115
|
</UButton>
|
|
116
116
|
</div>
|
|
117
117
|
|
|
@@ -119,7 +119,8 @@ const showOAuthDivider = computed(
|
|
|
119
119
|
v-if="showOAuthDivider && mode !== 'forgot'"
|
|
120
120
|
class="my-4 flex items-center gap-3 text-xs text-slate-500"
|
|
121
121
|
>
|
|
122
|
-
<span class="h-px flex-1 bg-slate-800" /> or
|
|
122
|
+
<span class="h-px flex-1 bg-slate-800" /> {{ t('auth.login.or') }}
|
|
123
|
+
<span class="h-px flex-1 bg-slate-800" />
|
|
123
124
|
</div>
|
|
124
125
|
|
|
125
126
|
<!-- Email / password -->
|
|
@@ -131,7 +132,7 @@ const showOAuthDivider = computed(
|
|
|
131
132
|
<UInput
|
|
132
133
|
v-if="mode === 'signup'"
|
|
133
134
|
v-model="name"
|
|
134
|
-
placeholder="
|
|
135
|
+
:placeholder="t('auth.login.namePlaceholder')"
|
|
135
136
|
icon="i-lucide-user"
|
|
136
137
|
size="lg"
|
|
137
138
|
class="w-full"
|
|
@@ -140,7 +141,7 @@ const showOAuthDivider = computed(
|
|
|
140
141
|
v-model="email"
|
|
141
142
|
type="email"
|
|
142
143
|
required
|
|
143
|
-
placeholder="
|
|
144
|
+
:placeholder="t('auth.login.emailPlaceholder')"
|
|
144
145
|
icon="i-lucide-at-sign"
|
|
145
146
|
size="lg"
|
|
146
147
|
class="w-full"
|
|
@@ -149,36 +150,46 @@ const showOAuthDivider = computed(
|
|
|
149
150
|
v-model="password"
|
|
150
151
|
type="password"
|
|
151
152
|
required
|
|
152
|
-
placeholder="
|
|
153
|
+
:placeholder="t('auth.login.passwordPlaceholder')"
|
|
153
154
|
icon="i-lucide-lock"
|
|
154
155
|
size="lg"
|
|
155
156
|
class="w-full"
|
|
156
157
|
/>
|
|
157
158
|
<p v-if="error" class="text-sm text-rose-400">{{ error }}</p>
|
|
158
159
|
<UButton block size="lg" color="primary" type="submit" :loading="busy">
|
|
159
|
-
{{ mode === 'signup' ? '
|
|
160
|
+
{{ mode === 'signup' ? t('auth.login.createAccount') : t('auth.login.signIn') }}
|
|
160
161
|
</UButton>
|
|
161
162
|
<p class="text-center text-xs text-slate-400">
|
|
162
163
|
<template v-if="mode === 'login'">
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
164
|
+
<i18n-t keypath="auth.login.needAccount" tag="span" scope="global">
|
|
165
|
+
<template #signUp>
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
class="text-indigo-400 hover:underline"
|
|
169
|
+
@click="setMode('signup')"
|
|
170
|
+
>
|
|
171
|
+
{{ t('auth.login.signUp') }}
|
|
172
|
+
</button>
|
|
173
|
+
</template>
|
|
174
|
+
</i18n-t>
|
|
171
175
|
</template>
|
|
172
176
|
<template v-else>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
<i18n-t keypath="auth.login.haveAccount" tag="span" scope="global">
|
|
178
|
+
<template #signIn>
|
|
179
|
+
<button
|
|
180
|
+
type="button"
|
|
181
|
+
class="text-indigo-400 hover:underline"
|
|
182
|
+
@click="setMode('login')"
|
|
183
|
+
>
|
|
184
|
+
{{ t('auth.login.signIn') }}
|
|
185
|
+
</button>
|
|
186
|
+
</template>
|
|
187
|
+
</i18n-t>
|
|
177
188
|
</template>
|
|
178
189
|
</p>
|
|
179
190
|
<p v-if="mode === 'login'" class="text-center text-xs text-slate-400">
|
|
180
191
|
<button type="button" class="text-indigo-400 hover:underline" @click="setMode('forgot')">
|
|
181
|
-
|
|
192
|
+
{{ t('auth.login.forgotPassword') }}
|
|
182
193
|
</button>
|
|
183
194
|
</p>
|
|
184
195
|
</form>
|
|
@@ -191,8 +202,7 @@ const showOAuthDivider = computed(
|
|
|
191
202
|
>
|
|
192
203
|
<template v-if="forgotSent">
|
|
193
204
|
<p class="text-sm text-slate-300">
|
|
194
|
-
|
|
195
|
-
inbox.
|
|
205
|
+
{{ t('auth.login.forgotSent') }}
|
|
196
206
|
</p>
|
|
197
207
|
</template>
|
|
198
208
|
<template v-else>
|
|
@@ -200,19 +210,19 @@ const showOAuthDivider = computed(
|
|
|
200
210
|
v-model="email"
|
|
201
211
|
type="email"
|
|
202
212
|
required
|
|
203
|
-
placeholder="
|
|
213
|
+
:placeholder="t('auth.login.emailPlaceholder')"
|
|
204
214
|
icon="i-lucide-at-sign"
|
|
205
215
|
size="lg"
|
|
206
216
|
class="w-full"
|
|
207
217
|
/>
|
|
208
218
|
<p v-if="error" class="text-sm text-rose-400">{{ error }}</p>
|
|
209
219
|
<UButton block size="lg" color="primary" type="submit" :loading="busy">
|
|
210
|
-
|
|
220
|
+
{{ t('auth.login.sendResetLink') }}
|
|
211
221
|
</UButton>
|
|
212
222
|
</template>
|
|
213
223
|
<p class="text-center text-xs text-slate-400">
|
|
214
224
|
<button type="button" class="text-indigo-400 hover:underline" @click="setMode('login')">
|
|
215
|
-
|
|
225
|
+
{{ t('auth.login.backToSignIn') }}
|
|
216
226
|
</button>
|
|
217
227
|
</p>
|
|
218
228
|
</form>
|
|
@@ -5,6 +5,7 @@ import { computed, ref } from 'vue'
|
|
|
5
5
|
// (`/reset-password?token=…`). It is a public route (see AuthGate), so a recipient who
|
|
6
6
|
// is signed out can still set a new password. On success we send them to the login.
|
|
7
7
|
const auth = useAuthStore()
|
|
8
|
+
const { t } = useI18n()
|
|
8
9
|
|
|
9
10
|
const token = computed(() => {
|
|
10
11
|
if (typeof window === 'undefined') return ''
|
|
@@ -20,11 +21,11 @@ const done = ref(false)
|
|
|
20
21
|
async function submit() {
|
|
21
22
|
error.value = null
|
|
22
23
|
if (password.value.length < 8) {
|
|
23
|
-
error.value = '
|
|
24
|
+
error.value = t('auth.resetPassword.errorTooShort')
|
|
24
25
|
return
|
|
25
26
|
}
|
|
26
27
|
if (password.value !== confirm.value) {
|
|
27
|
-
error.value = '
|
|
28
|
+
error.value = t('auth.resetPassword.errorMismatch')
|
|
28
29
|
return
|
|
29
30
|
}
|
|
30
31
|
busy.value = true
|
|
@@ -34,7 +35,7 @@ async function submit() {
|
|
|
34
35
|
} catch (e) {
|
|
35
36
|
error.value =
|
|
36
37
|
(e as { data?: { error?: { message?: string } } })?.data?.error?.message ??
|
|
37
|
-
'
|
|
38
|
+
t('auth.resetPassword.errorInvalidLink')
|
|
38
39
|
} finally {
|
|
39
40
|
busy.value = false
|
|
40
41
|
}
|
|
@@ -52,23 +53,25 @@ function goToLogin() {
|
|
|
52
53
|
>
|
|
53
54
|
<div class="mb-6 text-center">
|
|
54
55
|
<UIcon name="i-lucide-key-round" class="mx-auto mb-3 h-10 w-10 text-indigo-400" />
|
|
55
|
-
<h1 class="mb-1 text-lg font-semibold text-white">
|
|
56
|
-
<p class="text-sm text-slate-400">
|
|
56
|
+
<h1 class="mb-1 text-lg font-semibold text-white">{{ t('auth.resetPassword.title') }}</h1>
|
|
57
|
+
<p class="text-sm text-slate-400">{{ t('auth.resetPassword.subtitle') }}</p>
|
|
57
58
|
</div>
|
|
58
59
|
|
|
59
60
|
<template v-if="done">
|
|
60
61
|
<p class="mb-4 text-sm text-slate-300">
|
|
61
|
-
|
|
62
|
+
{{ t('auth.resetPassword.doneBody') }}
|
|
62
63
|
</p>
|
|
63
|
-
<UButton block size="lg" color="primary" @click="goToLogin">
|
|
64
|
+
<UButton block size="lg" color="primary" @click="goToLogin">{{
|
|
65
|
+
t('auth.resetPassword.goToSignIn')
|
|
66
|
+
}}</UButton>
|
|
64
67
|
</template>
|
|
65
68
|
|
|
66
69
|
<template v-else-if="!token">
|
|
67
70
|
<p class="mb-4 text-sm text-rose-400">
|
|
68
|
-
|
|
71
|
+
{{ t('auth.resetPassword.missingToken') }}
|
|
69
72
|
</p>
|
|
70
73
|
<UButton block size="lg" color="neutral" variant="subtle" @click="goToLogin">
|
|
71
|
-
|
|
74
|
+
{{ t('auth.resetPassword.backToSignIn') }}
|
|
72
75
|
</UButton>
|
|
73
76
|
</template>
|
|
74
77
|
|
|
@@ -77,7 +80,7 @@ function goToLogin() {
|
|
|
77
80
|
v-model="password"
|
|
78
81
|
type="password"
|
|
79
82
|
required
|
|
80
|
-
placeholder="
|
|
83
|
+
:placeholder="t('auth.resetPassword.newPasswordPlaceholder')"
|
|
81
84
|
icon="i-lucide-lock"
|
|
82
85
|
size="lg"
|
|
83
86
|
class="w-full"
|
|
@@ -86,18 +89,18 @@ function goToLogin() {
|
|
|
86
89
|
v-model="confirm"
|
|
87
90
|
type="password"
|
|
88
91
|
required
|
|
89
|
-
placeholder="
|
|
92
|
+
:placeholder="t('auth.resetPassword.confirmPasswordPlaceholder')"
|
|
90
93
|
icon="i-lucide-lock"
|
|
91
94
|
size="lg"
|
|
92
95
|
class="w-full"
|
|
93
96
|
/>
|
|
94
97
|
<p v-if="error" class="text-sm text-rose-400">{{ error }}</p>
|
|
95
98
|
<UButton block size="lg" color="primary" type="submit" :loading="busy">
|
|
96
|
-
|
|
99
|
+
{{ t('auth.resetPassword.submit') }}
|
|
97
100
|
</UButton>
|
|
98
101
|
<p class="text-center text-xs text-slate-400">
|
|
99
102
|
<button type="button" class="text-indigo-400 hover:underline" @click="goToLogin">
|
|
100
|
-
|
|
103
|
+
{{ t('auth.resetPassword.backToSignIn') }}
|
|
101
104
|
</button>
|
|
102
105
|
</p>
|
|
103
106
|
</form>
|
|
@@ -4,20 +4,21 @@ import type { DropdownMenuItem } from '@nuxt/ui'
|
|
|
4
4
|
// Signed-in identity + per-user actions, shown in the sidebar when auth is enabled.
|
|
5
5
|
const auth = useAuthStore()
|
|
6
6
|
const ui = useUiStore()
|
|
7
|
+
const { t } = useI18n()
|
|
7
8
|
|
|
8
9
|
const items = computed<DropdownMenuItem[][]>(() => [
|
|
9
10
|
[
|
|
10
11
|
{
|
|
11
12
|
// The user-scoped "My setup" hub (personal GitHub token, local runners, personal
|
|
12
13
|
// subscriptions) — kept out of the workspace Integrations hub, reachable here.
|
|
13
|
-
label: '
|
|
14
|
+
label: t('auth.userMenu.mySetup'),
|
|
14
15
|
icon: 'i-lucide-user-cog',
|
|
15
16
|
onSelect: () => ui.openPersonalSetup(),
|
|
16
17
|
},
|
|
17
18
|
],
|
|
18
19
|
[
|
|
19
20
|
{
|
|
20
|
-
label: '
|
|
21
|
+
label: t('auth.userMenu.signOut'),
|
|
21
22
|
icon: 'i-lucide-log-out',
|
|
22
23
|
onSelect: () => auth.logout(),
|
|
23
24
|
},
|
|
@@ -11,6 +11,7 @@ const props = defineProps<{ accountId: string }>()
|
|
|
11
11
|
|
|
12
12
|
const store = useAccountSettingsStore()
|
|
13
13
|
const toast = useToast()
|
|
14
|
+
const { t } = useI18n()
|
|
14
15
|
|
|
15
16
|
const slack = reactive({ clientId: '', clientSecret: '', redirectUrl: '' })
|
|
16
17
|
const web = reactive({ braveApiKey: '', searxngUrl: '', searxngApiKey: '' })
|
|
@@ -24,7 +25,7 @@ onMounted(async () => {
|
|
|
24
25
|
await store.load(props.accountId)
|
|
25
26
|
} catch (e) {
|
|
26
27
|
toast.add({
|
|
27
|
-
title: '
|
|
28
|
+
title: t('layout.accountDeployment.loadFailed'),
|
|
28
29
|
description: e instanceof Error ? e.message : String(e),
|
|
29
30
|
icon: 'i-lucide-triangle-alert',
|
|
30
31
|
color: 'error',
|
|
@@ -34,7 +35,7 @@ onMounted(async () => {
|
|
|
34
35
|
|
|
35
36
|
async function saveSlack() {
|
|
36
37
|
if (!slack.clientId.trim() || !slack.clientSecret.trim() || !slack.redirectUrl.trim()) {
|
|
37
|
-
toast.add({ title: '
|
|
38
|
+
toast.add({ title: t('layout.accountDeployment.slack.validation'), color: 'error' })
|
|
38
39
|
return
|
|
39
40
|
}
|
|
40
41
|
savingSlack.value = true
|
|
@@ -51,10 +52,14 @@ async function saveSlack() {
|
|
|
51
52
|
slack.clientId = ''
|
|
52
53
|
slack.clientSecret = ''
|
|
53
54
|
slack.redirectUrl = ''
|
|
54
|
-
toast.add({
|
|
55
|
+
toast.add({
|
|
56
|
+
title: t('layout.accountDeployment.slack.saved'),
|
|
57
|
+
icon: 'i-lucide-check',
|
|
58
|
+
color: 'success',
|
|
59
|
+
})
|
|
55
60
|
} catch (e) {
|
|
56
61
|
toast.add({
|
|
57
|
-
title: '
|
|
62
|
+
title: t('layout.accountDeployment.slack.saveFailed'),
|
|
58
63
|
description: e instanceof Error ? e.message : String(e),
|
|
59
64
|
color: 'error',
|
|
60
65
|
})
|
|
@@ -67,10 +72,14 @@ async function clearSlack() {
|
|
|
67
72
|
savingSlack.value = true
|
|
68
73
|
try {
|
|
69
74
|
await store.save(props.accountId, { secrets: { slackOAuth: null } })
|
|
70
|
-
toast.add({
|
|
75
|
+
toast.add({
|
|
76
|
+
title: t('layout.accountDeployment.slack.cleared'),
|
|
77
|
+
icon: 'i-lucide-check',
|
|
78
|
+
color: 'success',
|
|
79
|
+
})
|
|
71
80
|
} catch (e) {
|
|
72
81
|
toast.add({
|
|
73
|
-
title: '
|
|
82
|
+
title: t('layout.accountDeployment.slack.clearFailed'),
|
|
74
83
|
description: e instanceof Error ? e.message : String(e),
|
|
75
84
|
color: 'error',
|
|
76
85
|
})
|
|
@@ -83,7 +92,7 @@ async function saveWeb() {
|
|
|
83
92
|
const brave = web.braveApiKey.trim()
|
|
84
93
|
const searxng = web.searxngUrl.trim()
|
|
85
94
|
if (!brave && !searxng) {
|
|
86
|
-
toast.add({ title: '
|
|
95
|
+
toast.add({ title: t('layout.accountDeployment.web.validation'), color: 'error' })
|
|
87
96
|
return
|
|
88
97
|
}
|
|
89
98
|
savingWeb.value = true
|
|
@@ -100,10 +109,14 @@ async function saveWeb() {
|
|
|
100
109
|
web.braveApiKey = ''
|
|
101
110
|
web.searxngUrl = ''
|
|
102
111
|
web.searxngApiKey = ''
|
|
103
|
-
toast.add({
|
|
112
|
+
toast.add({
|
|
113
|
+
title: t('layout.accountDeployment.web.saved'),
|
|
114
|
+
icon: 'i-lucide-check',
|
|
115
|
+
color: 'success',
|
|
116
|
+
})
|
|
104
117
|
} catch (e) {
|
|
105
118
|
toast.add({
|
|
106
|
-
title: '
|
|
119
|
+
title: t('layout.accountDeployment.web.saveFailed'),
|
|
107
120
|
description: e instanceof Error ? e.message : String(e),
|
|
108
121
|
color: 'error',
|
|
109
122
|
})
|
|
@@ -116,10 +129,14 @@ async function clearWeb() {
|
|
|
116
129
|
savingWeb.value = true
|
|
117
130
|
try {
|
|
118
131
|
await store.save(props.accountId, { secrets: { webSearch: null } })
|
|
119
|
-
toast.add({
|
|
132
|
+
toast.add({
|
|
133
|
+
title: t('layout.accountDeployment.web.cleared'),
|
|
134
|
+
icon: 'i-lucide-check',
|
|
135
|
+
color: 'success',
|
|
136
|
+
})
|
|
120
137
|
} catch (e) {
|
|
121
138
|
toast.add({
|
|
122
|
-
title: '
|
|
139
|
+
title: t('layout.accountDeployment.web.clearFailed'),
|
|
123
140
|
description: e instanceof Error ? e.message : String(e),
|
|
124
141
|
color: 'error',
|
|
125
142
|
})
|
|
@@ -132,38 +149,50 @@ async function clearWeb() {
|
|
|
132
149
|
<template>
|
|
133
150
|
<div v-if="store.available !== false" class="space-y-6">
|
|
134
151
|
<div>
|
|
135
|
-
<h3 class="mb-1 font-semibold text-white">
|
|
152
|
+
<h3 class="mb-1 font-semibold text-white">{{ t('layout.accountDeployment.title') }}</h3>
|
|
136
153
|
<p class="text-[11px] text-slate-400">
|
|
137
|
-
|
|
138
|
-
shown after saving; leave a field blank to keep the stored secret.
|
|
154
|
+
{{ t('layout.accountDeployment.intro') }}
|
|
139
155
|
</p>
|
|
140
156
|
</div>
|
|
141
157
|
|
|
142
158
|
<!-- Slack app OAuth -->
|
|
143
159
|
<section class="space-y-2">
|
|
144
160
|
<div class="flex items-center gap-2">
|
|
145
|
-
<h4 class="text-sm font-semibold text-slate-200">
|
|
161
|
+
<h4 class="text-sm font-semibold text-slate-200">
|
|
162
|
+
{{ t('layout.accountDeployment.slack.title') }}
|
|
163
|
+
</h4>
|
|
146
164
|
<UBadge
|
|
147
165
|
:color="summary?.slackOAuthConfigured ? 'success' : 'neutral'"
|
|
148
166
|
variant="subtle"
|
|
149
167
|
size="xs"
|
|
150
168
|
>
|
|
151
|
-
{{
|
|
169
|
+
{{
|
|
170
|
+
summary?.slackOAuthConfigured
|
|
171
|
+
? t('layout.accountDeployment.configured')
|
|
172
|
+
: t('layout.accountDeployment.notSet')
|
|
173
|
+
}}
|
|
152
174
|
</UBadge>
|
|
153
175
|
</div>
|
|
154
176
|
<p class="text-[11px] text-slate-400">
|
|
155
|
-
|
|
156
|
-
pasting a bot token.
|
|
177
|
+
{{ t('layout.accountDeployment.slack.description') }}
|
|
157
178
|
</p>
|
|
158
179
|
<div class="grid grid-cols-1 gap-2 sm:grid-cols-3">
|
|
159
|
-
<UInput
|
|
180
|
+
<UInput
|
|
181
|
+
v-model="slack.clientId"
|
|
182
|
+
:placeholder="t('layout.accountDeployment.slack.clientId')"
|
|
183
|
+
size="sm"
|
|
184
|
+
/>
|
|
160
185
|
<UInput
|
|
161
186
|
v-model="slack.clientSecret"
|
|
162
187
|
type="password"
|
|
163
|
-
placeholder="
|
|
188
|
+
:placeholder="t('layout.accountDeployment.slack.clientSecret')"
|
|
189
|
+
size="sm"
|
|
190
|
+
/>
|
|
191
|
+
<UInput
|
|
192
|
+
v-model="slack.redirectUrl"
|
|
193
|
+
:placeholder="t('layout.accountDeployment.slack.redirectUrl')"
|
|
164
194
|
size="sm"
|
|
165
195
|
/>
|
|
166
|
-
<UInput v-model="slack.redirectUrl" placeholder="Redirect URL" size="sm" />
|
|
167
196
|
</div>
|
|
168
197
|
<div class="flex gap-2">
|
|
169
198
|
<UButton
|
|
@@ -173,7 +202,7 @@ async function clearWeb() {
|
|
|
173
202
|
:loading="savingSlack"
|
|
174
203
|
@click="saveSlack"
|
|
175
204
|
>
|
|
176
|
-
|
|
205
|
+
{{ t('common.save') }}
|
|
177
206
|
</UButton>
|
|
178
207
|
<UButton
|
|
179
208
|
v-if="summary?.slackOAuthConfigured"
|
|
@@ -183,7 +212,7 @@ async function clearWeb() {
|
|
|
183
212
|
:loading="savingSlack"
|
|
184
213
|
@click="clearSlack"
|
|
185
214
|
>
|
|
186
|
-
|
|
215
|
+
{{ t('layout.accountDeployment.clear') }}
|
|
187
216
|
</UButton>
|
|
188
217
|
</div>
|
|
189
218
|
</section>
|
|
@@ -191,22 +220,36 @@ async function clearWeb() {
|
|
|
191
220
|
<!-- Web search keys -->
|
|
192
221
|
<section class="space-y-2 border-t border-slate-800 pt-6">
|
|
193
222
|
<div class="flex items-center gap-2">
|
|
194
|
-
<h4 class="text-sm font-semibold text-slate-200">
|
|
223
|
+
<h4 class="text-sm font-semibold text-slate-200">
|
|
224
|
+
{{ t('layout.accountDeployment.web.title') }}
|
|
225
|
+
</h4>
|
|
195
226
|
<UBadge :color="summary?.webSearch ? 'success' : 'neutral'" variant="subtle" size="xs">
|
|
196
|
-
{{
|
|
227
|
+
{{
|
|
228
|
+
summary?.webSearch
|
|
229
|
+
? t('layout.accountDeployment.web.configured', { provider: summary.webSearch })
|
|
230
|
+
: t('layout.accountDeployment.notSet')
|
|
231
|
+
}}
|
|
197
232
|
</UBadge>
|
|
198
233
|
</div>
|
|
199
234
|
<p class="text-[11px] text-slate-400">
|
|
200
|
-
|
|
201
|
-
(recommended), or a self-hosted SearXNG URL (with an optional bearer key).
|
|
235
|
+
{{ t('layout.accountDeployment.web.description') }}
|
|
202
236
|
</p>
|
|
203
237
|
<div class="grid grid-cols-1 gap-2 sm:grid-cols-3">
|
|
204
|
-
<UInput
|
|
205
|
-
|
|
238
|
+
<UInput
|
|
239
|
+
v-model="web.braveApiKey"
|
|
240
|
+
type="password"
|
|
241
|
+
:placeholder="t('layout.accountDeployment.web.braveKey')"
|
|
242
|
+
size="sm"
|
|
243
|
+
/>
|
|
244
|
+
<UInput
|
|
245
|
+
v-model="web.searxngUrl"
|
|
246
|
+
:placeholder="t('layout.accountDeployment.web.searxngUrl')"
|
|
247
|
+
size="sm"
|
|
248
|
+
/>
|
|
206
249
|
<UInput
|
|
207
250
|
v-model="web.searxngApiKey"
|
|
208
251
|
type="password"
|
|
209
|
-
placeholder="
|
|
252
|
+
:placeholder="t('layout.accountDeployment.web.searxngKey')"
|
|
210
253
|
size="sm"
|
|
211
254
|
/>
|
|
212
255
|
</div>
|
|
@@ -218,7 +261,7 @@ async function clearWeb() {
|
|
|
218
261
|
:loading="savingWeb"
|
|
219
262
|
@click="saveWeb"
|
|
220
263
|
>
|
|
221
|
-
|
|
264
|
+
{{ t('common.save') }}
|
|
222
265
|
</UButton>
|
|
223
266
|
<UButton
|
|
224
267
|
v-if="summary?.webSearch"
|
|
@@ -228,7 +271,7 @@ async function clearWeb() {
|
|
|
228
271
|
:loading="savingWeb"
|
|
229
272
|
@click="clearWeb"
|
|
230
273
|
>
|
|
231
|
-
|
|
274
|
+
{{ t('layout.accountDeployment.clear') }}
|
|
232
275
|
</UButton>
|
|
233
276
|
</div>
|
|
234
277
|
</section>
|
|
@@ -8,6 +8,7 @@ import FragmentLibraryManager from '~/components/fragments/FragmentLibraryManage
|
|
|
8
8
|
|
|
9
9
|
const props = defineProps<{ accountId: string }>()
|
|
10
10
|
const workspace = useWorkspaceStore()
|
|
11
|
+
const { t } = useI18n()
|
|
11
12
|
|
|
12
13
|
// Account-tier document fragments are fetched through a board's stored
|
|
13
14
|
// document-source connection (credentials are per-workspace). Prefer the active
|
|
@@ -22,9 +23,7 @@ const viaWorkspaceId = computed(() => {
|
|
|
22
23
|
<div class="space-y-6 text-sm">
|
|
23
24
|
<section>
|
|
24
25
|
<p class="mb-3 text-[11px] text-slate-400">
|
|
25
|
-
|
|
26
|
-
and can override or add its own. Code-aware agents (coder, reviewer, architect, fixers) fold
|
|
27
|
-
the relevant ones into their prompt.
|
|
26
|
+
{{ t('layout.accountFragments.intro') }}
|
|
28
27
|
</p>
|
|
29
28
|
<FragmentLibraryManager
|
|
30
29
|
kind="account"
|