From 7e4ff9d84723a9b0f3afde689f46ccdabfbeb984 Mon Sep 17 00:00:00 2001 From: mrevanzak Date: Wed, 24 Jul 2024 02:45:33 +0700 Subject: [PATCH] feat: recent question asked for example on mainpage --- apps/web/public/locales/en.json | 1 + apps/web/public/locales/id.json | 1 + apps/web/src/app/(app)/@user/card.tsx | 10 ++++- apps/web/src/app/(app)/@user/page.tsx | 27 ++++++++++++- apps/web/src/app/api/chat/route.ts | 10 ++--- .../src/components/sidebar/sidebar-user.tsx | 2 +- .../server/api/routers/chat/chat.procedure.ts | 38 ++++++++++++++++++- apps/web/src/server/db/index.ts | 2 +- apps/web/src/server/db/schema.ts | 6 ++- packages/ui/src/chat.tsx | 22 ++--------- 10 files changed, 86 insertions(+), 33 deletions(-) diff --git a/apps/web/public/locales/en.json b/apps/web/public/locales/en.json index d31ae7a..00154c7 100644 --- a/apps/web/public/locales/en.json +++ b/apps/web/public/locales/en.json @@ -58,6 +58,7 @@ "title": "Tanya.in", "chooseTopic": "Type '/' to choose the topic", "hint": "Ask a question with english", + "examples": "Get started with an example below", "history": { "show": "Show history", "title": "History", diff --git a/apps/web/public/locales/id.json b/apps/web/public/locales/id.json index f8975d1..547b609 100644 --- a/apps/web/public/locales/id.json +++ b/apps/web/public/locales/id.json @@ -58,6 +58,7 @@ "title": "Tanya.in saja!", "chooseTopic": "Ketik '/' untuk memilih topik", "hint": "Tulis pertanyaan dengan bahasa Indonesia", + "examples": "Mulai dengan contoh di bawah ini", "history": { "show": "Tampilkan riwayat", "title": "Riwayat", diff --git a/apps/web/src/app/(app)/@user/card.tsx b/apps/web/src/app/(app)/@user/card.tsx index 1c10d04..9918534 100644 --- a/apps/web/src/app/(app)/@user/card.tsx +++ b/apps/web/src/app/(app)/@user/card.tsx @@ -5,6 +5,7 @@ import { api } from "@/trpc/react"; import { useTranslations } from "next-intl"; import type { Message } from "@tanya.in/ui"; +import { cn } from "@tanya.in/ui"; import { Card, CardContent, @@ -14,7 +15,7 @@ import { } from "@tanya.in/ui/card"; import { Chat } from "@tanya.in/ui/chat"; -export function ChatCard() { +export function ChatCard(props: { className?: string }) { const t = useTranslations("Home"); const searchParams = useSearchParams(); const utils = api.useUtils(); @@ -26,7 +27,12 @@ export function ChatCard() { const initialMessages = data?.messages as Message[] | undefined; return ( - + {t("title")} diff --git a/apps/web/src/app/(app)/@user/page.tsx b/apps/web/src/app/(app)/@user/page.tsx index 004a492..98aa397 100644 --- a/apps/web/src/app/(app)/@user/page.tsx +++ b/apps/web/src/app/(app)/@user/page.tsx @@ -1,9 +1,32 @@ +import { api } from "@/trpc/server"; +import { getTranslations } from "next-intl/server"; + +import { Card, CardContent } from "@tanya.in/ui/card"; + import { ChatCard } from "./card"; -export default function HomePage() { +export default async function HomePage() { + const t = await getTranslations("Home"); + const recent = await api.chat.getRecentQuestion(); + return (
- + + +

+ {t("examples")} +

+ +
+ {recent.map((chat) => ( + + + {chat.content} + + + ))} +
+
); } diff --git a/apps/web/src/app/api/chat/route.ts b/apps/web/src/app/api/chat/route.ts index 4ac71d0..5a9ede0 100644 --- a/apps/web/src/app/api/chat/route.ts +++ b/apps/web/src/app/api/chat/route.ts @@ -1,7 +1,7 @@ import { env } from "@/env"; import { auth } from "@/server/auth"; import { db } from "@/server/db"; -import { chats, messages } from "@/server/db/schema"; +import { chats, insertMessageSchema, messages } from "@/server/db/schema"; import { Ratelimit } from "@upstash/ratelimit"; import { Redis } from "@upstash/redis"; import { get } from "@vercel/edge-config"; @@ -34,11 +34,8 @@ export async function POST(req: Request) { const body = z .object({ chatId: z.string(), - messages: z - .object({ - id: z.string(), - content: z.string(), - }) + messages: insertMessageSchema + .pick({ id: true, content: true, role: true }) .array(), }) .safeParse(await req.json()); @@ -92,6 +89,7 @@ export async function POST(req: Request) { id: lastMessage.id, chatId: chatId[0]?.id ?? body.data.chatId, content: lastMessage.content, + role: lastMessage.role, }); const reader = res.body.getReader(); diff --git a/apps/web/src/components/sidebar/sidebar-user.tsx b/apps/web/src/components/sidebar/sidebar-user.tsx index 3bd9d76..768f515 100644 --- a/apps/web/src/components/sidebar/sidebar-user.tsx +++ b/apps/web/src/components/sidebar/sidebar-user.tsx @@ -127,7 +127,7 @@ export function SidebarUser() { : router.push(`/?id=${crypto.randomUUID()}&new`); }} > - + {t("new")} diff --git a/apps/web/src/server/api/routers/chat/chat.procedure.ts b/apps/web/src/server/api/routers/chat/chat.procedure.ts index af9c1b2..c762598 100644 --- a/apps/web/src/server/api/routers/chat/chat.procedure.ts +++ b/apps/web/src/server/api/routers/chat/chat.procedure.ts @@ -1,6 +1,7 @@ import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc"; -import { chats } from "@/server/db/schema"; -import { eq } from "drizzle-orm"; +import { chats, messages } from "@/server/db/schema"; +import { and, desc, eq, ilike, notExists, or } from "drizzle-orm"; +import { alias } from "drizzle-orm/pg-core"; import { z } from "zod"; export const chatRouter = createTRPCRouter({ @@ -20,6 +21,7 @@ export const chatRouter = createTRPCRouter({ ), with: { messages: { + orderBy: (message, { asc }) => [asc(message.createdAt)], limit: 1, }, }, @@ -27,6 +29,37 @@ export const chatRouter = createTRPCRouter({ }); }), + getRecentQuestion: protectedProcedure.query(async ({ ctx }) => { + const m = alias(messages, "m"); + return await ctx.db + .select() + .from(m) + .where( + and( + eq(m.role, "user"), + notExists( + ctx.db + .select() + .from(messages) + .where( + and( + eq(messages.chatId, m.chatId), + eq(messages.role, "assistant"), + or( + ilike(messages.content, "%saya tidak tahu%"), + ilike(messages.content, "%hi %"), + ilike(messages.content, "%halo%"), + ), + ), + ) + .orderBy(desc(messages.createdAt)), + ), + ), + ) + .orderBy(desc(m.createdAt)) + .limit(3); + }), + show: protectedProcedure .input( z.object({ @@ -41,6 +74,7 @@ export const chatRouter = createTRPCRouter({ columns: { chatId: false, }, + orderBy: (message, { asc }) => [asc(message.createdAt)], }, }, }); diff --git a/apps/web/src/server/db/index.ts b/apps/web/src/server/db/index.ts index 671359a..9860048 100644 --- a/apps/web/src/server/db/index.ts +++ b/apps/web/src/server/db/index.ts @@ -3,4 +3,4 @@ import { drizzle } from "drizzle-orm/vercel-postgres"; import * as schema from "./schema"; -export const db = drizzle(sql, { schema }); +export const db = drizzle(sql, { schema, logger: true }); diff --git a/apps/web/src/server/db/schema.ts b/apps/web/src/server/db/schema.ts index d4ccf55..9b2a729 100644 --- a/apps/web/src/server/db/schema.ts +++ b/apps/web/src/server/db/schema.ts @@ -7,10 +7,11 @@ import { timestamp, uuid, } from "drizzle-orm/pg-core"; +import { createInsertSchema } from "drizzle-zod"; export const createTable = pgTableCreator((name) => `tanyain_${name}`); -const roleEnum = pgEnum("role", ["user", "admin"]); +export const roleEnum = pgEnum("role", ["user", "admin"]); export const users = createTable("user", { id: uuid("id") .primaryKey() @@ -37,10 +38,12 @@ export const chatRelations = relations(chats, ({ many }) => ({ messages: many(messages), })); +export const senderEnum = pgEnum("sender", ["user", "assistant"]); export const messages = createTable("message", { id: text("id").primaryKey(), content: text("content").notNull(), createdAt: timestamp("created_at", { mode: "date" }).defaultNow(), + role: senderEnum("sender").notNull().default("assistant"), chatId: uuid("chat_id") .notNull() .references(() => chats.id), @@ -51,3 +54,4 @@ export const messageRelations = relations(messages, ({ one }) => ({ references: [chats.id], }), })); +export const insertMessageSchema = createInsertSchema(messages); diff --git a/packages/ui/src/chat.tsx b/packages/ui/src/chat.tsx index a2d5e3c..457d5e0 100644 --- a/packages/ui/src/chat.tsx +++ b/packages/ui/src/chat.tsx @@ -96,19 +96,19 @@ export function Chat({ >
- {messages.map((item, index) => ( + {messages.map((item) => (

- // prev + 1 < TOPICS.length ? prev + 1 : 0, - // ); - // } - - // if (e.key === "ArrowUp") { - // e.preventDefault(); - // setSelectedTopicIndex((prev) => - // prev - 1 >= 0 ? prev - 1 : TOPICS.length - 1, - // ); - // } }} onValueChange={(value) => { if (value.startsWith("/") && !watch("topic")) {