fix: clear loading on request failure

This commit is contained in:
Quicy
2026-04-07 17:19:08 +08:00
committed by 晨苒
parent 18e19cc176
commit fb71f5a5f1
10 changed files with 109 additions and 91 deletions
+1
View File
@@ -22,6 +22,7 @@
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-serialize": "^0.14.0",
"@xterm/xterm": "^6.0.0",
"animate.css": "^4.1.1",
"dotenv": "^17.2.3",
"echarts": "^6.0.0",
"katex": "^0.16.28",
@@ -109,7 +109,7 @@
<!-- Compatibilities (chat only) -->
<div v-if="selectedType === 'chat'">
<Label class="mb-2">
<Label class="mb-4">
{{ $t('models.compatibilities') }}
</Label>
<div class="flex flex-wrap gap-3 mt-2">
+1
View File
@@ -3,6 +3,7 @@ import './style.css'
import App from './App.vue'
import router from './router'
import { setupApiClient } from './lib/api-client'
import 'animate.css'
// Configure SDK client before anything else
setupApiClient()
@@ -52,79 +52,84 @@
</PopoverContent>
</Popover>
</div>
<div
v-if="bindingsLoading"
class="flex items-center gap-2 text-xs text-muted-foreground p-4"
>
<Spinner />
<span>{{ $t('common.loading') }}</span>
</div>
<div
v-else-if="!bindings?.length"
class="rounded-md border p-4"
>
<p class="text-xs text-muted-foreground">
{{ $t('bots.email.noBindings') }}
</p>
</div>
<div
v-else
class="space-y-2"
<Transition
enter-active-class="animate__fadeIn animate__animated"
leave-active-class="animate__fadeOut"
mode="out-in"
>
<div
v-for="binding in bindings"
:key="binding.id"
v-if="bindingsLoading"
class="flex items-center gap-2 text-xs text-muted-foreground p-4"
>
<Spinner />
<span>{{ $t('common.loading') }}</span>
</div>
<div
v-else-if="!bindings?.length"
class="rounded-md border p-4"
>
<div class="flex items-center justify-between">
<div class="min-w-0">
<p class="font-medium text-xs">
{{ providerNameMap[binding.email_provider_id!] || binding.email_provider_id }}
</p>
<p
v-if="binding.email_address"
class="text-xs text-muted-foreground mt-0.5"
>
{{ binding.email_address }}
</p>
</div>
<ConfirmPopover
:message="$t('bots.email.unbindConfirm')"
:loading="deletingId === binding.id"
@confirm="handleDeleteBinding(binding.id!)"
>
<template #trigger>
<Button
variant="destructive"
size="sm"
<p class="text-xs text-muted-foreground">
{{ $t('bots.email.noBindings') }}
</p>
</div>
<div
v-else
class="space-y-2"
>
<div
v-for="binding in bindings"
:key="binding.id"
class="rounded-md border p-4"
>
<div class="flex items-center justify-between">
<div class="min-w-0">
<p class="font-medium text-xs">
{{ providerNameMap[binding.email_provider_id!] || binding.email_provider_id }}
</p>
<p
v-if="binding.email_address"
class="text-xs text-muted-foreground mt-0.5"
>
{{ $t('bots.email.unbind') }}
</Button>
</template>
</ConfirmPopover>
</div>
<Separator class="my-3" />
<div class="flex gap-6 text-xs">
<label class="flex items-center gap-2 cursor-pointer">
<Switch
:model-value="binding.can_read"
@update:model-value="(v) => handleTogglePerm(binding, 'can_read', !!v)"
/>
<span>{{ $t('bots.email.canRead') }}</span>
</label>
<label class="flex items-center gap-2 cursor-pointer">
<Switch
:model-value="binding.can_write"
@update:model-value="(v) => handleTogglePerm(binding, 'can_write', !!v)"
/>
<span>{{ $t('bots.email.canWrite') }}</span>
</label>
{{ binding.email_address }}
</p>
</div>
<ConfirmPopover
:message="$t('bots.email.unbindConfirm')"
:loading="deletingId === binding.id"
@confirm="handleDeleteBinding(binding.id!)"
>
<template #trigger>
<Button
variant="destructive"
size="sm"
>
{{ $t('bots.email.unbind') }}
</Button>
</template>
</ConfirmPopover>
</div>
<Separator class="my-3" />
<div class="flex gap-6 text-xs">
<label class="flex items-center gap-2 cursor-pointer">
<Switch
:model-value="binding.can_read"
@update:model-value="(v) => handleTogglePerm(binding, 'can_read', !!v)"
/>
<span>{{ $t('bots.email.canRead') }}</span>
</label>
<label class="flex items-center gap-2 cursor-pointer">
<Switch
:model-value="binding.can_write"
@update:model-value="(v) => handleTogglePerm(binding, 'can_write', !!v)"
/>
<span>{{ $t('bots.email.canWrite') }}</span>
</label>
</div>
</div>
</div>
</div>
</Transition>
</div>
<Separator />
@@ -578,7 +578,7 @@
<p class="text-xs text-muted-foreground mt-2">
{{ $t('mcp.importHint') }}
</p>
<div class="h-[350px] rounded-md border overflow-hidden mt-3">
<div class="h-87.5 rounded-md border overflow-hidden mt-3">
<MonacoEditor
v-model="importJson"
language="json"
@@ -610,7 +610,7 @@
<DialogHeader>
<DialogTitle>{{ $t('common.export') }} mcpServers</DialogTitle>
</DialogHeader>
<div class="h-[350px] rounded-md border overflow-hidden mt-4">
<div class="h-87.5 rounded-md border overflow-hidden mt-4">
<MonacoEditor
:model-value="exportJson"
language="json"
@@ -176,7 +176,7 @@
<!-- Charts Section -->
<div
v-if="showChartSection"
class="h-[240px] border-t flex flex-col bg-muted/5 shrink-0"
class="h-60 border-t flex flex-col bg-muted/5 shrink-0"
>
<div class="px-3 py-1.5 border-b bg-muted/10 flex items-center justify-between shrink-0">
<h5 class="text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70">
@@ -222,7 +222,7 @@
<h3 class="text-xs font-medium text-foreground">
{{ $t('bots.memory.title') }}
</h3>
<p class="text-xs mt-1 max-w-[240px]">
<p class="text-xs mt-1 max-w-60">
Select a file from the sidebar to view or edit, or create a new one to persist long-term information for your bot.
</p>
<Button
@@ -26,12 +26,12 @@
name="name"
>
<FormItem>
<Label :for="componentField.id || 'browser-context-name'">
<Label :for="'browser-context-name'">
{{ $t('browser.name') }}
</Label>
<FormControl>
<Input
:id="componentField.id || 'browser-context-name'"
:id="'browser-context-name'"
type="text"
:placeholder="$t('browser.namePlaceholder')"
v-bind="componentField"
@@ -78,7 +78,10 @@ const { mutateAsync: createMutation, isLoading } = useMutation({
})
return result
},
onSettled: () => queryCache.invalidateQueries({ key: ['browser-contexts'] }),
onSettled: () => queryCache.invalidateQueries({ key: ['browser-contexts'] }).catch((err)=>{console.error(err)}),
onError: (_, __, previous) => {
queryCache.setQueryData(['browser-contexts'], previous)
},
})
const schema = toTypedSchema(z.object({
@@ -26,12 +26,12 @@
name="name"
>
<FormItem>
<Label :for="componentField.id || 'email-provider-name'">
<Label :for="'email-provider-name'">
{{ $t('common.name') }}
</Label>
<FormControl>
<Input
:id="componentField.id || 'email-provider-name'"
:id="'email-provider-name'"
type="text"
:placeholder="$t('common.namePlaceholder')"
v-bind="componentField"
@@ -44,13 +44,13 @@
name="provider"
>
<FormItem>
<Label :for="componentField.id || 'email-provider-type'">
<Label :for=" 'email-provider-type'">
{{ $t('email.providerType') }}
</Label>
<FormControl>
<Select v-bind="componentField">
<SelectTrigger
:id="componentField.id || 'email-provider-type'"
:id="'email-provider-type'"
class="w-full"
>
<SelectValue :placeholder="$t('common.typePlaceholder')" />
@@ -25,12 +25,12 @@
name="name"
>
<FormItem>
<Label :for="componentField.id || 'email-provider-name'">
<Label :for="'email-provider-name'">
{{ $t('common.name') }}
</Label>
<FormControl>
<Input
:id="componentField.id || 'email-provider-name'"
:id="'email-provider-name'"
type="text"
:placeholder="$t('common.namePlaceholder')"
v-bind="componentField"
@@ -68,17 +68,17 @@
>
<Input
:id="`email-field-${field.key}`"
v-model="configData[field.key]"
:type="visibleSecrets[field.key] ? 'text' : 'password'"
v-model="configData[field.key!] as string"
:type="visibleSecrets[field.key!] ? 'text' : 'password'"
:placeholder="field.example ? String(field.example) : ''"
/>
<button
type="button"
class="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
@click="visibleSecrets[field.key] = !visibleSecrets[field.key]"
@click="visibleSecrets[field.key!] = !visibleSecrets[field.key!]"
>
<component
:is="visibleSecrets[field.key] ? EyeOff : Eye"
:is="visibleSecrets[field.key!] ? EyeOff : Eye"
class="size-3.5"
/>
</button>
@@ -86,22 +86,22 @@
<Switch
v-else-if="field.type === 'bool'"
:model-value="!!configData[field.key]"
@update:model-value="(val) => configData[field.key] = !!val"
:model-value="!!configData[field.key!]"
@update:model-value="(val) => configData[field.key!] = !!val"
/>
<Input
v-else-if="field.type === 'number'"
:id="`email-field-${field.key}`"
v-model.number="configData[field.key]"
v-model.number="configData[field.key!] as string"
type="number"
:placeholder="field.example ? String(field.example) : ''"
/>
<Select
v-else-if="field.type === 'enum' && field.enum"
:model-value="String(configData[field.key] || '')"
@update:model-value="(val) => configData[field.key] = val"
:model-value="String(configData[field.key!] || '')"
@update:model-value="(val) => configData[field.key!] = val"
>
<SelectTrigger>
<SelectValue :placeholder="field.title || field.key" />
@@ -120,7 +120,7 @@
<Input
v-else
:id="`email-field-${field.key}`"
v-model="configData[field.key]"
v-model="configData[field.key !] as string"
type="text"
:placeholder="field.example ? String(field.example) : ''"
/>
@@ -133,7 +133,7 @@
class="mt-6 p-4 border rounded-lg bg-muted/30"
>
<div class="flex flex-wrap items-start justify-between gap-4">
<div class="flex-1 min-w-[220px]">
<div class="flex-1 min-w-55">
<p class="text-xs font-medium">
{{ $t('email.oauth.title') }}
</p>
+8
View File
@@ -131,6 +131,9 @@ importers:
'@xterm/xterm':
specifier: ^6.0.0
version: 6.0.0
animate.css:
specifier: ^4.1.1
version: 4.1.1
dotenv:
specifier: ^17.2.3
version: 17.2.3
@@ -2543,6 +2546,9 @@ packages:
alien-signals@3.1.2:
resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
animate.css@4.1.1:
resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==}
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
@@ -6939,6 +6945,8 @@ snapshots:
alien-signals@3.1.2: {}
animate.css@4.1.1: {}
ansi-colors@4.1.3: {}
ansi-escapes@7.3.0: