chat ui added
This commit is contained in:
		
							parent
							
								
									1c7b4c328f
								
							
						
					
					
						commit
						4efc9e05e0
					
				| @ -41,8 +41,9 @@ 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 at the end. |             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 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} | ||||||
|  | |||||||
							
								
								
									
										170
									
								
								src/components/Chat/ChatBox.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/components/Chat/ChatBox.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,170 @@ | |||||||
|  | import { TrashIcon } from "@heroicons/react/24/outline"; | ||||||
|  | import Link from "next/link"; | ||||||
|  | import React from "react"; | ||||||
|  | import { iconUrl } from "~/utils/icon"; | ||||||
|  | 
 | ||||||
|  | type Message = { | ||||||
|  |   isBot: boolean; | ||||||
|  |   message: string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type History = { | ||||||
|  |   bot_response: string; | ||||||
|  |   human_message: string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type Props = { | ||||||
|  |   title: string | null; | ||||||
|  |   id: string; | ||||||
|  |   created_at: Date | null; | ||||||
|  |   icon: string | null; | ||||||
|  |   url: string | null; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const CahtBox = (props: Props) => { | ||||||
|  |   const [messages, setMessages] = React.useState<Message[]>([ | ||||||
|  |     { | ||||||
|  |       isBot: true, | ||||||
|  |       message: "Hi, I'm PageAssist Bot. How can I help you?", | ||||||
|  |     }, | ||||||
|  |   ]); | ||||||
|  | 
 | ||||||
|  |   const [history, setHistory] = React.useState<History[]>([]); | ||||||
|  |   const divRef = React.useRef(null); | ||||||
|  | 
 | ||||||
|  |   React.useEffect(() => { | ||||||
|  |     //@ts-ignore
 | ||||||
|  |     divRef.current.scrollIntoView({ behavior: "smooth" }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className="flex flex-col border bg-white"> | ||||||
|  |       {/* header */} | ||||||
|  |       <div className="bg-grey-lighter  flex flex-row items-center justify-between px-3 py-2"> | ||||||
|  |         <Link | ||||||
|  |           target="_blank" | ||||||
|  |           href={props.url ? props.url : "#"} | ||||||
|  |           className="flex items-center" | ||||||
|  |         > | ||||||
|  |           <div> | ||||||
|  |             <img | ||||||
|  |               className="h-10 w-10 rounded-full" | ||||||
|  |               //@ts-ignore
 | ||||||
|  |               src={iconUrl(props?.icon, props?.url)} | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  |           <div className="ml-4"> | ||||||
|  |             <p className="text-grey-darkest"> | ||||||
|  |               {props?.title && props?.title?.length > 100 | ||||||
|  |                 ? props?.title?.slice(0, 100) + "..." | ||||||
|  |                 : props?.title} | ||||||
|  |             </p> | ||||||
|  |             <p className="mt-1 text-xs text-gray-400"> | ||||||
|  |               {props.url && new URL(props.url).hostname} | ||||||
|  |             </p> | ||||||
|  |           </div> | ||||||
|  |         </Link> | ||||||
|  | 
 | ||||||
|  |         <div className="flex"> | ||||||
|  |           <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" | ||||||
|  |           > | ||||||
|  |             <TrashIcon className="h-5 w-5" aria-hidden="true" /> | ||||||
|  |           </button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       {/*  */} | ||||||
|  |       <div | ||||||
|  |         style={{ height: "calc(100vh - 260px)" }} | ||||||
|  |         className="flex-grow overflow-auto" | ||||||
|  |       > | ||||||
|  |         <div className="px-3 py-2"> | ||||||
|  |           {messages.map((message, index) => { | ||||||
|  |             return ( | ||||||
|  |               <div | ||||||
|  |                 key={index} | ||||||
|  |                 className={ | ||||||
|  |                   message.isBot | ||||||
|  |                     ? "mt-2 flex w-full max-w-xs space-x-3" | ||||||
|  |                     : "ml-auto mt-2 flex w-full max-w-xs justify-end space-x-3" | ||||||
|  |                 } | ||||||
|  |               > | ||||||
|  |                 <div> | ||||||
|  |                   <div | ||||||
|  |                     className={ | ||||||
|  |                       message.isBot | ||||||
|  |                         ? "rounded-r-lg rounded-bl-lg bg-gray-300 p-3" | ||||||
|  |                         : "rounded-l-lg rounded-br-lg bg-blue-600 p-3 text-white" | ||||||
|  |                     } | ||||||
|  |                   > | ||||||
|  |                     <p className="text-sm"> | ||||||
|  |                       {/* <ReactMarkdown>{message.message}</ReactMarkdown> */} | ||||||
|  |                       {message.message} | ||||||
|  |                     </p> | ||||||
|  |                   </div> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |             ); | ||||||
|  |           })} | ||||||
|  |           <div ref={divRef} /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div className="items-center bg-gray-300 px-4  py-4"> | ||||||
|  |         <form | ||||||
|  |         //   onSubmit={form.onSubmit(async (values) => {
 | ||||||
|  |         //     setMessages([...messages, values])
 | ||||||
|  |         //     form.reset()
 | ||||||
|  |         //     await sendToBotAsync(values.message)
 | ||||||
|  |         //   })}
 | ||||||
|  |         > | ||||||
|  |           <div className="flex-grow space-y-6"> | ||||||
|  |             <div className="flex"> | ||||||
|  |               <span className="mr-3"> | ||||||
|  |                 <button | ||||||
|  |                   //   disabled={isSending || isSaving}
 | ||||||
|  |                   onClick={() => { | ||||||
|  |                     setHistory([]); | ||||||
|  |                     setMessages([ | ||||||
|  |                       { | ||||||
|  |                         message: "Hi, I'm PageAssist. How can I help you?", | ||||||
|  |                         isBot: true, | ||||||
|  |                       }, | ||||||
|  |                     ]); | ||||||
|  |                   }} | ||||||
|  |                   className="inline-flex h-10 items-center rounded-md border border-gray-700 bg-white px-3 text-sm font-medium text-gray-700  hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2" | ||||||
|  |                   type="button" | ||||||
|  |                 > | ||||||
|  |                   <svg | ||||||
|  |                     xmlns="http://www.w3.org/2000/svg" | ||||||
|  |                     viewBox="0 0 24 24" | ||||||
|  |                     fill="none" | ||||||
|  |                     stroke="currentColor" | ||||||
|  |                     stroke-width="2" | ||||||
|  |                     stroke-linecap="round" | ||||||
|  |                     stroke-linejoin="round" | ||||||
|  |                     className="h-5 w-5 text-gray-600" | ||||||
|  |                   > | ||||||
|  |                     <path d="M18.37 2.63 14 7l-1.59-1.59a2 2 0 0 0-2.82 0L8 7l9 9 1.59-1.59a2 2 0 0 0 0-2.82L17 10l4.37-4.37a2.12 2.12 0 1 0-3-3Z"></path> | ||||||
|  |                     <path d="M9 8c-2 3-4 3.5-7 4l8 10c2-1 6-5 6-7"></path> | ||||||
|  |                     <path d="M14.5 17.5 4.5 15"></path> | ||||||
|  |                   </svg> | ||||||
|  |                 </button> | ||||||
|  |               </span> | ||||||
|  |               <div className="flex-grow"> | ||||||
|  |                 <input | ||||||
|  |                   //   disabled={isSending || isSaving}
 | ||||||
|  |                   className="flex h-10 w-full items-center rounded px-3 text-sm" | ||||||
|  |                   type="text" | ||||||
|  |                   required | ||||||
|  |                   placeholder="Type your message…" | ||||||
|  |                   //   {...form.getInputProps("message")}
 | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </form> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
							
								
								
									
										26
									
								
								src/components/Chat/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/components/Chat/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | import { useRouter } from "next/router"; | ||||||
|  | import React from "react"; | ||||||
|  | import { api } from "~/utils/api"; | ||||||
|  | import { CahtBox } from "./ChatBox"; | ||||||
|  | 
 | ||||||
|  | export const DashboardChatBody = () => { | ||||||
|  |   const router = useRouter(); | ||||||
|  | 
 | ||||||
|  |   const { id } = router.query; | ||||||
|  | 
 | ||||||
|  |   const { data: chat, status } = api.chat.getChatById.useQuery( | ||||||
|  |     { id: id as string }, | ||||||
|  |     { | ||||||
|  |       onError: (err) => { | ||||||
|  |         router.push("/dashboard"); | ||||||
|  |       }, | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div> | ||||||
|  |       {status === "loading" && <div>Loading...</div>} | ||||||
|  |       {status === "success" && <CahtBox {...chat} />} | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
| @ -3,26 +3,12 @@ import Empty from "./Empty"; | |||||||
| import Loading from "./Loading"; | import Loading from "./Loading"; | ||||||
| import { api } from "~/utils/api"; | import { api } from "~/utils/api"; | ||||||
| import { ChevronRightIcon } from "@heroicons/react/24/outline"; | import { ChevronRightIcon } from "@heroicons/react/24/outline"; | ||||||
|  | import Link from "next/link"; | ||||||
|  | import { iconUrl } from "~/utils/icon"; | ||||||
| 
 | 
 | ||||||
| export default function DashboardBoby() { | export default function DashboardBoby() { | ||||||
|   const { data: savedSites, status } = api.chat.getSavedSitesForChat.useQuery(); |   const { data: savedSites, status } = api.chat.getSavedSitesForChat.useQuery(); | ||||||
| 
 | 
 | ||||||
|   const iconUrl = (icon: string, url: string) => { |  | ||||||
|     // check if icon is valid url  (http:// or https://)
 |  | ||||||
|     if (icon.startsWith("http://") || icon.startsWith("https://")) { |  | ||||||
|       return icon; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // check if icon is valid url  (//)
 |  | ||||||
|     if (icon.startsWith("//")) { |  | ||||||
|       return `https:${icon}`; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const host = new URL(url).hostname; |  | ||||||
|     const protocol = new URL(url).protocol; |  | ||||||
| 
 |  | ||||||
|     return `${protocol}//${host}/${icon}`; |  | ||||||
|   }; |  | ||||||
|    |    | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
| @ -31,7 +17,8 @@ export default function DashboardBoby() { | |||||||
|       {status === "success" && savedSites.data.length > 0 && ( |       {status === "success" && savedSites.data.length > 0 && ( | ||||||
|         <div className="grid grid-cols-1 gap-4 sm:grid-cols-3"> |         <div className="grid grid-cols-1 gap-4 sm:grid-cols-3"> | ||||||
|           {savedSites.data.map((site, idx) => ( |           {savedSites.data.map((site, idx) => ( | ||||||
|             <div |             <Link | ||||||
|  |               href={`/dashboard/chat/${site.id}`} | ||||||
|               key={idx} |               key={idx} | ||||||
|               className="bg-panel-header-light  border-panel-border-light  hover:bg-panel-border-light hover:border-panel-border-hover-light  h-30 group relative flex cursor-pointer flex-row rounded-md border px-6 py-4 text-left transition duration-150 ease-in-out hover:border-gray-300" |               className="bg-panel-header-light  border-panel-border-light  hover:bg-panel-border-light hover:border-panel-border-hover-light  h-30 group relative flex cursor-pointer flex-row rounded-md border px-6 py-4 text-left transition duration-150 ease-in-out hover:border-gray-300" | ||||||
|             > |             > | ||||||
| @ -65,7 +52,7 @@ export default function DashboardBoby() { | |||||||
|                   </div> |                   </div> | ||||||
|                 </div> |                 </div> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </Link> | ||||||
|           ))} |           ))} | ||||||
|         </div> |         </div> | ||||||
|       )} |       )} | ||||||
|  | |||||||
| @ -7,13 +7,18 @@ import { | |||||||
|   XMarkIcon, |   XMarkIcon, | ||||||
| } from "@heroicons/react/24/outline"; | } from "@heroicons/react/24/outline"; | ||||||
| import { ChevronDownIcon } from "@heroicons/react/20/solid"; | import { ChevronDownIcon } from "@heroicons/react/20/solid"; | ||||||
| import { useUser } from "@supabase/auth-helpers-react"; | import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; | ||||||
| import { useRouter } from "next/router"; | import { useRouter } from "next/router"; | ||||||
| import Link from "next/link"; | import Link from "next/link"; | ||||||
| 
 | 
 | ||||||
| const navigation = [ | const navigation = [ | ||||||
|   { name: "Home", href: "/dashboard", icon: HomeIcon, current: true }, |   { name: "Home", href: "/dashboard", icon: HomeIcon, current: true }, | ||||||
|   { name: "Settings", href: "/dashboard/settings", icon: CogIcon, current: false }, |   { | ||||||
|  |     name: "Settings", | ||||||
|  |     href: "/dashboard/settings", | ||||||
|  |     icon: CogIcon, | ||||||
|  |     current: false, | ||||||
|  |   }, | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| //@ts-ignore
 | //@ts-ignore
 | ||||||
| @ -29,6 +34,7 @@ export default function DashboardLayout({ | |||||||
|   const [sidebarOpen, setSidebarOpen] = useState(false); |   const [sidebarOpen, setSidebarOpen] = useState(false); | ||||||
|   const user = useUser(); |   const user = useUser(); | ||||||
|   const router = useRouter(); |   const router = useRouter(); | ||||||
|  |   const supabase = useSupabaseClient(); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
| @ -127,13 +133,9 @@ export default function DashboardLayout({ | |||||||
|                   </nav> |                   </nav> | ||||||
|                 </Dialog.Panel> |                 </Dialog.Panel> | ||||||
|               </Transition.Child> |               </Transition.Child> | ||||||
|               <div className="w-14 flex-shrink-0" aria-hidden="true"> |  | ||||||
|                 {/* Dummy element to force sidebar to shrink to fit close icon */} |  | ||||||
|               </div> |  | ||||||
|             </div> |             </div> | ||||||
|           </Dialog> |           </Dialog> | ||||||
|         </Transition.Root> |         </Transition.Root> | ||||||
| 
 |  | ||||||
|         <div className="hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col"> |         <div className="hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col"> | ||||||
|           <div className="flex flex-grow flex-col overflow-y-auto border-r border-gray-200 bg-white pb-4 pt-5"> |           <div className="flex flex-grow flex-col overflow-y-auto border-r border-gray-200 bg-white pb-4 pt-5"> | ||||||
|             <div className="flex flex-shrink-0 items-center px-4"> |             <div className="flex flex-shrink-0 items-center px-4"> | ||||||
| @ -145,7 +147,7 @@ export default function DashboardLayout({ | |||||||
|             > |             > | ||||||
|               <div className="space-y-1 px-2"> |               <div className="space-y-1 px-2"> | ||||||
|                 {navigation.map((item) => ( |                 {navigation.map((item) => ( | ||||||
|                   <a |                   <Link | ||||||
|                     key={item.name} |                     key={item.name} | ||||||
|                     href={item.href} |                     href={item.href} | ||||||
|                     className={classNames( |                     className={classNames( | ||||||
| @ -154,7 +156,9 @@ export default function DashboardLayout({ | |||||||
|                         : "text-gray-600 hover:bg-gray-50 hover:text-gray-900", |                         : "text-gray-600 hover:bg-gray-50 hover:text-gray-900", | ||||||
|                       "group flex items-center rounded-md px-2 py-2 text-sm font-medium" |                       "group flex items-center rounded-md px-2 py-2 text-sm font-medium" | ||||||
|                     )} |                     )} | ||||||
|                     aria-current={router.pathname === item.href ? "page" : undefined} |                     aria-current={ | ||||||
|  |                       router.pathname === item.href ? "page" : undefined | ||||||
|  |                     } | ||||||
|                   > |                   > | ||||||
|                     <item.icon |                     <item.icon | ||||||
|                       className={classNames( |                       className={classNames( | ||||||
| @ -166,7 +170,7 @@ export default function DashboardLayout({ | |||||||
|                       aria-hidden="true" |                       aria-hidden="true" | ||||||
|                     /> |                     /> | ||||||
|                     {item.name} |                     {item.name} | ||||||
|                   </a> |                   </Link> | ||||||
|                 ))} |                 ))} | ||||||
|               </div> |               </div> | ||||||
|             </nav> |             </nav> | ||||||
| @ -217,28 +221,31 @@ export default function DashboardLayout({ | |||||||
|                     <Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> |                     <Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> | ||||||
|                       <Menu.Item> |                       <Menu.Item> | ||||||
|                         {({ active }) => ( |                         {({ active }) => ( | ||||||
|                           <a |                           <Link | ||||||
|                             href="#" |                             href="/dashboard/settings" | ||||||
|                             className={classNames( |                             className={classNames( | ||||||
|                               active ? "bg-gray-100" : "", |                               active ? "bg-gray-100" : "", | ||||||
|                               "block px-4 py-2 text-sm text-gray-700" |                               "block px-4 py-2 text-sm text-gray-700" | ||||||
|                             )} |                             )} | ||||||
|                           > |                           > | ||||||
|                             Settings |                             Settings | ||||||
|                           </a> |                           </Link> | ||||||
|                         )} |                         )} | ||||||
|                       </Menu.Item> |                       </Menu.Item> | ||||||
|                       <Menu.Item> |                       <Menu.Item> | ||||||
|                         {({ active }) => ( |                         {({ active }) => ( | ||||||
|                           <a |                           <div | ||||||
|                             href="#" |                             onClick={async () => { | ||||||
|  |                               await supabase.auth.signOut(); | ||||||
|  |                               router.push("/"); | ||||||
|  |                             }} | ||||||
|                             className={classNames( |                             className={classNames( | ||||||
|                               active ? "bg-gray-100" : "", |                               active ? "bg-gray-100" : "", | ||||||
|                               "block px-4 py-2 text-sm text-gray-700" |                               "block px-4 py-2 text-sm text-gray-700" | ||||||
|                             )} |                             )} | ||||||
|                           > |                           > | ||||||
|                             Logout |                             Logout | ||||||
|                           </a> |                           </div> | ||||||
|                         )} |                         )} | ||||||
|                       </Menu.Item> |                       </Menu.Item> | ||||||
|                     </Menu.Items> |                     </Menu.Items> | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								src/pages/dashboard/chat/[id]/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/pages/dashboard/chat/[id]/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | import { createServerSupabaseClient } from "@supabase/auth-helpers-nextjs"; | ||||||
|  | import { GetServerSideProps, NextPage } from "next"; | ||||||
|  | import Head from "next/head"; | ||||||
|  | import { DashboardChatBody } from "~/components/Chat"; | ||||||
|  | import DashboardLayout from "~/components/Layouts/DashboardLayout"; | ||||||
|  | 
 | ||||||
|  | export const getServerSideProps: GetServerSideProps = async (ctx) => { | ||||||
|  |   const supabase = createServerSupabaseClient(ctx); | ||||||
|  |   const { | ||||||
|  |     data: { session }, | ||||||
|  |   } = await supabase.auth.getSession(); | ||||||
|  | 
 | ||||||
|  |   if (!session) { | ||||||
|  |     return { | ||||||
|  |       redirect: { | ||||||
|  |         destination: "/auth", | ||||||
|  |         permanent: false, | ||||||
|  |       }, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     props: {}, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const DashboardChatPage: NextPage = () => { | ||||||
|  |   return ( | ||||||
|  |     <DashboardLayout> | ||||||
|  |       <Head> | ||||||
|  |         <title>Chat / PageAssist</title> | ||||||
|  |       </Head> | ||||||
|  |       <DashboardChatBody /> | ||||||
|  |     </DashboardLayout> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default DashboardChatPage; | ||||||
| @ -15,19 +15,6 @@ export const chatRouter = createTRPCRouter({ | |||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       const isUserExist = await prisma.user.findFirst({ |  | ||||||
|         where: { |  | ||||||
|           id: user.id, |  | ||||||
|         }, |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       if (!isUserExist) { |  | ||||||
|         throw new TRPCError({ |  | ||||||
|           "code": "UNAUTHORIZED", |  | ||||||
|           "message": "You are not authorized to access this resource", |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       const sites = await prisma.website.findMany({ |       const sites = await prisma.website.findMany({ | ||||||
|         where: { |         where: { | ||||||
|           user_id: user.id, |           user_id: user.id, | ||||||
| @ -48,4 +35,43 @@ export const chatRouter = createTRPCRouter({ | |||||||
|         length: sites.length, |         length: sites.length, | ||||||
|       }; |       }; | ||||||
|     }), |     }), | ||||||
|  | 
 | ||||||
|  |   getChatById: publicProcedure.input(z.object({ | ||||||
|  |     id: z.string(), | ||||||
|  |   })).query(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, | ||||||
|  |       }, | ||||||
|  |       select: { | ||||||
|  |         user_id: false, | ||||||
|  |         id: true, | ||||||
|  |         created_at: true, | ||||||
|  |         html: false, | ||||||
|  |         icon: true, | ||||||
|  |         title: true, | ||||||
|  |         url: true, | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (!site) { | ||||||
|  |       throw new TRPCError({ | ||||||
|  |         "code": "NOT_FOUND", | ||||||
|  |         "message": "Chat not found", | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return site; | ||||||
|  |   }), | ||||||
| }); | }); | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								src/utils/icon.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/utils/icon.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | export const iconUrl = (icon: string, url: string) => { | ||||||
|  |     // check if icon is valid url  (http:// or https://)
 | ||||||
|  |     if (icon.startsWith("http://") || icon.startsWith("https://")) { | ||||||
|  |       return icon; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // check if icon is valid url  (//)
 | ||||||
|  |     if (icon.startsWith("//")) { | ||||||
|  |       return `https:${icon}`; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const host = new URL(url).hostname; | ||||||
|  |     const protocol = new URL(url).protocol; | ||||||
|  | 
 | ||||||
|  |     return `${protocol}//${host}/${icon}`; | ||||||
|  |   }; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user