diff --git a/client/src/plus/ai-help/history.tsx b/client/src/plus/ai-help/history.tsx new file mode 100644 index 000000000000..42e560356089 --- /dev/null +++ b/client/src/plus/ai-help/history.tsx @@ -0,0 +1,157 @@ +import useSWR, { KeyedMutator } from "swr"; +import { Button } from "../../ui/atoms/button"; +import { useEffect } from "react"; + +function monthYearLabel(date: Date): string { + const formattedDate = date.toLocaleString(undefined, { + month: "short", + year: "numeric", + }); + return formattedDate; +} + +interface HistoryEntry { + chat_id: string; + label: string; + last: string; +} +interface HistoryEntries { + label: string; + entries: HistoryEntry[]; +} + +function groupHistory(history) { + const now = new Date(); + const today = new Date(now.toDateString()); + const yesterday = new Date( + structuredClone(today).setDate(today.getDate() - 1) + ); + const last30Days = new Date( + structuredClone(today).setDate(today.getDate() - 30) + ); + const groups = [ + { label: "Last 30 Days", d: last30Days }, + { label: "Yesterday", d: yesterday }, + { label: "Today", d: today }, + ]; + const grouped: HistoryEntries[] = []; + + let { label = "unknown", d } = groups.pop() || {}; + let current: HistoryEntries = { label, entries: [] }; + for (const entry of history) { + let last = new Date(entry.last); + while (!d || last < d) { + if (!d) { + label = monthYearLabel(last); + break; + } else if (last < d) { + ({ label = "unknown", d } = groups.pop() || {}); + continue; + } + break; + } + if (current.label !== label) { + grouped.push(current); + current = { label, entries: [entry] }; + } else { + current.entries.push(entry); + } + } + + if (current.entries.length) { + grouped.push(current); + } + return grouped; +} + +function AIHelpHistorySubList({ + currentChatId, + entries, + mutate, +}: { + currentChatId?: string; + entries: HistoryEntries; + mutate: KeyedMutator; +}) { + return ( + <> + +
    + {entries.entries.map(({ chat_id, last, label }, index) => { + return ( +
  1. + + {label} + + {chat_id === currentChatId && ( +
  2. + ); + })} +
+ + ); +} + +export function AIHelpHistory({ + currentChatId, + lastUpdate, +}: { + currentChatId?: string; + lastUpdate: Date; +}) { + const { data, mutate } = useSWR( + `/api/v1/plus/ai/help/history/list`, + async (url) => { + const res = await (await fetch(url)).json(); + return Array.isArray(res) ? res : []; + }, + { + fallbackData: [], + } + ); + + useEffect(() => { + mutate(); + }, [lastUpdate, mutate]); + + return ( + + ); +} diff --git a/client/src/plus/ai-help/index.scss b/client/src/plus/ai-help/index.scss index 359e721f8233..47f1bf3fded6 100644 --- a/client/src/plus/ai-help/index.scss +++ b/client/src/plus/ai-help/index.scss @@ -355,23 +355,25 @@ width: 100%; &.role-user { - white-space: pre-wrap; + align-items: center; display: flex; flex-direction: row; - align-items: center; justify-content: space-between; + white-space: pre-wrap; .ai-help-message-nav { display: flex; justify-content: space-between; + > span { - width: 4rem; text-align: center; + width: 4rem; } } + .ai-help-user-message { - width: 100%; padding: 0 1rem; + width: 100%; } } diff --git a/client/src/plus/ai-help/index.tsx b/client/src/plus/ai-help/index.tsx index 16b044f5ff57..8aca1761eca8 100644 --- a/client/src/plus/ai-help/index.tsx +++ b/client/src/plus/ai-help/index.tsx @@ -42,7 +42,6 @@ import ExpandingTextarea from "../../ui/atoms/form/expanding-textarea"; import React from "react"; import { SESSION_KEY } from "../../playground/utils"; import { PlayQueue } from "../../playground/queue"; -import useSWR, { KeyedMutator } from "swr"; import { AIHelpHistory } from "./history"; type Category = "apis" | "css" | "html" | "http" | "js" | "learn"; diff --git a/client/src/plus/ai-help/use-ai.ts b/client/src/plus/ai-help/use-ai.ts index a65b42371b80..d233d0db62df 100644 --- a/client/src/plus/ai-help/use-ai.ts +++ b/client/src/plus/ai-help/use-ai.ts @@ -575,7 +575,6 @@ export function useAiChat({ content: messageTemplate(query), }); - return; const eventSource = new SSE(`/api/v1/plus/ai/help`, { headers: { "Content-Type": "application/json",