Skip to content

Commit bc558b1

Browse files
committed
feat: reduce login steps
1 parent e3ebe50 commit bc558b1

File tree

10 files changed

+115
-85
lines changed

10 files changed

+115
-85
lines changed

frontend/src/components/ProviderSelector.vue

Lines changed: 81 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@ import { Plus } from 'lucide-vue-next'
33
import { onMounted, ref, watchEffect } from 'vue'
44
import { useI18n } from 'vue-i18n'
55
import { Badge } from '@/components/ui/badge'
6+
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
67
import { useProvidersStore } from '@/stores'
78
import AddProviderDialog from './AddProviderDialog.vue'
9+
import ManualConfigCard from './ManualConfigCard.vue'
10+
import SiliconFlowConfigDialog from './SiliconFlowConfigDialog.vue'
811
import Button from './ui/button/Button.vue'
912
13+
const props = defineProps<{
14+
compact?: boolean
15+
}>()
1016
const selectedProviderId = defineModel<string>()
11-
1217
const { t } = useI18n()
1318
const providersStore = useProvidersStore()
1419
1520
const openAddProviderDialog = ref(false)
21+
const showSiliconFlowConfig = ref(false)
1622
1723
onMounted(() => providersStore.loadProviders())
1824
@@ -28,47 +34,89 @@ watchEffect(() => {
2834

2935
<template>
3036
<div class="space-y-2">
31-
<div class="text-sm font-medium">
32-
{{ t('providers.selectProvider') }}:
33-
</div>
3437
<div v-if="providersStore.loading" class="text-sm text-muted-foreground">
3538
{{ t('providers.loadingProviders') }}
3639
</div>
3740
<div v-else-if="providersStore.providers.length === 0" class="text-sm text-muted-foreground">
38-
<span class="text-red-500 dark:text-red-600">
39-
{{ t('providers.noProvidersAvailable') }}
40-
</span>
41+
<template v-if="props.compact">
42+
<span class="text-red-500 dark:text-red-600">
43+
{{ t('providers.noProvidersAvailable') }}
44+
</span>
45+
46+
<Button variant="link" class="text-blue-600 dark:text-blue-400 underline hover:opacity-80" @click="openAddProviderDialog = true">
47+
{{ t('providers.addNewProvider') }}
48+
</Button>
49+
</template>
50+
<template v-else>
51+
<div class="grid gap-4 grid-cols-2 mb-4">
52+
<!-- SiliconFlow configuration card (if not configured) -->
53+
<Card class="relative gap-2">
54+
<CardHeader>
55+
<div class="flex items-center justify-between">
56+
<CardTitle class="text-lg">
57+
{{ t('providers.siliconFlow') }}
58+
</CardTitle>
59+
</div>
60+
</CardHeader>
61+
62+
<CardContent>
63+
<div class="text-sm text-muted-foreground">
64+
<p>
65+
{{ t('providers.siliconFlowDescription1') }}
66+
<a href="https://www.siliconflow.cn" target="_blank" class="text-blue-900 dark:text-blue-200 hover:underline">
67+
{{ t('providers.siliconFlow') }}
68+
</a>
69+
{{ t('providers.siliconFlowDescription2') }}
70+
</p>
71+
</div>
72+
</CardContent>
73+
74+
<CardFooter>
75+
<Button class="w-full" @click="showSiliconFlowConfig = true">
76+
{{ t('providers.configureSiliconFlow') }}
77+
</Button>
78+
</CardFooter>
79+
</Card>
4180

42-
<Button variant="link" class="text-blue-600 dark:text-blue-400 underline hover:opacity-80" @click="openAddProviderDialog = true">
43-
{{ t('providers.addNewProvider') }}
44-
</Button>
81+
<ManualConfigCard class="relative gap-2" />
82+
</div>
83+
</template>
4584
</div>
46-
<div v-else class="flex flex-wrap gap-2">
47-
<Badge
48-
v-for="provider in providersStore.providers"
49-
:key="provider.id"
50-
:variant="selectedProviderId === provider.id ? 'default' : 'secondary'"
51-
class="cursor-pointer transition-colors"
52-
:class="{
53-
'bg-blue-500 hover:bg-blue-600 dark:bg-blue-800 dark:hover:bg-blue-600 text-white': selectedProviderId === provider.id,
54-
'bg-muted hover:bg-muted-foreground': selectedProviderId !== provider.id,
55-
}"
56-
@click="selectedProviderId = provider.id"
57-
>
58-
{{ provider.name }}
59-
</Badge>
85+
<div v-else class="flex gap-4">
86+
<div :class="compact ? 'text-sm font-medium mb-1' : 'text-base font-bold mb-1'">
87+
{{ t('providers.selectProvider') }}
88+
</div>
89+
<div class="w-0 flex-grow flex flex-wrap gap-2 h-fit">
90+
<Badge
91+
v-for="provider in providersStore.providers"
92+
:key="provider.id"
93+
:variant="selectedProviderId === provider.id ? 'default' : 'secondary'"
94+
class="cursor-pointer transition-colors"
95+
:class="{
96+
'bg-blue-500 hover:bg-blue-600 dark:bg-blue-800 dark:hover:bg-blue-600 text-white': selectedProviderId === provider.id,
97+
'bg-muted hover:bg-gray-300 dark:hover:bg-gray-700': selectedProviderId !== provider.id,
98+
}"
99+
@click="selectedProviderId = provider.id"
100+
>
101+
{{ provider.name }}
102+
</Badge>
60103

