Skip to content

Commit 38bb4ae

Browse files
committed
fix(admin-ui): restore legacy settings and auth surfaces
1 parent 0ca9d94 commit 38bb4ae

6 files changed

Lines changed: 776 additions & 394 deletions

File tree

web/admin/src/layouts/AdminLayout.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ const activeAccount = computed(() =>
3838
accountsQuery.data.value?.accounts.find((account) => account.isActive),
3939
)
4040
41+
const zhLanguageOptionLabel = computed(() =>
42+
locale.value === "zh" ? "\u7b80\u4f53\u4e2d\u6587" : "Simplified Chinese",
43+
)
44+
4145
function formatAccountType(accountType: AccountType): string {
4246
if (accountType === "business") {
4347
return t("accounts.accountTypeBusiness")
@@ -144,7 +148,7 @@ async function handleLogout(): Promise<void> {
144148
@change="preferencesStore.setLocale(($event.target as HTMLSelectElement).value as 'zh' | 'en')"
145149
>
146150
<option value="en">English</option>
147-
<option value="zh">简体中文</option>
151+
<option value="zh">{{ zhLanguageOptionLabel }}</option>
148152
</select>
149153
</div>
150154

web/admin/src/pages/LoginPage.vue

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { onBeforeUnmount, onMounted, ref } from "vue"
2+
import { computed, onBeforeUnmount, onMounted, ref } from "vue"
33
import { useI18n } from "vue-i18n"
44
import { useRoute, useRouter } from "vue-router"
55
@@ -13,11 +13,34 @@ const { locale, t } = useI18n()
1313
const noticeStore = useNoticeStore()
1414
const sessionStore = useSessionStore()
1515
16+
const LOGIN_PAGE_TEXT = {
17+
en: {
18+
badge: "Admin Access",
19+
description:
20+
"Enter the management secret to access the copilot-api admin dashboard.",
21+
title: "Admin Console Login",
22+
},
23+
zh: {
24+
badge: "\u7ba1\u7406\u8bbf\u95ee",
25+
description:
26+
"\u8f93\u5165\u7ba1\u7406\u5bc6\u94a5\u4ee5\u8bbf\u95ee copilot-api \u7684\u540e\u53f0\u7ba1\u7406\u9762\u677f\u3002",
27+
title: "\u7ba1\u7406\u63a7\u5236\u53f0\u767b\u5f55",
28+
},
29+
} as const
30+
1631
const secret = ref("")
1732
const secretInput = ref<HTMLInputElement | null>(null)
1833
const loading = ref(false)
1934
const errorMessage = ref("")
2035
36+
const zhLanguageOptionLabel = computed(() =>
37+
locale.value === "zh" ? "\u7b80\u4f53\u4e2d\u6587" : "Simplified Chinese",
38+
)
39+
40+
const loginPageText = computed(() =>
41+
locale.value === "zh" ? LOGIN_PAGE_TEXT.zh : LOGIN_PAGE_TEXT.en,
42+
)
43+
2144
onMounted(() => {
2245
document.body.classList.add("auth-body")
2346
secretInput.value?.focus()
@@ -30,12 +53,14 @@ onBeforeUnmount(() => {
3053
async function submit(): Promise<void> {
3154
errorMessage.value = ""
3255
const normalizedSecret = secret.value.trim()
56+
3357
if (!normalizedSecret) {
3458
errorMessage.value = t("auth.invalidSecret")
3559
return
3660
}
3761
3862
loading.value = true
63+
3964
try {
4065
await sessionStore.login(normalizedSecret)
4166
noticeStore.success(t("auth.loginSuccess"))
@@ -57,17 +82,17 @@ async function submit(): Promise<void> {
5782
<main class="panel">
5883
<section class="panel-header">
5984
<div class="header-top">
60-
<span class="eyebrow">{{ t("auth.loginBadge") }}</span>
85+
<span class="eyebrow">{{ loginPageText.badge }}</span>
6186
<div class="language-box">
6287
<label for="loginLanguageSelect">{{ t("common.language") }}</label>
6388
<select id="loginLanguageSelect" v-model="locale">
6489
<option value="en">English</option>
65-
<option value="zh">简体中文</option>
90+
<option value="zh">{{ zhLanguageOptionLabel }}</option>
6691
</select>
6792
</div>
6893
</div>
69-
<h1>{{ t("auth.loginTitle") }}</h1>
70-
<p>{{ t("auth.loginCopy") }}</p>
94+
<h1>{{ loginPageText.title }}</h1>
95+
<p>{{ loginPageText.description }}</p>
7196
</section>
7297

7398
<section class="panel-body">
@@ -84,8 +109,8 @@ async function submit(): Promise<void> {
84109
v-model="secret"
85110
type="password"
86111
autocomplete="current-password"
87-
>
88-
</label>
112+
>
113+
</label>
89114

90115
<button type="submit" :disabled="loading">
91116
{{ loading ? t("common.loading") : t("auth.submitLogin") }}

web/admin/src/pages/ManualPage.vue

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { computed } from "vue"
33
import { useI18n } from "vue-i18n"
44
5-
const { locale, t } = useI18n()
5+
const { locale } = useI18n()
66
77
const manualZhHtml = `<section class="manual-section">
88
<div class="manual-section-title">1. 每个端点可用模型</div>
@@ -81,15 +81,25 @@ const manualEnHtml = `<section class="manual-section">
8181
const manualHtml = computed(() =>
8282
locale.value === "zh" ? manualZhHtml : manualEnHtml,
8383
)
84+
85+
const manualTitle = computed(() =>
86+
locale.value === "zh" ? "\u4f7f\u7528\u624b\u518c" : "User Manual",
87+
)
88+
89+
const manualSubtitle = computed(() =>
90+
locale.value === "zh" ?
91+
"\u7aef\u70b9\u6620\u5c04 + \u8de8\u9879\u76ee\u63a5\u5165"
92+
: "Endpoint mapping + cross-project integration",
93+
)
8494
</script>
8595

8696
<template>
8797
<div id="tab-manual" class="tab-content active">
8898
<div class="card">
8999
<div class="card-header">
90100
<div class="settings-header-main">
91-
<span class="card-title">{{ t("manual.title") }}</span>
92-
<p class="settings-subtitle">{{ t("manual.subtitle") }}</p>
101+
<span class="card-title">{{ manualTitle }}</span>
102+
<p class="settings-subtitle">{{ manualSubtitle }}</p>
93103
</div>
94104
</div>
95105
<article class="manual-doc" v-html="manualHtml" />

web/admin/src/pages/ModelMappingsPage.vue

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query"
33
import { computed, ref } from "vue"
44
import { useI18n } from "vue-i18n"
55
6-
import { fetchModelMappings, saveModelMapping, deleteModelMapping } from "@/api/mappings"
6+
import {
7+
deleteModelMapping,
8+
fetchModelMappings,
9+
saveModelMapping,
10+
} from "@/api/mappings"
711
import { fetchAdminModels, fetchPremiumConfig } from "@/api/models"
812
import { fetchAdminSettings } from "@/api/settings"
913
import { adminQueryKeys } from "@/query/keys"
@@ -18,14 +22,17 @@ const mappingsQuery = useQuery({
1822
queryKey: adminQueryKeys.mappings,
1923
queryFn: fetchModelMappings,
2024
})
25+
2126
const modelsQuery = useQuery({
2227
queryKey: adminQueryKeys.models,
2328
queryFn: fetchAdminModels,
2429
})
30+
2531
const premiumQuery = useQuery({
2632
queryKey: adminQueryKeys.premiumConfig,
2733
queryFn: fetchPremiumConfig,
2834
})
35+
2936
const settingsQuery = useQuery({
3037
queryKey: adminQueryKeys.settings,
3138
queryFn: fetchAdminSettings,
@@ -37,7 +44,8 @@ const targetModel = ref("")
3744
3845
const availableModels = computed(() => {
3946
const hiddenModels = new Set(premiumQuery.data.value?.hiddenModels ?? [])
40-
const disableHiddenModels = settingsQuery.data.value?.disableHiddenModels ?? false
47+
const disableHiddenModels =
48+
settingsQuery.data.value?.disableHiddenModels ?? false
4149
4250
return (modelsQuery.data.value?.data ?? []).filter((model) => {
4351
if (!disableHiddenModels) {
@@ -48,6 +56,18 @@ const availableModels = computed(() => {
4856
})
4957
})
5058
59+
const targetModelSelectDisabled = computed(() =>
60+
modelsQuery.isLoading.value
61+
|| premiumQuery.isLoading.value
62+
|| settingsQuery.isLoading.value,
63+
)
64+
65+
const targetModelOptionText = computed(() =>
66+
targetModelSelectDisabled.value ?
67+
t("mappings.loadingModels")
68+
: t("mappings.selectTarget"),
69+
)
70+
5171
const saveMutation = useMutation({
5272
mutationFn: async () =>
5373
saveModelMapping(fromAlias.value.trim(), targetModel.value.trim()),
@@ -113,12 +133,12 @@ function openMappingForm(): void {
113133

114134
<template>
115135
<div id="tab-model-mappings" class="tab-content active">
116-
<div class="card">
117-
<div class="card-header">
118-
<span class="card-title">{{ t("mappings.title") }}</span>
119-
<button type="button" class="btn btn-primary btn-sm" @click="openMappingForm">
120-
{{ t("mappings.add") }}
121-
</button>
136+
<div class="card">
137+
<div class="card-header">
138+
<span class="card-title">{{ t("mappings.title") }}</span>
139+
<button type="button" class="btn btn-primary btn-sm" @click="openMappingForm">
140+
{{ t("mappings.add") }}
141+
</button>
122142
</div>
123143

124144
<div class="mapping-form" :class="{ active: formOpen }">
@@ -128,9 +148,13 @@ function openMappingForm(): void {
128148
class="select mapping-input-inline"
129149
:placeholder="t('mappings.fromPlaceholder')"
130150
>
131-
<span class="mapping-arrow">→</span>
132-
<select v-model="targetModel" class="select mapping-input-inline">
133-
<option value="">{{ t("mappings.selectTarget") }}</option>
151+
<span class="mapping-arrow" aria-hidden="true">&rarr;</span>
152+
<select
153+
v-model="targetModel"
154+
class="select mapping-input-inline"
155+
:disabled="targetModelSelectDisabled"
156+
>
157+
<option value="">{{ targetModelOptionText }}</option>
134158
<option v-for="model in availableModels" :key="model.id" :value="model.id">
135159
{{ model.id }}
136160
</option>
@@ -186,7 +210,11 @@ function openMappingForm(): void {
186210
<button type="button" class="btn btn-sm" @click="copyMapping(from, to)">
187211
{{ t("mappings.copy") }}
188212
</button>
189-
<button type="button" class="btn btn-danger btn-sm" @click="removeMapping(from)">
213+
<button
214+
type="button"
215+
class="btn btn-danger btn-sm"
216+
@click="removeMapping(from)"
217+
>
190218
{{ t("common.delete") }}
191219
</button>
192220
</div>

0 commit comments

Comments
 (0)