mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
feat: add support for username (#2047)
* feat: add support for username * fix: lint * fix: migerations * fix: change to version 21 * fix: remove duplicate dbm021 migration and rename dbm022 * feat: add user_id and user_name display with copy functionality in BotSessionMonitor --------- Co-authored-by: wangcham <wangcham@gmail.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Copy, Check } from 'lucide-react';
|
||||
import {
|
||||
MessageChainComponent,
|
||||
Plain,
|
||||
@@ -27,6 +28,7 @@ interface SessionInfo {
|
||||
is_active: boolean;
|
||||
platform?: string | null;
|
||||
user_id?: string | null;
|
||||
user_name?: string | null;
|
||||
}
|
||||
|
||||
interface SessionMessage {
|
||||
@@ -60,8 +62,29 @@ export default function BotSessionMonitor({ botId }: BotSessionMonitorProps) {
|
||||
const [messages, setMessages] = useState<SessionMessage[]>([]);
|
||||
const [loadingSessions, setLoadingSessions] = useState(false);
|
||||
const [loadingMessages, setLoadingMessages] = useState(false);
|
||||
const [copiedUserId, setCopiedUserId] = useState(false);
|
||||
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const parseSessionType = (sessionId: string): string | null => {
|
||||
const idx = sessionId.indexOf('_');
|
||||
if (idx === -1) return null;
|
||||
const type = sessionId.slice(0, idx);
|
||||
if (type === 'person' || type === 'group') return type;
|
||||
return null;
|
||||
};
|
||||
|
||||
const abbreviateId = (id: string): string => {
|
||||
if (id.length <= 10) return id;
|
||||
return `${id.slice(0, 4)}..${id.slice(-4)}`;
|
||||
};
|
||||
|
||||
const copyUserId = (userId: string) => {
|
||||
navigator.clipboard.writeText(userId).then(() => {
|
||||
setCopiedUserId(true);
|
||||
setTimeout(() => setCopiedUserId(false), 2000);
|
||||
});
|
||||
};
|
||||
|
||||
const loadSessions = useCallback(async () => {
|
||||
setLoadingSessions(true);
|
||||
try {
|
||||
@@ -338,24 +361,36 @@ export default function BotSessionMonitor({ botId }: BotSessionMonitorProps) {
|
||||
>
|
||||
<div className="flex items-center justify-between mb-0.5">
|
||||
<span className="text-sm font-medium truncate mr-2">
|
||||
{session.user_id || session.session_id.slice(0, 12)}
|
||||
{session.user_name ||
|
||||
session.user_id ||
|
||||
session.session_id.slice(0, 12)}
|
||||
</span>
|
||||
<span className="text-[11px] text-muted-foreground tabular-nums flex-shrink-0">
|
||||
{formatRelativeTime(session.last_activity)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
{parseSessionType(session.session_id) && (
|
||||
<span className="px-1 py-0.5 rounded bg-muted text-[10px]">
|
||||
{parseSessionType(session.session_id)}
|
||||
</span>
|
||||
)}
|
||||
{session.platform && (
|
||||
<span className="px-1 py-0.5 rounded bg-muted text-[10px]">
|
||||
{session.platform}
|
||||
</span>
|
||||
)}
|
||||
{session.user_id && (
|
||||
<span className="truncate text-[10px]">
|
||||
{abbreviateId(session.user_id)}
|
||||
</span>
|
||||
)}
|
||||
{session.is_active && (
|
||||
<span className="flex items-center gap-0.5 text-green-600 dark:text-green-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-green-500 inline-block" />
|
||||
</span>
|
||||
)}
|
||||
<span>{session.pipeline_name}</span>
|
||||
<span className="truncate">{session.pipeline_name}</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
@@ -377,15 +412,42 @@ export default function BotSessionMonitor({ botId }: BotSessionMonitorProps) {
|
||||
<div className="px-6 py-3 border-b shrink-0 flex items-center justify-between">
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-medium truncate">
|
||||
{selectedSession?.user_id || selectedSessionId.slice(0, 20)}
|
||||
{selectedSession?.user_name ||
|
||||
selectedSession?.user_id ||
|
||||
selectedSessionId.slice(0, 20)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
{parseSessionType(selectedSessionId) && (
|
||||
<span>{parseSessionType(selectedSessionId)}</span>
|
||||
)}
|
||||
{selectedSession?.platform && (
|
||||
<span>{selectedSession.platform}</span>
|
||||
<>
|
||||
{parseSessionType(selectedSessionId) && <span>·</span>}
|
||||
<span>{selectedSession.platform}</span>
|
||||
</>
|
||||
)}
|
||||
{selectedSession?.user_id && (
|
||||
<>
|
||||
<span>·</span>
|
||||
<span className="font-mono">
|
||||
{selectedSession.user_id}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => copyUserId(selectedSession.user_id!)}
|
||||
className="inline-flex items-center text-muted-foreground hover:text-foreground transition-colors"
|
||||
title={t('common.copy')}
|
||||
>
|
||||
{copiedUserId ? (
|
||||
<Check className="w-3 h-3 text-green-600" />
|
||||
) : (
|
||||
<Copy className="w-3 h-3" />
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{selectedSession?.pipeline_name && (
|
||||
<>
|
||||
{selectedSession?.platform && <span>·</span>}
|
||||
<span>·</span>
|
||||
<span>{selectedSession.pipeline_name}</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -356,6 +356,7 @@ export class BackendClient extends BaseHttpClient {
|
||||
is_active: boolean;
|
||||
platform: string | null;
|
||||
user_id: string | null;
|
||||
user_name: string | null;
|
||||
}>;
|
||||
total: number;
|
||||
}> {
|
||||
@@ -384,6 +385,7 @@ export class BackendClient extends BaseHttpClient {
|
||||
level: string;
|
||||
platform: string | null;
|
||||
user_id: string | null;
|
||||
user_name: string | null;
|
||||
runner_name: string | null;
|
||||
variables: string | null;
|
||||
role: string | null;
|
||||
|
||||
Reference in New Issue
Block a user