61-
<!-- Add new provider button -->
62-
<Badge
63-
variant="secondary"
64-
class="text-xs cursor-pointer transition-colors bg-muted hover:bg-muted-foreground h-6"
65-
@click="openAddProviderDialog = true"
66-
>
67-
<Plus class="inline h-4 w-4" />
68-
{{ t('providers.addNewProvider') }}
69-
</Badge>
104+
<!-- Add new provider button -->
105+
<Badge
106+
variant="secondary"
107+
class="ml-auto text-xs cursor-pointer transition-colors bg-muted hover:bg-gray-300 dark:hover:bg-gray-700 h-6"
108+
@click="openAddProviderDialog = true"
109+
>
110+
<Plus class="inline h-4 w-4" />
111+
{{ t('providers.addNewProvider') }}
112+
</Badge>
113+
</div>
70114
</div>
71115

116+
<SiliconFlowConfigDialog
117+
v-model:open="showSiliconFlowConfig"
118+
@configured="selectedProviderId = $event"
119+
/>
72120
<AddProviderDialog v-model:open="openAddProviderDialog" @configured="selectedProviderId = $event" />
73121
</div>
74122
</template>

frontend/src/components/ProvidersSection.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const showEditDialog = ref(false)
2121
const editingProvider = ref<LLMProvider | null>(null)
2222
2323
function editProvider(provider: LLMProvider) {
24+
if (provider.type === 'siliconflow') {
25+
showSiliconFlowConfig.value = true
26+
return
27+
}
2428
editingProvider.value = provider
2529
showEditDialog.value = true
2630
}

frontend/src/components/SiliconFlowCard.vue

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,6 @@
22
import { onMounted, ref } from 'vue'
33
import { useI18n } from 'vue-i18n'
44
import { toast } from 'vue-sonner'
5-
import {
6-
AlertDialog,
7-
AlertDialogAction,
8-
AlertDialogCancel,
9-
AlertDialogContent,
10-
AlertDialogDescription,
11-
AlertDialogFooter,
12-
AlertDialogHeader,
13-
AlertDialogTitle,
14-
AlertDialogTrigger,
15-
} from '@/components/ui/alert-dialog'
165
import { Button } from '@/components/ui/button'
176
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
187
import { Skeleton } from '@/components/ui/skeleton'
@@ -48,13 +37,6 @@ function handleRecharge() {
4837
siliconFlowStore.openPaymentDialog()
4938
}
5039
}
51-
52-
async function handleCreateKey() {
53-
const result = await siliconFlowStore.createApiKeyAndApply()
54-
if (result) {
55-
emit('configured', result.id)
56-
}
57-
}
5840
</script>
5941

