| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | import { Button } from "@/components/ui/button"; | 
					
						
							|  |  |  | import { Input } from "@/components/ui/input"; | 
					
						
							|  |  |  | import { Toaster } from "@/components/ui/toaster"; | 
					
						
							|  |  |  | import { useToast } from "@/components/ui/use-toast"; | 
					
						
							|  |  |  | import { Footer } from "@/components/utils/Footer"; | 
					
						
							| 
									
										
										
										
											2024-08-16 14:12:30 +08:00
										 |  |  | import { ContentContainer, Page } from "@/components/utils/Layout"; | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | import { NavBar } from "@/components/utils/NavBar"; | 
					
						
							|  |  |  | import { SEO } from "@/components/utils/SEO"; | 
					
						
							|  |  |  | import { Config } from "@/data/config"; | 
					
						
							| 
									
										
										
										
											2024-01-06 20:20:05 +08:00
										 |  |  | import { isEmptyString } from "@/lib/utils"; | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  | import type { TSearchResultItem } from "@/types/search-result"; | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | import axios from "axios"; | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  | import { isArray } from "lodash"; | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | import { nanoid } from "nanoid"; | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  | import type { GetServerSideProps } from "next"; | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | import Link from "next/link"; | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  | import { useRouter } from "next/router"; | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  | import { type ChangeEvent, type KeyboardEvent, useEffect, useState } from "react"; | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  | type SearchPageProps = { query: string | null }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default function SearchPage(props: SearchPageProps) { | 
					
						
							|  |  |  |   const [searchText, setSearchText] = useState<string>(props.query ?? ""); | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |   const [searchResult, setSearchResult] = useState<TSearchResultItem[]>([]); | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |   const [isLoading, setIsLoading] = useState<boolean>(false); | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |   const { toast } = useToast(); | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |   const router = useRouter(); | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     if (!isEmptyString(searchText)) { | 
					
						
							|  |  |  |       handleMakeSearch(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, []); | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |   const fetchSearchAPI = (param: string): Promise<TSearchResultItem[]> => { | 
					
						
							|  |  |  |     return axios.get<TSearchResultItem[]>(`/api/search/${param}`).then((response) => response.data); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const handleInputSearchText = (event: ChangeEvent<HTMLInputElement>) => { | 
					
						
							|  |  |  |     setSearchText(event.target.value); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleEnterKeySearch = (event: KeyboardEvent<HTMLInputElement>) => { | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |     if (event.key === "Go" || event.key === "Enter") { | 
					
						
							|  |  |  |       handleMakeSearch(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleMakeSearch = () => { | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |     const searchQuery = searchText; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (isEmptyString(searchQuery)) { | 
					
						
							| 
									
										
										
										
											2024-01-06 20:20:05 +08:00
										 |  |  |       toast({ title: "Enter a Keyword", description: "Please enter one keyword at least." }); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |     if (searchQuery && searchQuery.length < 4) { | 
					
						
							| 
									
										
										
										
											2024-01-15 11:44:48 +08:00
										 |  |  |       toast({ title: "Keywords too short", description: "Keyword length must be at least 5." }); | 
					
						
							| 
									
										
										
										
											2024-01-09 16:57:42 +08:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     router.push({ | 
					
						
							|  |  |  |       pathname: router.pathname, | 
					
						
							|  |  |  |       query: { ...router.query, q: searchQuery }, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     setIsLoading(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fetchSearchAPI(searchQuery) | 
					
						
							|  |  |  |       .then((data) => { | 
					
						
							|  |  |  |         setSearchResult(data); | 
					
						
							|  |  |  |         if (data.length === 0) { | 
					
						
							|  |  |  |           toast({ | 
					
						
							|  |  |  |             title: "Empty Result", | 
					
						
							|  |  |  |             description: "No results were found for this keyword. Try another keyword.", | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .catch(() => { | 
					
						
							|  |  |  |         toast({ title: "Network Error", description: "Please try it later." }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .finally(() => { | 
					
						
							|  |  |  |         setIsLoading(false); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <Page> | 
					
						
							| 
									
										
										
										
											2024-04-03 22:08:27 +08:00
										 |  |  |       <SEO description={"Search the posts on your demand."} title={`${Config.SiteTitle} - Search`} /> | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |       <Toaster /> | 
					
						
							| 
									
										
										
										
											2024-01-09 16:48:48 +08:00
										 |  |  |       <NavBar /> | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |       <ContentContainer> | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |         <h2 className={"caption-font my-10 flex justify-center font-bold text-2xl"}>{"SEARCH POSTS"}</h2> | 
					
						
							|  |  |  |         <div className="my-10 flex"> | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |           <Input | 
					
						
							|  |  |  |             className="my-auto py-0" | 
					
						
							| 
									
										
										
										
											2024-04-03 22:08:27 +08:00
										 |  |  |             onChange={handleInputSearchText} | 
					
						
							|  |  |  |             onKeyDown={handleEnterKeySearch} | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |             placeholder="Input the keyword" | 
					
						
							|  |  |  |             value={searchText} | 
					
						
							|  |  |  |           /> | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |           <Button className="mx-3 my-auto w-32" disabled={isLoading} onClick={handleMakeSearch}> | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |             {isLoading ? "Loading" : "Search"} | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |           </Button> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |         <div className="flex flex-col justify-center"> | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |           <div className={"flex min-h-full flex-col content-font"}> | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |             {searchResult.map((item, index) => ( | 
					
						
							|  |  |  |               <Link | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |                 className={`border-t p-2 ${index === searchResult.length - 1 && "border-b"} flex flex-col hover:bg-gray-50 dark:hover:bg-gray-900`} | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |                 href={`/blog/${item.id}`} | 
					
						
							|  |  |  |                 key={nanoid()} | 
					
						
							|  |  |  |                 target="_blank" | 
					
						
							|  |  |  |               > | 
					
						
							|  |  |  |                 <div className="my-1"> | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |                   <div className="post-list-caption-font font-bold text-md capitalize">{item.title}</div> | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |                   {item.summary && <div>{item.summary}</div>} | 
					
						
							|  |  |  |                 </div> | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |                 <div className="flex flex-wrap space-x-2"> | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |                   {item.tags?.map((tagitem) => ( | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |                     <div className="text-gray-500 text-sm dark:text-gray-400" key={nanoid()}> | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |                       {tagitem} | 
					
						
							|  |  |  |                     </div> | 
					
						
							|  |  |  |                   ))} | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |               </Link> | 
					
						
							|  |  |  |             ))} | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |           </div> | 
					
						
							|  |  |  |         </div> | 
					
						
							| 
									
										
										
										
											2024-08-14 12:57:22 +08:00
										 |  |  |         <div className="my-3 text-center text-gray-500 dark:text-gray-400"> | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  |           <p className="mx-auto text-sm">{"For search efficiency, only the first 20 results are displayed."}</p> | 
					
						
							|  |  |  |         </div> | 
					
						
							| 
									
										
										
										
											2024-01-06 11:47:18 +08:00
										 |  |  |       </ContentContainer> | 
					
						
							|  |  |  |       <Footer /> | 
					
						
							|  |  |  |     </Page> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-08-12 10:57:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const getServerSideProps: GetServerSideProps<SearchPageProps> = async (context) => { | 
					
						
							|  |  |  |   let query = context.query.q; | 
					
						
							|  |  |  |   if (isArray(query)) query = query.join(" "); | 
					
						
							|  |  |  |   return { props: { query: query ?? null } }; | 
					
						
							|  |  |  | }; |