new changes
This commit is contained in:
parent
4efc9e05e0
commit
92cb203503
@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.7.13",
|
"@headlessui/react": "^1.7.13",
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
|
"@mantine/form": "^6.0.7",
|
||||||
"@prisma/client": "^4.11.0",
|
"@prisma/client": "^4.11.0",
|
||||||
"@supabase/auth-helpers-nextjs": "^0.6.0",
|
"@supabase/auth-helpers-nextjs": "^0.6.0",
|
||||||
"@supabase/auth-helpers-react": "^0.3.1",
|
"@supabase/auth-helpers-react": "^0.3.1",
|
||||||
@ -24,6 +25,7 @@
|
|||||||
"@trpc/next": "^10.18.0",
|
"@trpc/next": "^10.18.0",
|
||||||
"@trpc/react-query": "^10.18.0",
|
"@trpc/react-query": "^10.18.0",
|
||||||
"@trpc/server": "^10.18.0",
|
"@trpc/server": "^10.18.0",
|
||||||
|
"axios": "^1.3.5",
|
||||||
"langchain": "^0.0.55",
|
"langchain": "^0.0.55",
|
||||||
"next": "^13.2.4",
|
"next": "^13.2.4",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
36
pnpm-lock.yaml
generated
36
pnpm-lock.yaml
generated
@ -7,6 +7,9 @@ dependencies:
|
|||||||
'@heroicons/react':
|
'@heroicons/react':
|
||||||
specifier: ^2.0.17
|
specifier: ^2.0.17
|
||||||
version: 2.0.17(react@18.2.0)
|
version: 2.0.17(react@18.2.0)
|
||||||
|
'@mantine/form':
|
||||||
|
specifier: ^6.0.7
|
||||||
|
version: 6.0.7(react@18.2.0)
|
||||||
'@prisma/client':
|
'@prisma/client':
|
||||||
specifier: ^4.11.0
|
specifier: ^4.11.0
|
||||||
version: 4.11.0(prisma@4.11.0)
|
version: 4.11.0(prisma@4.11.0)
|
||||||
@ -43,6 +46,9 @@ dependencies:
|
|||||||
'@trpc/server':
|
'@trpc/server':
|
||||||
specifier: ^10.18.0
|
specifier: ^10.18.0
|
||||||
version: 10.18.0
|
version: 10.18.0
|
||||||
|
axios:
|
||||||
|
specifier: ^1.3.5
|
||||||
|
version: 1.3.5
|
||||||
langchain:
|
langchain:
|
||||||
specifier: ^0.0.55
|
specifier: ^0.0.55
|
||||||
version: 0.0.55(@supabase/supabase-js@2.15.0)
|
version: 0.0.55(@supabase/supabase-js@2.15.0)
|
||||||
@ -244,6 +250,16 @@ packages:
|
|||||||
'@jridgewell/resolve-uri': 3.1.0
|
'@jridgewell/resolve-uri': 3.1.0
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
|
|
||||||
|
/@mantine/form@6.0.7(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-5fApVmV9gqqh0h04KkeLzZ79LCBMt91Nydj/h1uzFNluiIxcLCpN/VrOvqiG9DbvIi6h6yeBP/7rbZheHjuXgg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
klona: 2.0.6
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@next/env@13.2.4:
|
/@next/env@13.2.4:
|
||||||
resolution: {integrity: sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA==}
|
resolution: {integrity: sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -969,6 +985,16 @@ packages:
|
|||||||
- debug
|
- debug
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/axios@1.3.5:
|
||||||
|
resolution: {integrity: sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==}
|
||||||
|
dependencies:
|
||||||
|
follow-redirects: 1.15.2
|
||||||
|
form-data: 4.0.0
|
||||||
|
proxy-from-env: 1.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- debug
|
||||||
|
dev: false
|
||||||
|
|
||||||
/axobject-query@3.1.1:
|
/axobject-query@3.1.1:
|
||||||
resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==}
|
resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1672,7 +1698,6 @@ packages:
|
|||||||
|
|
||||||
/fast-deep-equal@3.1.3:
|
/fast-deep-equal@3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fast-glob@3.2.12:
|
/fast-glob@3.2.12:
|
||||||
resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
|
resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
|
||||||
@ -2220,6 +2245,11 @@ packages:
|
|||||||
object.assign: 4.1.4
|
object.assign: 4.1.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/klona@2.0.6:
|
||||||
|
resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/langchain@0.0.55(@supabase/supabase-js@2.15.0):
|
/langchain@0.0.55(@supabase/supabase-js@2.15.0):
|
||||||
resolution: {integrity: sha512-ScL53LvBm2X0rIO1fdMLEoCFYESLVTmY0d71qX7qDrB1y8Y8nCtCA1ZiUNYl4WDQeEvKcvB39qWmAJ2XcB8tqQ==}
|
resolution: {integrity: sha512-ScL53LvBm2X0rIO1fdMLEoCFYESLVTmY0d71qX7qDrB1y8Y8nCtCA1ZiUNYl4WDQeEvKcvB39qWmAJ2XcB8tqQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@ -2891,6 +2921,10 @@ packages:
|
|||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
|
|
||||||
|
/proxy-from-env@1.1.0:
|
||||||
|
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/punycode@2.3.0:
|
/punycode@2.3.0:
|
||||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -23,3 +23,18 @@ class SupaService:
|
|||||||
"user_id": user_id
|
"user_id": user_id
|
||||||
}).execute()
|
}).execute()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def find_website(self, id: str, user_id: str):
|
||||||
|
result = self.supabase.table("Website").select("*").eq("id", id).eq("user_id", user_id).execute()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_user(self, jwt: str):
|
||||||
|
try:
|
||||||
|
result = self.supabase.auth.get_user(jwt)
|
||||||
|
return result
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
from models import ChatBody
|
from models import ChatBody, ChatAppBody
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
from langchain.docstore.document import Document as LDocument
|
from langchain.docstore.document import Document as LDocument
|
||||||
@ -14,21 +14,43 @@ from langchain.prompts.chat import (
|
|||||||
)
|
)
|
||||||
from langchain.vectorstores import Chroma
|
from langchain.vectorstores import Chroma
|
||||||
|
|
||||||
|
from db.supa import SupaService
|
||||||
|
|
||||||
async def chat_extension_handler(body: ChatBody):
|
|
||||||
|
supabase = SupaService()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def chat_app_handler(body: ChatAppBody, jwt: str):
|
||||||
try:
|
try:
|
||||||
soup = BeautifulSoup(body.html, 'lxml')
|
|
||||||
|
|
||||||
iframe = soup.find('iframe', id='pageassist-iframe')
|
|
||||||
if iframe:
|
user = supabase.get_user(jwt)
|
||||||
iframe.decompose()
|
|
||||||
div = soup.find('div', id='pageassist-icon')
|
if not user:
|
||||||
if div:
|
return {
|
||||||
div.decompose()
|
"bot_response": "You are not logged in",
|
||||||
div = soup.find('div', id='__plasmo-loading__')
|
"human_message": body.user_message,
|
||||||
if div:
|
}
|
||||||
div.decompose()
|
|
||||||
text = soup.get_text()
|
|
||||||
|
user_id = user.user.id
|
||||||
|
|
||||||
|
|
||||||
|
website_response = supabase.find_website(body.id, user_id)
|
||||||
|
|
||||||
|
website = website_response.data
|
||||||
|
|
||||||
|
if len(website) == 0:
|
||||||
|
return {
|
||||||
|
"bot_response": "Website not found",
|
||||||
|
"human_message": body.user_message,
|
||||||
|
}
|
||||||
|
|
||||||
|
website = website[0]
|
||||||
|
|
||||||
|
|
||||||
|
text = website["html"]
|
||||||
|
|
||||||
result = [LDocument(page_content=text, metadata={"source": "test"})]
|
result = [LDocument(page_content=text, metadata={"source": "test"})]
|
||||||
token_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
token_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
||||||
@ -43,7 +65,76 @@ async def chat_extension_handler(body: ChatBody):
|
|||||||
messages = [
|
messages = [
|
||||||
SystemMessagePromptTemplate.from_template("""You are PageAssist bot. Use the following pieces of context from this webpage to answer the question from the user.
|
SystemMessagePromptTemplate.from_template("""You are PageAssist bot. Use the following pieces of context from this webpage to answer the question from the user.
|
||||||
If you don't know the answer, just say you don't know. DO NOT try to make up an answer.
|
If you don't know the answer, just say you don't know. DO NOT try to make up an answer.
|
||||||
If user want recommendation, helping based on the context then help the user.
|
If user want recommendation, help from the context, or any other information, please provide it.
|
||||||
|
If the question is not related to the context, politely respond that you are tuned to only answer questions that are related to the context. Helpful answer in markdown:
|
||||||
|
-----------------
|
||||||
|
{context}
|
||||||
|
"""),
|
||||||
|
HumanMessagePromptTemplate.from_template("{question}")
|
||||||
|
]
|
||||||
|
|
||||||
|
prompt = ChatPromptTemplate.from_messages(messages)
|
||||||
|
|
||||||
|
|
||||||
|
chat = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0, model_name="gpt-3.5-turbo"), vectorstore.as_retriever(search_kwargs={"k": 1}), return_source_documents=True, qa_prompt=prompt,)
|
||||||
|
|
||||||
|
history = [(d["human_message"], d["bot_response"]) for d in body.history]
|
||||||
|
|
||||||
|
response = chat({
|
||||||
|
"question": body.user_message,
|
||||||
|
"chat_history": history
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
answer = response["answer"]
|
||||||
|
answer = answer[answer.find(":")+1:].strip()
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
"bot_response": answer,
|
||||||
|
"human_message": body.user_message,
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return {
|
||||||
|
"bot_response": "Something went wrong please try again later",
|
||||||
|
"human_message": body.user_message,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def chat_extension_handler(body: ChatBody):
|
||||||
|
try:
|
||||||
|
soup = BeautifulSoup(body.html, 'lxml')
|
||||||
|
|
||||||
|
iframe = soup.find('iframe', id='pageassist-iframe')
|
||||||
|
if iframe:
|
||||||
|
iframe.decompose()
|
||||||
|
div = soup.find('div', id='pageassist-icon')
|
||||||
|
if div:
|
||||||
|
div.decompose()
|
||||||
|
div = soup.find('div', id='__plasmo-loading__')
|
||||||
|
if div:
|
||||||
|
div.decompose()
|
||||||
|
text = soup.get_text()
|
||||||
|
|
||||||
|
result = [LDocument(page_content=text, metadata={"source": "test"})]
|
||||||
|
token_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
||||||
|
doc = token_splitter.split_documents(result)
|
||||||
|
|
||||||
|
print(f'Number of documents: {len(doc)}')
|
||||||
|
|
||||||
|
|
||||||
|
vectorstore = Chroma.from_documents(doc, OpenAIEmbeddings())
|
||||||
|
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
SystemMessagePromptTemplate.from_template("""You are PageAssist bot. Use the following pieces of context from this webpage to answer the question from the user.
|
||||||
|
If you don't know the answer, just say you don't know. DO NOT try to make up an answer.
|
||||||
|
If user want recommendation, help from the context, or any other information, please provide it.
|
||||||
If the question is not related to the context, politely respond that you are tuned to only answer questions that are related to the context. Helpful answer in markdown:
|
If the question is not related to the context, politely respond that you are tuned to only answer questions that are related to the context. Helpful answer in markdown:
|
||||||
-----------------
|
-----------------
|
||||||
{context}
|
{context}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
from .chat import ChatBody
|
from .chat import ChatBody, ChatAppBody
|
||||||
from .user import UserValidation, SaveChatToApp
|
from .user import UserValidation, SaveChatToApp
|
@ -5,3 +5,9 @@ class ChatBody(BaseModel):
|
|||||||
html: str
|
html: str
|
||||||
history: list
|
history: list
|
||||||
# url: str
|
# url: str
|
||||||
|
|
||||||
|
class ChatAppBody(BaseModel):
|
||||||
|
id: str
|
||||||
|
user_message: str
|
||||||
|
url: str
|
||||||
|
history: list
|
@ -1,9 +1,14 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter, Header
|
||||||
from models import ChatBody
|
from models import ChatBody, ChatAppBody
|
||||||
from handlers.chat import chat_extension_handler
|
from handlers.chat import chat_extension_handler, chat_app_handler
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/v1")
|
router = APIRouter(prefix="/api/v1")
|
||||||
|
|
||||||
@router.post("/chat/chrome", tags=["chat"])
|
@router.post("/chat/chrome", tags=["chat"])
|
||||||
async def chat_extension(body: ChatBody):
|
async def chat_extension(body: ChatBody):
|
||||||
return await chat_extension_handler(body)
|
return await chat_extension_handler(body)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/chat/app", tags=["chat"])
|
||||||
|
async def chat_app(body: ChatAppBody, x_auth_token: str = Header()):
|
||||||
|
return await chat_app_handler(body, x_auth_token)
|
@ -1,8 +1,15 @@
|
|||||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { useSupabaseClient } from "@supabase/auth-helpers-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
import { iconUrl } from "~/utils/icon";
|
import { iconUrl } from "~/utils/icon";
|
||||||
|
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
|
||||||
type Message = {
|
type Message = {
|
||||||
isBot: boolean;
|
isBot: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
@ -22,6 +29,51 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CahtBox = (props: Props) => {
|
export const CahtBox = (props: Props) => {
|
||||||
|
const supabase = useSupabaseClient();
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
initialValues: {
|
||||||
|
message: "",
|
||||||
|
isBot: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendToBot = async (message: string) => {
|
||||||
|
const { data } = await supabase.auth.getSession();
|
||||||
|
|
||||||
|
const response = await axios.post(
|
||||||
|
`${process.env.NEXT_PUBLIC_PAGEASSIST_URL}/api/v1/chat/app`,
|
||||||
|
{
|
||||||
|
user_message: message,
|
||||||
|
history: history,
|
||||||
|
url: props.url,
|
||||||
|
id: props.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"X-Auth-Token": data.session?.access_token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { mutateAsync: sendToBotAsync, isLoading: isSending } = useMutation(
|
||||||
|
sendToBot,
|
||||||
|
{
|
||||||
|
onSuccess: (data) => {
|
||||||
|
setMessages([...messages, { isBot: true, message: data.bot_response }]);
|
||||||
|
setHistory([...history, data]);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
setMessages([
|
||||||
|
...messages,
|
||||||
|
{ isBot: true, message: "Something went wrong" },
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
const [messages, setMessages] = React.useState<Message[]>([
|
const [messages, setMessages] = React.useState<Message[]>([
|
||||||
{
|
{
|
||||||
isBot: true,
|
isBot: true,
|
||||||
@ -29,6 +81,12 @@ export const CahtBox = (props: Props) => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// const fetchSession = async () => {
|
||||||
|
|
||||||
|
// const {data}= await supabase.auth.getSession();
|
||||||
|
// data.session?.access_token
|
||||||
|
// }
|
||||||
|
|
||||||
const [history, setHistory] = React.useState<History[]>([]);
|
const [history, setHistory] = React.useState<History[]>([]);
|
||||||
const divRef = React.useRef(null);
|
const divRef = React.useRef(null);
|
||||||
|
|
||||||
@ -37,6 +95,15 @@ export const CahtBox = (props: Props) => {
|
|||||||
divRef.current.scrollIntoView({ behavior: "smooth" });
|
divRef.current.scrollIntoView({ behavior: "smooth" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { mutateAsync: deleteChatByIdAsync, isLoading: isDeleting } =
|
||||||
|
api.chat.deleteChatById.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
router.push("/dashboard");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col border bg-white">
|
<div className="flex flex-col border bg-white">
|
||||||
{/* header */}
|
{/* header */}
|
||||||
@ -67,10 +134,40 @@ export const CahtBox = (props: Props) => {
|
|||||||
|
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
const isOk = confirm(
|
||||||
|
"Are you sure you want to delete this chat?"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isOk) {
|
||||||
|
await deleteChatByIdAsync({
|
||||||
|
id: props.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={isDeleting}
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex items-center rounded-full border border-transparent bg-red-600 p-1.5 text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
|
className="inline-flex items-center rounded-full border border-transparent bg-red-600 p-1.5 text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
|
||||||
>
|
>
|
||||||
<TrashIcon className="h-5 w-5" aria-hidden="true" />
|
{isDeleting ? (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
className="h-5 w-5 animate-spin fill-white text-white dark:text-gray-600"
|
||||||
|
viewBox="0 0 100 101"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M100 50.59c0 27.615-22.386 50.001-50 50.001s-50-22.386-50-50 22.386-50 50-50 50 22.386 50 50zm-90.919 0c0 22.6 18.32 40.92 40.919 40.92 22.599 0 40.919-18.32 40.919-40.92 0-22.598-18.32-40.918-40.919-40.918-22.599 0-40.919 18.32-40.919 40.919z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentFill"
|
||||||
|
d="M93.968 39.04c2.425-.636 3.894-3.128 3.04-5.486A50 50 0 0041.735 1.279c-2.474.414-3.922 2.919-3.285 5.344.637 2.426 3.12 3.849 5.6 3.484a40.916 40.916 0 0144.131 25.769c.902 2.34 3.361 3.802 5.787 3.165z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<TrashIcon className="h-5 w-5" aria-hidden="true" />
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -107,22 +204,31 @@ export const CahtBox = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{isSending && (
|
||||||
|
<div className="mt-2 flex w-full max-w-xs space-x-3">
|
||||||
|
<div>
|
||||||
|
<div className="rounded-r-lg rounded-bl-lg bg-gray-300 p-3">
|
||||||
|
<p className="text-sm">Hold on, I'm looking...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div ref={divRef} />
|
<div ref={divRef} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="items-center bg-gray-300 px-4 py-4">
|
<div className="items-center bg-gray-300 px-4 py-4">
|
||||||
<form
|
<form
|
||||||
// onSubmit={form.onSubmit(async (values) => {
|
onSubmit={form.onSubmit(async (values) => {
|
||||||
// setMessages([...messages, values])
|
setMessages([...messages, values]);
|
||||||
// form.reset()
|
form.reset();
|
||||||
// await sendToBotAsync(values.message)
|
await sendToBotAsync(values.message);
|
||||||
// })}
|
})}
|
||||||
>
|
>
|
||||||
<div className="flex-grow space-y-6">
|
<div className="flex-grow space-y-6">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<span className="mr-3">
|
<span className="mr-3">
|
||||||
<button
|
<button
|
||||||
// disabled={isSending || isSaving}
|
disabled={isSending}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setHistory([]);
|
setHistory([]);
|
||||||
setMessages([
|
setMessages([
|
||||||
@ -153,12 +259,12 @@ export const CahtBox = (props: Props) => {
|
|||||||
</span>
|
</span>
|
||||||
<div className="flex-grow">
|
<div className="flex-grow">
|
||||||
<input
|
<input
|
||||||
// disabled={isSending || isSaving}
|
disabled={isSending}
|
||||||
className="flex h-10 w-full items-center rounded px-3 text-sm"
|
className="flex h-10 w-full items-center rounded px-3 text-sm"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
placeholder="Type your message…"
|
placeholder="Type your message…"
|
||||||
// {...form.getInputProps("message")}
|
{...form.getInputProps("message")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,7 +48,6 @@ export const chatRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const site = await prisma.website.findFirst({
|
const site = await prisma.website.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: input.id,
|
id: input.id,
|
||||||
@ -74,4 +73,39 @@ export const chatRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return site;
|
return site;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
deleteChatById: publicProcedure.input(z.object({
|
||||||
|
id: z.string(),
|
||||||
|
})).mutation(async ({ ctx, input }) => {
|
||||||
|
const user = ctx.user;
|
||||||
|
const prisma = ctx.prisma;
|
||||||
|
if (!user) {
|
||||||
|
throw new TRPCError({
|
||||||
|
"code": "UNAUTHORIZED",
|
||||||
|
"message": "You are not authorized to access this resource",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const site = await prisma.website.findFirst({
|
||||||
|
where: {
|
||||||
|
id: input.id,
|
||||||
|
user_id: user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!site) {
|
||||||
|
throw new TRPCError({
|
||||||
|
"code": "NOT_FOUND",
|
||||||
|
"message": "Chat not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.website.delete({
|
||||||
|
where: {
|
||||||
|
id: input.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return site;
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user