6042
<template>
@@ -98,7 +80,7 @@ async function handleCreateKey() {
9880
</div>
9981
</CardContent>
10082
<CardFooter class="pt-2 flex gap-2">
101-
<AlertDialog>
83+
<!-- <AlertDialog>
10284
<AlertDialogTrigger as-child>
10385
<Button variant="default" size="sm" class="h-8">
10486
{{ t('siliconFlow.createKey') }}
@@ -118,7 +100,11 @@ async function handleCreateKey() {
118100
</AlertDialogAction>
119101
</AlertDialogFooter>
120102
</AlertDialogContent>
121-
</AlertDialog>
103+
</AlertDialog> -->
104+
<Button variant="default" size="sm" class="h-8" @click="emit('configured', siliconFlowStore.providerId!)">
105+
{{ t('common.confirm') }}
106+
</Button>
107+
<div class="flex-grow" />
122108
<Button variant="secondary" size="sm" class="h-8" @click="handleRecharge">
123109
{{ t('siliconFlow.recharge') }}
124110
</Button>
Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,18 @@
11
<script setup lang="ts">
2-
import { useI18n } from 'vue-i18n'
32
import SiliconFlowCard from '@/components/SiliconFlowCard.vue'
4-
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
3+
import { Dialog, DialogContent } from '@/components/ui/dialog'
54
65
const emit = defineEmits<{
76
configured: [provider: string]
87
}>()
98
10-
const { t } = useI18n()
119
const open = defineModel<boolean>('open')
1210
</script>
1311

1412
<template>
1513
<Dialog v-model:open="open">
16-
<DialogContent class="sm:max-w-lg">
17-
<DialogHeader>
18-
<DialogTitle>{{ t('providers.configureSiliconFlow') }}</DialogTitle>
19-
<DialogDescription>
20-
{{ t('providers.siliconFlowDescription') }}
21-
</DialogDescription>
22-
</DialogHeader>
23-
24-
<div class="my-2">
25-
<SiliconFlowCard @configured="(provider) => emit('configured', provider)" />
26-
</div>
14+
<DialogContent class="sm:max-w-lg p-0 border-none">
15+
<SiliconFlowCard show-title @configured="(provider) => emit('configured', provider)" />
2716
</DialogContent>
2817
</Dialog>
2918
</template>

frontend/src/components/SiliconFlowLoginCard.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function wxLogin() {
9999
</div>
100100
<Button class="w-full h-10" :disabled="!siliconFlowStore.canLogin" @click="siliconFlowStore.login()">
101101
<span v-if="siliconFlowStore.isLoading">{{ t('siliconFlow.loggingIn') }}</span>
102-
<span v-else>{{ t('siliconFlow.registerLogin') }}</span>
102+
<span v-else>{{ siliconFlowStore.isEmailLogin ? t('siliconFlow.login') : t('siliconFlow.registerLogin') }}</span>
103103
</Button>
104104
<div class="w-full grid grid-cols-2 gap-3">
105105
<Dialog>

frontend/src/locales/en-US.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@
273273
"emailLogin": "Email Login",
274274
"phoneLogin": "Phone Login",
275275
"emailAddress": "Email Address",
276-
"emailAddressPlaceholder": "Your email address"
276+
"emailAddressPlaceholder": "Your email address",
277+
"login": "Login"
277278
},
278279
"presets": {
279280
"preset": "Preset",
@@ -378,7 +379,7 @@
378379
},
379380
"captcha": {
380381
"getCode": "Get Verification Code",
381-
"smsSent": "SMS Sent"
382+
"smsSent": "Code Sent"
382383
},
383384
"editProviderDialog": {
384385
"title": "Edit Provider",

frontend/src/locales/zh-CN.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@
273273
"emailLogin": "邮箱登录",
274274
"phoneLogin": "短信登录",
275275
"emailAddress": "邮箱地址",
276-
"emailAddressPlaceholder": "您的邮箱地址"
276+
"emailAddressPlaceholder": "您的邮箱地址",
277+
"login": "登录"
277278
},
278279
"presets": {
279280
"preset": "预设",
@@ -378,7 +379,7 @@
378379
},
379380
"captcha": {
380381
"getCode": "获取验证码",
381-
"smsSent": "短信已发送"
382+
"smsSent": "已发送"
382383
},
383384
"editProviderDialog": {
384385
"title": "编辑 Provider",

frontend/src/stores/siliconflow.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ export const useSiliconFlowStore = defineStore('siliconflow', () => {
8181
const phoneNumber = ref('')
8282
const email = ref('')
8383
const smsCode = ref('')
84-
const agreed = ref(false)
84+
const agreed = ref(true)
8585
const keepLogin = ref(true)
86-
const isLoading = ref(false)
86+
const isLoading = ref(true)
8787
const isEmailLogin = ref(false)
8888

8989
// Payment related state
@@ -129,7 +129,6 @@ export const useSiliconFlowStore = defineStore('siliconflow', () => {
129129

130130
// Actions
131131
async function checkLoginStatus() {
132-
isLoading.value = true
133132
try {
134133
const res = await fetch('siliconflow/status', {
135134
method: 'GET',
@@ -593,6 +592,7 @@ export const useSiliconFlowStore = defineStore('siliconflow', () => {
593592
canLogin,
594593
canCreatePayment,
595594
authed: computed(() => userInfo.value?.data?.auth === 1),
595+
providerId: computed(() => providersStore.providers.find(p => p.type === 'siliconflow')?.id || null),
596596

597597
// Actions
598598
checkLoginStatus,

frontend/src/views/ActionHandler.vue

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { toast } from 'vue-sonner'
55
import ProviderSelector from '@/components/ProviderSelector.vue'
66
import { Button } from '@/components/ui/button'
77
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
8+
import { useProvidersStore } from '@/stores'
89
import { useAppStore } from '@/stores/app'
910
1011
const { t } = useI18n()
@@ -14,6 +15,7 @@ let actionType = params.get('action')
1415
const open = ref(!!actionType)
1516
const selectedProvider = ref<string>('')
1617
const appStore = useAppStore()
18+
const providersStore = useProvidersStore()
1719
1820
async function registerAction(granted: boolean) {
1921
const appId = params.get('appId')
@@ -39,7 +41,7 @@ watch(open, (open) => {
3941

4042
<template>
4143
<Dialog v-model:open="open">
42-
<DialogContent v-if="actionType === 'register'" class="sm:max-w-lg">
44+
<DialogContent v-if="actionType === 'register'" class="sm:max-w-xl">
4345
<DialogHeader>
4446
<DialogTitle>{{ t('actionHandler.appPermissionRequest') }}</DialogTitle>
4547
<DialogDescription>
@@ -50,18 +52,17 @@ watch(open, (open) => {
5052
<div class="space-y-4">
5153
<div class="space-y-2">
5254
<div>
53-
<strong>{{ t('actionHandler.appName') }}: </strong>
54-
<span>{{ params.get('appName') }}</span>
55+
<strong class="mr-4">{{ t('actionHandler.appName') }}</strong>
56+
<span class="text-lg">{{ params.get('appName') }}</span>
5557
</div>
5658
<div>
57-
<strong>{{ t('actionHandler.appDescription') }}: </strong>
58-
<span>{{ params.get('appDescription') }}</span>
59+
<strong class="mr-4">{{ t('actionHandler.appDescription') }}</strong>
60+
<span class="text-lg">{{ params.get('appDescription') }}</span>
5961
</div>
62+
<ProviderSelector v-model="selectedProvider" class="pt-1"/>
6063
</div>
6164

62-
<ProviderSelector v-model="selectedProvider" />
63-
64-
<div class="flex justify-end mt-6 gap-2">
65+
<div v-if="providersStore.providers.length > 0" class="flex justify-end mt-6 gap-2">
6566
<Button variant="outline" @click="registerAction(false)">
6667
{{ t('actionHandler.deny') }}
6768
</Button>

frontend/src/views/AppManagement.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ onMounted(() => {
9292
{{ app.granted ? t('appManagement.hasAccess') : t('appManagement.noAccess') }}
9393
</p>
9494
</div>
95-
<div :class="{ 'opacity-40 pointer-events-none select-none': !app.granted }" class="mt-4">
96-
<ProviderSelector v-model="app.provider" @update:model-value="appStore.toggleAppAuthorization(app.id, true, app.provider)" />
95+
<div :class="{ 'opacity-0 pointer-events-none select-none': !app.granted }" class="mt-4 transition-opacity duration-300">
96+
<ProviderSelector v-model="app.provider" compact @update:model-value="appStore.toggleAppAuthorization(app.id, true, app.provider)" />
9797
</div>
9898
</CardContent>
9999
</Card>

0 commit comments

Comments
 (0)