From a9fc7c4ff706e39f920ba6879709dd9425d72c72 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Thu, 23 Jul 2026 11:14:07 +0330 Subject: [PATCH] compeleted ai --- src/app/[name]/(Main)/ai/AiPage.tsx | 77 +++++++++++++++---- src/app/[name]/(Main)/ai/hooks/useAiData.ts | 8 ++ src/app/[name]/(Main)/ai/service/AiService.ts | 7 ++ src/app/[name]/(Main)/ai/types/Types.ts | 15 ++++ src/config/const.ts | 4 +- src/locales/fa/common.json | 3 +- 6 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 src/app/[name]/(Main)/ai/hooks/useAiData.ts create mode 100644 src/app/[name]/(Main)/ai/service/AiService.ts create mode 100644 src/app/[name]/(Main)/ai/types/Types.ts diff --git a/src/app/[name]/(Main)/ai/AiPage.tsx b/src/app/[name]/(Main)/ai/AiPage.tsx index 8fa7ef6..1c5a182 100644 --- a/src/app/[name]/(Main)/ai/AiPage.tsx +++ b/src/app/[name]/(Main)/ai/AiPage.tsx @@ -2,11 +2,14 @@ import { useGetAbout } from "@/app/[name]/(Main)/about/hooks/useAboutData"; import { useGetProfile } from "@/app/[name]/(Profile)/profile/hooks/userProfileData"; +import { toast } from "@/components/Toast"; +import { extractErrorMessage } from "@/lib/func"; import { glassSurfaceFlat } from "@/lib/styles/glassSurface"; import { Send2 } from "iconsax-react"; import Image from "next/image"; -import { FormEvent, useState } from "react"; +import { FormEvent, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useChat } from "./hooks/useAiData"; import { AI_SUGGESTIONS } from "./suggestions"; type Message = { @@ -27,21 +30,46 @@ function AiPage() { const { t } = useTranslation("common", { keyPrefix: "AI" }); const { data: about } = useGetAbout(); const { data: profile, isSuccess } = useGetProfile(); + const { mutate: chat, isPending } = useChat(); const [input, setInput] = useState(""); const [messages, setMessages] = useState([]); + const listRef = useRef(null); const restaurant = about?.data; const brandName = restaurant?.name || t("BrandFallback"); - const logoUrl = restaurant?.logo || "/assets/images/danak-logo.png"; const userName = isSuccess && profile?.data?.firstName ? profile.data.firstName : t("GuestName"); + useEffect(() => { + listRef.current?.scrollTo({ top: listRef.current.scrollHeight, behavior: "smooth" }); + }, [messages, isPending]); + const sendMessage = (text: string) => { const content = text.trim(); - if (!content) return; + if (!content || isPending) return; - setMessages((prev) => [...prev, { id: `u-${Date.now()}`, role: "user", content }, { id: `a-${Date.now()}`, role: "assistant", content: t("MockReply") }]); + const userMessage: Message = { id: `u-${Date.now()}`, role: "user", content }; + setMessages((prev) => [...prev, userMessage]); setInput(""); + + chat( + { question: content }, + { + onSuccess: (res) => { + setMessages((prev) => [ + ...prev, + { + id: `a-${Date.now()}`, + role: "assistant", + content: res.data.answer, + }, + ]); + }, + onError: (error) => { + toast(extractErrorMessage(error, t("ErrorFallback")), "error"); + }, + } + ); }; const onSubmit = (e: FormEvent) => { @@ -55,15 +83,30 @@ function AiPage() {
{messages.length > 0 ? ( -
    - {messages.map((message) => ( -
  • -

    {message.content}

    +
      + {messages.map((message) => { + const isUser = message.role === "user"; + return ( +
    • +
      +

      {message.content}

      +
      +
    • + ); + })} + {isPending && ( +
    • +
      +

      {t("Thinking")}

      +
    • - ))} + )}
    ) : (
    @@ -79,8 +122,9 @@ function AiPage() { diff --git a/src/app/[name]/(Main)/ai/hooks/useAiData.ts b/src/app/[name]/(Main)/ai/hooks/useAiData.ts new file mode 100644 index 0000000..38971c8 --- /dev/null +++ b/src/app/[name]/(Main)/ai/hooks/useAiData.ts @@ -0,0 +1,8 @@ +import { useMutation } from "@tanstack/react-query"; +import * as api from "../service/AiService"; + +export const useChat = () => { + return useMutation({ + mutationFn: api.chat, + }); +}; diff --git a/src/app/[name]/(Main)/ai/service/AiService.ts b/src/app/[name]/(Main)/ai/service/AiService.ts new file mode 100644 index 0000000..b3463e0 --- /dev/null +++ b/src/app/[name]/(Main)/ai/service/AiService.ts @@ -0,0 +1,7 @@ +import { api } from "@/config/axios"; +import { ChatRequest, ChatResponse } from "../types/Types"; + +export const chat = async (params: ChatRequest) => { + const { data } = await api.post("/public/ai/chat", params); + return data; +}; diff --git a/src/app/[name]/(Main)/ai/types/Types.ts b/src/app/[name]/(Main)/ai/types/Types.ts new file mode 100644 index 0000000..e3cdc97 --- /dev/null +++ b/src/app/[name]/(Main)/ai/types/Types.ts @@ -0,0 +1,15 @@ +import { BaseResponse } from "@/app/[name]/(Main)/types/Types"; + +export type ChatRequest = { + question: string; +}; + +export type ChatData = { + answer: string; + consumedTokens: number; + cost: number; + promptTokens: number; + completionTokens: number; +}; + +export type ChatResponse = BaseResponse; diff --git a/src/config/const.ts b/src/config/const.ts index e341bf0..8a927db 100644 --- a/src/config/const.ts +++ b/src/config/const.ts @@ -1,6 +1,6 @@ // export const API_BASE_URL = "https://dmenuplus-api-production.dev.danakcorp.com"; -// export const API_BASE_URL = "http://192.168.1.107:2000"; -export const API_BASE_URL = "https://dmenu-api.danakcorp.com"; +export const API_BASE_URL = "http://192.168.99.131:2000"; +// export const API_BASE_URL = "https://dmenu-api.danakcorp.com"; process.env.NEXT_PUBLIC_API_URL ?? (process.env.NODE_ENV === "development" ? "http://192.168.99.131:2000" : "https://dmenu-api.danakcorp.com"); export const TOKEN_NAME = "dmenu-t"; export const REFRESH_TOKEN_NAME = "dmenu-rt"; diff --git a/src/locales/fa/common.json b/src/locales/fa/common.json index 32e34b4..f479793 100644 --- a/src/locales/fa/common.json +++ b/src/locales/fa/common.json @@ -83,6 +83,7 @@ "dessert": "پیشنهادت برای دسر چیه؟", "group": "برای ۴ نفر چی سفارش بدیم" }, - "MockReply": "این نسخه فقط رابط کاربری است؛ به‌زودی می‌تونم براتون از منو پیشنهاد واقعی بدم." + "Thinking": "در حال فکر کردن…", + "ErrorFallback": "متأسفانه نتونستم جواب بدم. دوباره امتحان کن." } } \ No newline at end of file