Skip to content

Commit

Permalink
fix: change history display from chatbot id to name, change history f…
Browse files Browse the repository at this point in the history
…ilter to dropdown, match styles on dropdownmenus, add localstorage memory for dropdown selections (bit buggy, uses id sometimes)
  • Loading branch information
bboinay committed Aug 15, 2024
1 parent 91803e1 commit f71149c
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 164 deletions.
351 changes: 198 additions & 153 deletions ui/src/components/pages/PageHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import * as React from "react";
import { useEffect, useState } from "react";
import {
ColumnDef,
ColumnFiltersState,
Expand All @@ -26,7 +26,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
// import { Input } from "@/components/ui/input";
import {
Table,
TableBody,
Expand All @@ -38,161 +38,198 @@ import {

import { useQuery } from "@tanstack/react-query";

import { chatbotService } from "../../services/chatbot-service";
import { historyService } from "../../services/history-service";

export const columns: ColumnDef<any>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "chatbot_id",
header: "Chatbot ID",
cell: ({ row }) => (
<div className="capitalize">{row.getValue("chatbot_id")}</div>
),
},
{
accessorKey: "time",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Time
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => (
<div className="lowercase">
{new Date(row.getValue("time")).toLocaleString()}
</div>
),
},
{
accessorKey: "input",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Input
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => <div className="lowercase">{row.getValue("input")}</div>,
},
{
accessorKey: "output",
header: "Output",
cell: ({ row }) => {
const output = row.getValue("output") as string;
return (
<div className="text-right font-medium">
{output.slice(0, 150) + (output.length > 150 ? "..." : "")}
</div>
);
},
},
{
accessorKey: "context",
header: () => <div className="text-right">Context</div>,
cell: ({ row }) => {
const context = row.getValue("context") as string;
return (
<div className="text-right font-medium">
{context.slice(0, 150) + (context.length > 150 ? "..." : "")}
</div>
);
},
},
{
accessorKey: "faithfulness",
header: () => <div className="text-right">Faithfulness</div>,
cell: ({ row }) => {
const score = parseFloat(row.getValue("faithfulness"));

return <div className="text-right font-medium">{score.toFixed(2)}</div>;
},
},
{
accessorKey: "answer_relevancy",
header: () => <div className="text-right">Answer Relevancy</div>,
cell: ({ row }) => {
const score = parseFloat(row.getValue("answer_relevancy"));
return <div className="text-right font-medium">{score.toFixed(2)}</div>;
},
},
{
id: "actions",
enableHiding: false,
cell: ({ row }) => {
const data = row.original;
console.log("ColumnDef rerendering...?");
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(data[0])}
>
Copy ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>View Details</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];


export function PageHistory() {
const { data: chatHistory, isLoading } = useQuery({
queryKey: ["chatHistory"],
queryFn: () => historyService.fetchChatbotHistory(),
// staleTime: 5 * 60 * 1000, // 5 minutes
// refetchOnWindowFocus: false, // Disable refetch on window focus
});

const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
const [chatbotNames, setChatbotNames] = useState<{ [key: string]: string }>({});

const [sorting, setSorting] = useState<SortingState>([
{ id: "time", desc: true }
]);

const [selectedChatbot, setSelectedChatbot] = useState(() => {
return localStorage.getItem('selectedChatbot') || '';
});

const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
[]
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
useState<VisibilityState>({});
const [rowSelection, setRowSelection] = useState({});

useEffect(() => {
if (selectedChatbot) {
localStorage.setItem('selectedChatbot', selectedChatbot);
}
}, [selectedChatbot]);

// console.log('DATA IS', data)
console.log("CHATHISTORY IS", chatHistory);
const handleChatbotSelection = (id: string) => {
setSelectedChatbot(id);
localStorage.setItem("selectedChatbot", id);
table.getColumn("chatbot_id")?.setFilterValue(id);
};

useEffect(() => {
const fetchChatbotNames = async () => {
if (chatHistory) {
const chatbots = await chatbotService.fetchChatbots();
const namesMap = chatbots.reduce((acc: any, chatbot: any) => {
acc[chatbot.id] = chatbot.name;
return acc;
}, {});
setChatbotNames(namesMap);
}
};

fetchChatbotNames();
}, [chatHistory]);

const columns: ColumnDef<any>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "chatbot_id",
header: "Chatbot",
cell: ({ row }) => {
const id = row.getValue("chatbot_id") as string;
const name = chatbotNames[id] || "Loading...";
return <div>{name}</div>;
},
},
{
accessorKey: "time",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Time
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => (
<div className="lowercase">
{new Date(row.getValue("time")).toLocaleString()}
</div>
),
},
{
accessorKey: "input",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Input
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => <div className="lowercase">{row.getValue("input")}</div>,
},
{
accessorKey: "output",
header: "Output",
cell: ({ row }) => {
const output = row.getValue("output") as string;
return (
<div className="text-right font-medium">
{output.slice(0, 150) + (output.length > 150 ? "..." : "")}
</div>
);
},
},
{
accessorKey: "context",
header: () => <div className="text-right">Context</div>,
cell: ({ row }) => {
const context = row.getValue("context") as string;
return (
<div className="text-right font-medium">
{context.slice(0, 150) + (context.length > 150 ? "..." : "")}
</div>
);
},
},
{
accessorKey: "faithfulness",
header: () => <div className="text-right">Faithfulness</div>,
cell: ({ row }) => {
const score = parseFloat(row.getValue("faithfulness"));

return <div className="text-right font-medium">{score.toFixed(2)}</div>;
},
},
{
accessorKey: "answer_relevancy",
header: () => <div className="text-right">Answer Relevancy</div>,
cell: ({ row }) => {
const score = parseFloat(row.getValue("answer_relevancy"));
return <div className="text-right font-medium">{score.toFixed(2)}</div>;
},
},
{
id: "actions",
enableHiding: false,
cell: ({ row }) => {
const data = row.original;
console.log("ColumnDef rerendering...?");
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(data[0])}
>
Copy ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>View Details</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
},
];

const table = useReactTable({
data: chatHistory || [],
Expand All @@ -216,19 +253,27 @@ export function PageHistory() {
if (isLoading) {
return <div>Loading...</div>;
}

return (
<div className="w-full">
<div className="flex items-center py-4">
<Input
placeholder="Filter chatbot id..."
value={
(table.getColumn("chatbot_id")?.getFilterValue() as string) ?? ""
}
onChange={(event) =>
table.getColumn("chatbot_id")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
{selectedChatbot ? chatbotNames[selectedChatbot] : "Select Chatbot"} <ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
{Object.entries(chatbotNames).map(([id, name]) => (
<DropdownMenuItem
key={id}
onClick={() => handleChatbotSelection(id)}
>
{name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Expand Down
Loading

0 comments on commit f71149c

Please sign in to comment.