diff --git a/apps/admin/src/App.tsx b/apps/admin/src/App.tsx index 208dfc7..457d839 100644 --- a/apps/admin/src/App.tsx +++ b/apps/admin/src/App.tsx @@ -69,7 +69,10 @@ function App() { mainContent={ <> {selectedChannel && ( - + )} } diff --git a/apps/admin/src/components/ChatMessageItemView.tsx b/apps/admin/src/components/ChatMessageItemView.tsx index 27cd146..0bf216b 100644 --- a/apps/admin/src/components/ChatMessageItemView.tsx +++ b/apps/admin/src/components/ChatMessageItemView.tsx @@ -1,16 +1,27 @@ -import React from "react"; -import { Button } from "@mui/material"; -import { DocsUserQuery, Message } from "../models/Models"; +import React, { useEffect } from "react"; +import { Button, Box } from "@mui/material"; +import { DocsUserQuery, Message, User } from "../models/Models"; +import { useUsers } from "../hooks/useUsers"; interface ChatMessageItemViewProps { message: Message; + selectedTeam: string; onClick: () => void; } const ChatMessageItemView: React.FC = ({ message, + selectedTeam, onClick, }) => { + const { data: users, error, isLoading } = useUsers({ selectedTeam }); + + useEffect(() => { + console.log(`Selected team: ${selectedTeam}`); + console.log(`message.user_id: ${message.user_id}`); + console.log(`Users.length: ${users?.length}`); + }, [users, selectedTeam]); + try { return ( ); - } catch (error) { + } catch (error: any) { console.error( "Error displaying ChatMessageItemView:", error.message, diff --git a/apps/admin/src/components/ChatMessageView.tsx b/apps/admin/src/components/ChatMessageView.tsx index bded89c..6039130 100644 --- a/apps/admin/src/components/ChatMessageView.tsx +++ b/apps/admin/src/components/ChatMessageView.tsx @@ -7,9 +7,13 @@ import { useMessages } from "../hooks/useMessages"; interface Props { selectedChannel: string; + selectedTeam: string; } -const ChatMessageView: React.FC = ({ selectedChannel }) => { +const ChatMessageView: React.FC = ({ + selectedChannel, + selectedTeam, +}) => { const theme = useTheme(); const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm")); const { messages, error, isLoading, setCurrentMessageId, currentMessageId } = @@ -38,6 +42,7 @@ const ChatMessageView: React.FC = ({ selectedChannel }) => { {message.content_type === "docs_user_query" && ( { setCurrentMessageId({ ts_date: message.ts_date, diff --git a/apps/admin/src/components/ThreadViewPane.tsx b/apps/admin/src/components/ThreadViewPane.tsx index 8aac1ce..648c35b 100644 --- a/apps/admin/src/components/ThreadViewPane.tsx +++ b/apps/admin/src/components/ThreadViewPane.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; import { Link, List, ListItem } from "@mui/material"; -import { Props } from "../models/Models"; +import { Props, Message } from "../models/Models"; import ThreadStart from "./ThreadStart"; import { useThreadReplies } from "../hooks/useThreadReplies"; import { RagPipelineResult } from "@digdir/assistants"; @@ -16,24 +16,32 @@ const ThreadViewPane: React.FC = ({ isLoading, } = useThreadReplies({ channelId, thread_ts_date, thread_ts_time }); - const botReplyDetails = (message) => { + const botReplyDetails = (message: Message) => { if (message?.content_type != "docs_bot_reply") { return ""; } const details = message?.content as RagPipelineResult; return ( <> - +
Phrases generated for retrieval:
-
    {details?.search_queries.map((query) =>
  • {query}
  • )}
+
    + {details?.search_queries.map((query: any) =>
  • {query}
  • )} +
- +
Sources
    - {details?.source_urls.map((url) => ( -
  • + {details?.source_urls.map((url: string) => ( +
  • {url.replace("https://docs.altinn.studio/", "")} @@ -41,28 +49,31 @@ const ThreadViewPane: React.FC = ({ ))}
- +
Processing times

Total: {message?.durations?.total.toFixed(1)}

    -
  • Analyze: {message?.durations?.analyze.toFixed(1)}
  • -
  • +
  • Analyze: {message?.durations?.analyze.toFixed(1)}
  • +
  • Generate searches:{" "} {message?.durations?.generate_searches.toFixed(1)}
  • -
  • +
  • Phrase similarity:{" "} {message?.durations?.phrase_similarity_search.toFixed(1)}
  • -
  • +
  • Execute searches:{" "} {message?.durations?.execute_searches.toFixed(1)}
  • -
  • Re-rank: {message?.durations?.colbert_rerank.toFixed(1)}
  • -
  • Generate answer: {message?.durations?.rag_query.toFixed(1)}
  • -
  • Translate: {message?.durations?.translation.toFixed(1)}
  • +
  • Re-rank: {message?.durations?.colbert_rerank.toFixed(1)}
  • +
  • Generate answer: {message?.durations?.rag_query.toFixed(1)}
  • +
  • Translate: {message?.durations?.translation.toFixed(1)}
diff --git a/apps/admin/src/components/TopBar.tsx b/apps/admin/src/components/TopBar.tsx index daf1f41..79f819a 100644 --- a/apps/admin/src/components/TopBar.tsx +++ b/apps/admin/src/components/TopBar.tsx @@ -12,6 +12,7 @@ const StyledAppBar = styled(AppBar)(({ theme }) => ({ interface TopBarProps { selectedTeam: string; onTeamChange: (teamId: string) => void; + selectedChannel: string; onChannelSelect: (channelId: string) => void; } @@ -32,7 +33,7 @@ const TopBar: React.FC = ({ return ( - + Digdir Assistants diff --git a/apps/admin/src/hooks/useUsers.ts b/apps/admin/src/hooks/useUsers.ts new file mode 100644 index 0000000..b57dcb8 --- /dev/null +++ b/apps/admin/src/hooks/useUsers.ts @@ -0,0 +1,108 @@ +import { useEffect } from "react"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import supabase from "../supabase/SupabaseClient"; +import { User } from "../models/Models"; + +interface UseUsersProps { + selectedTeam: string; +} + +export const useUsers = ({ selectedTeam }: UseUsersProps) => { + const queryClient = useQueryClient(); + + // const getUserById = (userId: string) => { + // const { data: users } = useQuery({ + // queryKey: ["users", selectedTeam], + // queryFn: async () => { + // try { + // if (!selectedTeam) { + // return []; + // } + // const { data, error } = await supabase + // .from("user") + // .select("*") + // .eq("team_id", selectedTeam); + // if (error) { + // console.error("Error fetching users:", error.message, error.details); + // throw new Error(error.message); + // } + // return data; + // } catch (error) { + // console.error( + // "Error in useUsers query:", + // error instanceof Error ? error.message : "An unknown error occurred", + // ); + // throw error; + // } + // }, + // enabled: !!selectedTeam, + // }); + + // return users?.find((user) => user.user_id === userId); + // }; + + useEffect(() => { + if (!selectedTeam) return; + + const subscription = supabase + .channel(`public:slack_user:team_id=eq.${selectedTeam}`) + .on( + "postgres_changes", + { event: "*", schema: "public", table: "slack_user" }, + (payload) => { + console.log( + `Realtime update received for users in team ${selectedTeam}:`, + payload.new, + ); + queryClient.invalidateQueries(["users", selectedTeam]); + }, + ) + .subscribe( + (status) => { + console.log(`Subscription status: ${status}`); + }, + (error) => { + console.error( + "Error subscribing to user updates:", + error.message, + error.stack, + ); + }, + ); + + return () => { + console.log("Unsubscribing from user updates."); + supabase.removeChannel(subscription); + }; + }, [selectedTeam, queryClient]); + + return useQuery({ + queryKey: ["users", selectedTeam], + enabled: !!selectedTeam, + queryFn: async (): Promise => { + try { + if (!selectedTeam) { + console.log("No team selected, won't fetch users"); + return []; + } + console.log(`Fetching users for team: ${selectedTeam}`); + const { data, error } = await supabase + .from("slack_user") + .select("*") + .eq("team_id", selectedTeam); + if (error) { + console.error("Error fetching users:", error.message, error.details); + throw new Error(error.message); + } + console.log("Users fetched successfully with React Query.", data); + return data; + } catch (error) { + console.error( + "Error in useUsers query:", + error instanceof Error ? error.message : "An unknown error occurred", + ); + throw error; + } + }, + }); +}; diff --git a/apps/admin/src/models/Models.ts b/apps/admin/src/models/Models.ts index 850abc9..5d82db7 100644 --- a/apps/admin/src/models/Models.ts +++ b/apps/admin/src/models/Models.ts @@ -1,16 +1,26 @@ import { z } from "zod"; -export interface Message { - ts: string; - ts_date: number; - ts_time: number; - thread_ts_date: number; - thread_ts_time: number; - content: object | Array; - content_type: string; - created_at: string; - user_name: string; -} +const UserSchema = z.object({ + user_id: z.string(), + name: z.string(), + type: z.string(), + team_id: z.string(), +}); +export type User = z.infer; + +const MessageSchema = z.object({ + ts: z.string(), + ts_date: z.number(), + ts_time: z.number(), + thread_ts_date: z.number(), + thread_ts_time: z.number(), + content: z.union([z.object({}), z.array(z.object({}))]), + content_type: z.string(), + created_at: z.string(), + user_id: z.string(), + durations: z.object({}).optional(), +}); +export type Message = z.infer; const DocsUserQuerySchema = z.object({ bot_name: z.string(),