From 4e7d55ff37363c56076106cb90d455665af95818 Mon Sep 17 00:00:00 2001 From: viettranx Date: Thu, 5 Mar 2026 19:43:18 +0700 Subject: [PATCH] feat: Add user and chat ID filtering to the events page and store event user/chat IDs. --- ui/web/src/pages/events/events-page.tsx | 68 +++++++++++++++++++++-- ui/web/src/stores/use-team-event-store.ts | 20 +++++++ 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/ui/web/src/pages/events/events-page.tsx b/ui/web/src/pages/events/events-page.tsx index fc4b3c15..8dc4b67c 100644 --- a/ui/web/src/pages/events/events-page.tsx +++ b/ui/web/src/pages/events/events-page.tsx @@ -28,6 +28,8 @@ export function EventsPage() { const [categoryFilter, setCategoryFilter] = useState("all"); const [teamFilter, setTeamFilter] = useState("all"); + const [userFilter, setUserFilter] = useState("all"); + const [chatFilter, setChatFilter] = useState("all"); const [isAtBottom, setIsAtBottom] = useState(true); const feedRef = useRef(null); @@ -65,17 +67,47 @@ export function EventsPage() { return Array.from(ids); }, [allEvents]); + // Unique user IDs from events for filter dropdown + const uniqueUsers = useMemo(() => { + const ids = new Set(); + for (const e of allEvents) { + if (e.userId) ids.add(e.userId); + } + return Array.from(ids).sort(); + }, [allEvents]); + + // Unique chat IDs from events for filter dropdown + const uniqueChats = useMemo(() => { + const ids = new Set(); + for (const e of allEvents) { + if (e.chatId) ids.add(e.chatId); + } + return Array.from(ids).sort(); + }, [allEvents]); + // Apply team filter const teamFilteredEvents = useMemo(() => { if (teamFilter === "all") return allEvents; return allEvents.filter((e) => e.teamId === teamFilter); }, [allEvents, teamFilter]); + // Apply user filter + const userFilteredEvents = useMemo(() => { + if (userFilter === "all") return teamFilteredEvents; + return teamFilteredEvents.filter((e) => e.userId === userFilter); + }, [teamFilteredEvents, userFilter]); + + // Apply chat filter + const chatFilteredEvents = useMemo(() => { + if (chatFilter === "all") return userFilteredEvents; + return userFilteredEvents.filter((e) => e.chatId === chatFilter); + }, [userFilteredEvents, chatFilter]); + // Apply category filter const filteredEvents = useMemo(() => { - if (categoryFilter === "all") return teamFilteredEvents; + if (categoryFilter === "all") return chatFilteredEvents; if (categoryFilter === "team.crud") { - return teamFilteredEvents.filter( + return chatFilteredEvents.filter( (e) => e.event === "team.created" || e.event === "team.updated" || @@ -83,8 +115,8 @@ export function EventsPage() { e.event.startsWith("team.member."), ); } - return teamFilteredEvents.filter((e) => e.event.startsWith(categoryFilter)); - }, [teamFilteredEvents, categoryFilter]); + return userFilteredEvents.filter((e) => e.event.startsWith(categoryFilter)); + }, [chatFilteredEvents, categoryFilter]); // Auto-scroll to bottom when new events arrive useEffect(() => { @@ -175,6 +207,34 @@ export function EventsPage() { ))} )} + + {/* User filter */} + + + {/* Chat filter */} + {/* Event feed */} diff --git a/ui/web/src/stores/use-team-event-store.ts b/ui/web/src/stores/use-team-event-store.ts index 3e37beab..7514c76f 100644 --- a/ui/web/src/stores/use-team-event-store.ts +++ b/ui/web/src/stores/use-team-event-store.ts @@ -11,6 +11,8 @@ export interface TeamEventEntry { payload: unknown; timestamp: number; teamId: string | null; + userId: string | null; + chatId: string | null; } interface TeamEventState { @@ -34,6 +36,22 @@ function extractTeamId(payload: unknown): string | null { return null; } +function extractUserId(payload: unknown): string | null { + if (!payload || typeof payload !== "object") return null; + const p = payload as Record; + if (typeof p.user_id === "string" && p.user_id) return p.user_id; + if (typeof p.userId === "string" && p.userId) return p.userId; + return null; +} + +function extractChatId(payload: unknown): string | null { + if (!payload || typeof payload !== "object") return null; + const p = payload as Record; + if (typeof p.chat_id === "string" && p.chat_id) return p.chat_id; + if (typeof p.chatId === "string" && p.chatId) return p.chatId; + return null; +} + function loadPersistedEvents(): TeamEventEntry[] { try { const raw = localStorage.getItem(PERSIST_KEY); @@ -71,6 +89,8 @@ export const useTeamEventStore = create((set) => ({ payload, timestamp: Date.now(), teamId: extractTeamId(payload), + userId: extractUserId(payload), + chatId: extractChatId(payload), }; const next = [...s.events, entry]; const trimmed = next.length > MAX_EVENTS ? next.slice(next.length - MAX_EVENTS) : next;