Skip to content

Commit e9ac8d5

Browse files
author
linyuan.yang
committed
session profile
1 parent 0c05d1d commit e9ac8d5

3 files changed

Lines changed: 69 additions & 2 deletions

File tree

packages/admin/src/i18n/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ export default {
209209
name_placeholder: 'a recognizable name',
210210
used_by_n: 'used by {n} session(s)',
211211
used_by_none: 'not in use',
212+
sessions_modal_title: 'Sessions using profile "{name}"',
212213
},
213214
settings: {
214215
http_port: 'HTTP Port',

packages/admin/src/i18n/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ export default {
209209
name_placeholder: '便于识别的名字',
210210
used_by_n: '{n} 个会话使用',
211211
used_by_none: '暂未被使用',
212+
sessions_modal_title: '使用 Profile "{name}" 的会话',
212213
},
213214
settings: {
214215
http_port: 'HTTP 端口',

packages/admin/src/views/chat/SessionProfilesView.vue

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ref, computed, onMounted } from 'vue'
33
import { useI18n } from 'vue-i18n'
44
import { apiFetch } from '@/shared/api'
55
import { store } from '@/shared/store'
6-
import { useToast, useConfirm, SButton, SInput, SFormItem, SPageToolbar, SPageContent, STable } from 'sbot-ui'
6+
import { useToast, useConfirm, SButton, SInput, SFormItem, SPageToolbar, SPageContent, STable, SModal } from 'sbot-ui'
77
import type { STableColumn } from 'sbot-ui'
88
import { ApprovalTimeoutValue } from 'sbot.commons'
99
import { PathPickerModal, WebSocketTransport } from '@sbot/chat-ui'
@@ -56,6 +56,39 @@ const pathPicker = ref<InstanceType<typeof PathPickerModal>>()
5656
5757
const profiles = ref<ProfileRow[]>([])
5858
59+
interface SessionLite {
60+
id: number
61+
channelId: string
62+
sessionId: string
63+
sessionName: string
64+
autoSessionName: string
65+
}
66+
const sessionsModalVisible = ref(false)
67+
const sessionsModalProfile = ref<ProfileRow | null>(null)
68+
const sessionsModalRows = ref<SessionLite[]>([])
69+
const sessionsModalLoading = ref(false)
70+
71+
const channelLabel = (channelId: string): string => {
72+
const c = (store.settings.channels as Record<string, any> | undefined)?.[channelId]
73+
return c?.name || channelId
74+
}
75+
76+
async function openSessions(p: ProfileRow) {
77+
if (!(p.sessionCount ?? 0)) return
78+
sessionsModalProfile.value = p
79+
sessionsModalRows.value = []
80+
sessionsModalVisible.value = true
81+
sessionsModalLoading.value = true
82+
try {
83+
const res = await apiFetch(`/api/session-profiles/${p.id}`)
84+
sessionsModalRows.value = ((res.data as any)?.sessions || []) as SessionLite[]
85+
} catch (e: any) {
86+
show(e.message, 'error')
87+
} finally {
88+
sessionsModalLoading.value = false
89+
}
90+
}
91+
5992
const agentOptions = computed(() => Object.entries(store.settings.agents || {}).map(([id, a]) => ({ id, label: (a as any).name || id, type: (a as any).type || '' })))
6093
const saverOptions = computed(() => Object.entries(store.settings.savers || {}).map(([id, s]) => ({ id, label: (s as any).name || id })))
6194
const memoryOptions = computed(() => Object.entries(store.settings.memories || {}).map(([id, m]: [string, any]) => ({ id, label: m.name || id })))
@@ -231,7 +264,9 @@ async function remove(p: ProfileRow) {
231264
<span v-else style="color: var(--sui-fg-disabled)">—</span>
232265
</template>
233266
<template #sessionCount="{ row }">
234-
<span v-if="row.sessionCount">{{ t('session_profiles.used_by_n', { n: row.sessionCount }) }}</span>
267+
<a v-if="row.sessionCount" href="#" class="session-count-link" @click.prevent="openSessions(row)">
268+
{{ t('session_profiles.used_by_n', { n: row.sessionCount }) }}
269+
</a>
235270
<span v-else style="color: var(--sui-fg-disabled)">{{ t('session_profiles.used_by_none') }}</span>
236271
</template>
237272
<template #ops="{ row }">
@@ -279,6 +314,22 @@ async function remove(p: ProfileRow) {
279314
@confirm="p => { form.overrides.workPath = p }"
280315
@error="msg => show(msg, 'error')"
281316
/>
317+
318+
<SModal v-model:visible="sessionsModalVisible" width="md">
319+
<template #header>
320+
<h3 class="s-modal-title">
321+
{{ t('session_profiles.sessions_modal_title', { name: sessionsModalProfile?.name || '' }) }}
322+
</h3>
323+
</template>
324+
<div v-if="sessionsModalLoading" class="modal-loading">{{ t('common.loading') }}</div>
325+
<div v-else-if="sessionsModalRows.length === 0" class="modal-empty">{{ t('session_profiles.used_by_none') }}</div>
326+
<ul v-else class="sess-list">
327+
<li v-for="s in sessionsModalRows" :key="s.id" class="sess-row">
328+
<span class="sess-channel">{{ channelLabel(s.channelId) }}</span>
329+
<span class="sess-name">{{ s.sessionName || s.autoSessionName || s.sessionId }}</span>
330+
</li>
331+
</ul>
332+
</SModal>
282333
</div>
283334
</template>
284335

@@ -313,4 +364,18 @@ async function remove(p: ProfileRow) {
313364
.drawer-fade-enter-from, .drawer-fade-leave-to { opacity: 0; }
314365
.drawer-slide-enter-active, .drawer-slide-leave-active { transition: transform 0.25s ease; }
315366
.drawer-slide-enter-from, .drawer-slide-leave-to { transform: translateX(100%); }
367+
368+
.session-count-link { color: var(--sui-fg-link, var(--sui-accent)); text-decoration: none; cursor: pointer; }
369+
.session-count-link:hover { text-decoration: underline; }
370+
371+
.modal-loading, .modal-empty { padding: var(--sui-sp-6); text-align: center; color: var(--sui-fg-muted); }
372+
.sess-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sui-sp-2); }
373+
.sess-row {
374+
display: flex; align-items: center; gap: var(--sui-sp-3);
375+
padding: var(--sui-sp-3) var(--sui-sp-4);
376+
border: 1px solid var(--sui-border);
377+
border-radius: var(--sui-radius);
378+
}
379+
.sess-channel { color: var(--sui-fg-muted); font-size: var(--sui-fs-sm); min-width: 120px; }
380+
.sess-name { flex: 1; }
316381
</style>

0 commit comments

Comments
 (0)