diff --git a/package-lock.json b/package-lock.json index 1438f19..afe3229 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "next-auth": "^4.24.6", "next-themes": "^0.3.0", "openai": "^4.24.7", - "orchid-orm": "^1.17.18", + "orchid-orm": "^1.36.1", "orchid-orm-schema-to-zod": "^0.4.21", "parse-diff": "^0.11.1", "patch-package": "^8.0.0", @@ -14977,17 +14977,16 @@ "integrity": "sha512-0drP24JKHmIm2LHaQhMvdbFGkggZGRUU9iJur/JmUqZfgdTp/nqcu8SJmOKaj+IaCb9aKUaZl3kaPOi3/F75oA==" }, "node_modules/orchid-orm": { - "version": "1.17.18", - "resolved": "https://registry.npmjs.org/orchid-orm/-/orchid-orm-1.17.18.tgz", - "integrity": "sha512-qGw8xWsm1uN2HJXCp/Df3wnY5AmgNpoMKVClzkMym4JLtli05736V5Uu+FJShS93w/17baHf/XaJFrOKBpX90Q==", + "version": "1.36.1", + "resolved": "https://registry.npmjs.org/orchid-orm/-/orchid-orm-1.36.1.tgz", + "integrity": "sha512-8qyhBPJomBAEobvYpNL88zL3DfBzYMYQxiWnXjLhGpLt6uBr8uYOW0THQbGqigAhFsU7ORRj88ctlIFbabDXnQ==", + "license": "ISC", "dependencies": { "inflection": "*", - "orchid-core": "0.10.11", - "pqb": "0.18.18", - "prompts": "^2.4.2" - }, - "bin": { - "orchid-orm": "dist/bin.js" + "orchid-core": "0.20.0", + "pqb": "0.41.1", + "prompts": "^2.4.2", + "rake-db": "2.23.1" }, "peerDependencies": { "typescript": "*" @@ -15018,6 +15017,42 @@ "zod": "^3.21.4" } }, + "node_modules/orchid-orm-test-factory/node_modules/orchid-orm": { + "version": "1.17.18", + "resolved": "https://registry.npmjs.org/orchid-orm/-/orchid-orm-1.17.18.tgz", + "integrity": "sha512-qGw8xWsm1uN2HJXCp/Df3wnY5AmgNpoMKVClzkMym4JLtli05736V5Uu+FJShS93w/17baHf/XaJFrOKBpX90Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "inflection": "*", + "orchid-core": "0.10.11", + "pqb": "0.18.18", + "prompts": "^2.4.2" + }, + "bin": { + "orchid-orm": "dist/bin.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/orchid-orm/node_modules/orchid-core": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/orchid-core/-/orchid-core-0.20.0.tgz", + "integrity": "sha512-tu7JKtJ0iH0kj9fjZuFc0mQyv1PsYla8h/2+c3ufZWrBtD98+XJ4Js41MXsLak6NRqAsBNZ6saq/IB5THH5gAg==", + "license": "ISC" + }, + "node_modules/orchid-orm/node_modules/pqb": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/pqb/-/pqb-0.41.1.tgz", + "integrity": "sha512-kxYffO4AU+MTpnNSxHE1ixNsN89oUbplxUDh2cxtMJ1gH6vwGRQRKAr98seYB+mqzylC22bZujU0V4auRP+ypQ==", + "license": "ISC", + "dependencies": { + "@types/pg": ">=8", + "orchid-core": "0.20.0", + "pg": ">=8" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -16067,16 +16102,33 @@ "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" }, "node_modules/rake-db": { - "version": "2.10.52", - "resolved": "https://registry.npmjs.org/rake-db/-/rake-db-2.10.52.tgz", - "integrity": "sha512-9Fy88HDCjY3L3KWGAO/OSOoepzqkSSGmvOOP8aZGe/S7eswwStQ0zzi0RcVxPFZkEM7cjcaa7YZl1TbFGv7l9g==", - "dev": true, + "version": "2.23.1", + "resolved": "https://registry.npmjs.org/rake-db/-/rake-db-2.23.1.tgz", + "integrity": "sha512-TakX7bpHNStj7My69R24WoLRrJrRtgowh5MrsP4WkOrXGl3oONSg2rF3OoQ+8j3gakorzJTW8jjMBXrcHKoWdA==", + "license": "ISC", "dependencies": { - "orchid-core": "0.10.11", - "pqb": "0.18.18", + "orchid-core": "0.20.0", + "pqb": "0.41.1", "prompts": "^2.4.2" } }, + "node_modules/rake-db/node_modules/orchid-core": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/orchid-core/-/orchid-core-0.20.0.tgz", + "integrity": "sha512-tu7JKtJ0iH0kj9fjZuFc0mQyv1PsYla8h/2+c3ufZWrBtD98+XJ4Js41MXsLak6NRqAsBNZ6saq/IB5THH5gAg==", + "license": "ISC" + }, + "node_modules/rake-db/node_modules/pqb": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/pqb/-/pqb-0.41.1.tgz", + "integrity": "sha512-kxYffO4AU+MTpnNSxHE1ixNsN89oUbplxUDh2cxtMJ1gH6vwGRQRKAr98seYB+mqzylC22bZujU0V4auRP+ypQ==", + "license": "ISC", + "dependencies": { + "@types/pg": ">=8", + "orchid-core": "0.20.0", + "pg": ">=8" + } + }, "node_modules/randexp": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", @@ -30479,14 +30531,32 @@ "integrity": "sha512-0drP24JKHmIm2LHaQhMvdbFGkggZGRUU9iJur/JmUqZfgdTp/nqcu8SJmOKaj+IaCb9aKUaZl3kaPOi3/F75oA==" }, "orchid-orm": { - "version": "1.17.18", - "resolved": "https://registry.npmjs.org/orchid-orm/-/orchid-orm-1.17.18.tgz", - "integrity": "sha512-qGw8xWsm1uN2HJXCp/Df3wnY5AmgNpoMKVClzkMym4JLtli05736V5Uu+FJShS93w/17baHf/XaJFrOKBpX90Q==", + "version": "1.36.1", + "resolved": "https://registry.npmjs.org/orchid-orm/-/orchid-orm-1.36.1.tgz", + "integrity": "sha512-8qyhBPJomBAEobvYpNL88zL3DfBzYMYQxiWnXjLhGpLt6uBr8uYOW0THQbGqigAhFsU7ORRj88ctlIFbabDXnQ==", "requires": { "inflection": "*", - "orchid-core": "0.10.11", - "pqb": "0.18.18", - "prompts": "^2.4.2" + "orchid-core": "0.20.0", + "pqb": "0.41.1", + "prompts": "^2.4.2", + "rake-db": "2.23.1" + }, + "dependencies": { + "orchid-core": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/orchid-core/-/orchid-core-0.20.0.tgz", + "integrity": "sha512-tu7JKtJ0iH0kj9fjZuFc0mQyv1PsYla8h/2+c3ufZWrBtD98+XJ4Js41MXsLak6NRqAsBNZ6saq/IB5THH5gAg==" + }, + "pqb": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/pqb/-/pqb-0.41.1.tgz", + "integrity": "sha512-kxYffO4AU+MTpnNSxHE1ixNsN89oUbplxUDh2cxtMJ1gH6vwGRQRKAr98seYB+mqzylC22bZujU0V4auRP+ypQ==", + "requires": { + "@types/pg": ">=8", + "orchid-core": "0.20.0", + "pg": ">=8" + } + } } }, "orchid-orm-schema-to-zod": { @@ -30512,6 +30582,20 @@ "orchid-orm-schema-to-zod": "0.4.21", "pqb": "0.18.18", "zod": "^3.21.4" + }, + "dependencies": { + "orchid-orm": { + "version": "1.17.18", + "resolved": "https://registry.npmjs.org/orchid-orm/-/orchid-orm-1.17.18.tgz", + "integrity": "sha512-qGw8xWsm1uN2HJXCp/Df3wnY5AmgNpoMKVClzkMym4JLtli05736V5Uu+FJShS93w/17baHf/XaJFrOKBpX90Q==", + "dev": true, + "requires": { + "inflection": "*", + "orchid-core": "0.10.11", + "pqb": "0.18.18", + "prompts": "^2.4.2" + } + } } }, "os-tmpdir": { @@ -31194,14 +31278,30 @@ "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" }, "rake-db": { - "version": "2.10.52", - "resolved": "https://registry.npmjs.org/rake-db/-/rake-db-2.10.52.tgz", - "integrity": "sha512-9Fy88HDCjY3L3KWGAO/OSOoepzqkSSGmvOOP8aZGe/S7eswwStQ0zzi0RcVxPFZkEM7cjcaa7YZl1TbFGv7l9g==", - "dev": true, + "version": "2.23.1", + "resolved": "https://registry.npmjs.org/rake-db/-/rake-db-2.23.1.tgz", + "integrity": "sha512-TakX7bpHNStj7My69R24WoLRrJrRtgowh5MrsP4WkOrXGl3oONSg2rF3OoQ+8j3gakorzJTW8jjMBXrcHKoWdA==", "requires": { - "orchid-core": "0.10.11", - "pqb": "0.18.18", + "orchid-core": "0.20.0", + "pqb": "0.41.1", "prompts": "^2.4.2" + }, + "dependencies": { + "orchid-core": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/orchid-core/-/orchid-core-0.20.0.tgz", + "integrity": "sha512-tu7JKtJ0iH0kj9fjZuFc0mQyv1PsYla8h/2+c3ufZWrBtD98+XJ4Js41MXsLak6NRqAsBNZ6saq/IB5THH5gAg==" + }, + "pqb": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/pqb/-/pqb-0.41.1.tgz", + "integrity": "sha512-kxYffO4AU+MTpnNSxHE1ixNsN89oUbplxUDh2cxtMJ1gH6vwGRQRKAr98seYB+mqzylC22bZujU0V4auRP+ypQ==", + "requires": { + "@types/pg": ">=8", + "orchid-core": "0.20.0", + "pg": ">=8" + } + } } }, "randexp": { diff --git a/package.json b/package.json index 984fd91..fb8d2c7 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "next-auth": "^4.24.6", "next-themes": "^0.3.0", "openai": "^4.24.7", - "orchid-orm": "^1.17.18", + "orchid-orm": "^1.36.1", "orchid-orm-schema-to-zod": "^0.4.21", "parse-diff": "^0.11.1", "patch-package": "^8.0.0", diff --git a/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx b/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx index 1071294..85dc29d 100644 --- a/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx +++ b/src/app/dashboard/[org]/[repo]/chat/components/Chat.tsx @@ -11,6 +11,7 @@ import { type Evaluation } from "~/server/api/routers/chat"; import { api } from "~/trpc/react"; import { motion, AnimatePresence } from "framer-motion"; import MarkdownRenderer from "../../components/MarkdownRenderer"; +import ChatHistoryDropdown from "./ChatHistoryDropdown"; interface ChatProps { project: Project; @@ -39,6 +40,9 @@ export function Chat({ project, contextItems, org, repo }: ChatProps) { const [isCreatingArtifact, setIsCreatingArtifact] = useState(false); const [showLoadingCard, setShowLoadingCard] = useState(false); const [codeFiles, setCodeFiles] = useState([]); + const [selectedSessionId, setSelectedSessionId] = useState( + null, + ); const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({ @@ -251,10 +255,18 @@ export function Chat({ project, contextItems, org, repo }: ChatProps) { if (!mounted) return null; + const handleSelectSession = (sessionId: string) => { + setSelectedSessionId(sessionId); + // TODO: Implement logic to load the selected chat session + }; + return (
-
+
+ +
+
{messages.map((m: Message, index: number) => (
void; +} + +const ChatHistoryDropdown: React.FC = ({ + onSelectSession, +}) => { + const [sessions, setSessions] = useState([]); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + // TODO: Fetch chat sessions from the API + // For now, we'll use mock data + setSessions([ + { + id: "1", + timestamp: "2023-05-01 10:00", + summary: "Discussion about React hooks", + }, + { + id: "2", + timestamp: "2023-05-02 14:30", + summary: "Debugging session for API integration", + }, + ]); + }, []); + + const handleSelectSession = (sessionId: string) => { + onSelectSession(sessionId); + setIsOpen(false); + }; + + return ( +
+
+ +
+ {isOpen && ( +
+
+ {sessions.map((session) => ( + + ))} +
+
+ )} +
+ ); +}; + +export default ChatHistoryDropdown; diff --git a/src/server/api/routers/chat.ts b/src/server/api/routers/chat.ts index cb9486a..8c1f077 100644 --- a/src/server/api/routers/chat.ts +++ b/src/server/api/routers/chat.ts @@ -1,6 +1,7 @@ import { TRPCError } from "@trpc/server"; import { z } from "zod"; import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc"; +import { chatSessions } from "~/server/db/tables/chatSessions.table"; import { sendGptRequestWithSchema } from "~/server/openai/request"; import { standardizePath } from "~/server/utils/files"; @@ -17,6 +18,51 @@ const EvaluationSchema = z.object({ export type Evaluation = z.infer; export const chatRouter = createTRPCRouter({ + getChatSessions: protectedProcedure.query(async ({ ctx }) => { + try { + const sessions = await chatSessions.findMany({ + where: { userId: ctx.session.user.id }, + orderBy: { sessionStart: "desc" }, + select: { + id: true, + sessionStart: true, + summary: true, + }, + }); + return sessions; + } catch (error) { + console.error("Error fetching chat sessions", error); + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to fetch chat sessions", + }); + } + }), + + getChatSessionMessages: protectedProcedure + .input(z.object({ sessionId: z.string().uuid() })) + .query(async ({ ctx, input }) => { + try { + const messages = await chatSessions.findFirst({ + where: { id: input.sessionId, userId: ctx.session.user.id }, + select: { messages: true }, + }); + if (!messages) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Chat session not found", + }); + } + return messages.messages; + } catch (error) { + console.error("Error fetching chat session messages", error); + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to fetch chat session messages", + }); + } + }), + evaluateChatMessage: protectedProcedure .input( z.object({ diff --git a/src/server/db/tables/chatSessions.table.ts b/src/server/db/tables/chatSessions.table.ts new file mode 100644 index 0000000..369802e --- /dev/null +++ b/src/server/db/tables/chatSessions.table.ts @@ -0,0 +1,16 @@ +import { table, text, timestamp, uuid } from "orchid-orm"; + +export const chatSessions = table("chat_sessions", { + id: uuid().primaryKey().defaultRandom(), + userId: uuid() + .notNull() + .references(() => users.id), + sessionStart: timestamp().notNull().defaultNow(), + sessionEnd: timestamp(), + summary: text(), + createdAt: timestamp().notNull().defaultNow(), + updatedAt: timestamp().notNull().defaultNow(), +}); + +export type ChatSession = typeof chatSessions.$inferSelect; +export type NewChatSession = typeof chatSessions.$inferInsert;