Skip to content

Commit

Permalink
thread page implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
saifullah-talukder committed Jun 19, 2024
1 parent 3ca997f commit cddf923
Show file tree
Hide file tree
Showing 14 changed files with 208 additions and 102 deletions.
44 changes: 44 additions & 0 deletions frontend/public/images/answer-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/src/actions/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ export async function searchHistory({ limit, offset }: { limit: number; offset:
throw new Error('Retrieving search history failed')
}

export async function searchReaction(reaction: SearchReactionBody): Promise<SearchResult> {
export async function searchReaction(reaction: SearchReactionBody): Promise<void> {
const response = await curieoFetch('/search/reaction', {
method: 'PATCH',
body: JSON.stringify(reaction),
})
if (response.ok) {
return (await response.json()) as SearchResult
return
}
throw new Error('Could not submit reaction')
}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import Providers from '@/components/wrappers/providers'
import { appDescription, appTitle } from '@/constants/app'
import '@/styles/globals.css'
import type { Metadata } from 'next'
import { Onest } from 'next/font/google'
import { Manrope } from 'next/font/google'
import { ReactNode } from 'react'
import 'react-toastify/dist/ReactToastify.css'

const onest = Onest({ subsets: ['latin'] })
const manrope = Manrope({ subsets: ['latin'] })

export const metadata: Metadata = {
title: appTitle,
Expand All @@ -25,7 +25,7 @@ export default async function RootLayout({ children }: LayoutProps) {
<link rel="icon" type="image/x-icon" href="/images/curieo-logo.svg" />
<title>Curieo Search</title>
</head>
<body className={onest.className} suppressHydrationWarning={true}>
<body className={manrope.className} suppressHydrationWarning={true}>
<Providers>
<App>{children}</App>
</Providers>
Expand Down
28 changes: 13 additions & 15 deletions frontend/src/app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use client'

import { P } from '@/components/lib/typography'
import SearchInput from '@/components/search/search-input'
import NewSearch from '@/components/search/new-search'
import Thread from '@/components/search/thread'
import SearchResultPageSkeleton from '@/components/skeletons/search-result-page-skeleton'
import { useSearchQuery } from '@/queries/search/search-query'
import { useFetchThreadByIdQuery } from '@/queries/search/fetch-thread-by-id-query'
import { useSearchQuery } from '@/queries/search/search-query'
import { useSearchStore } from '@/stores/search/search-store'
import { useRouter, useSearchParams } from 'next/navigation'
import { useEffect } from 'react'
Expand All @@ -19,7 +19,12 @@ export default function Search() {
fetchSearchResult().then(r => r)
}
const threadId = searchParams.get('thread_id')
const { data: thread } = useFetchThreadByIdQuery({ threadId: threadId as string })

// const { data: thread } = useFetchThreadByIdQuery({ threadId: threadId as string })

// useEffect(() => {
// console.log(thread)
// }, [thread])

useEffect(() => {
if (isSuccess) {
Expand All @@ -28,10 +33,6 @@ export default function Search() {
}
}, [isSuccess])

useEffect(() => {
console.log(thread)
}, [thread])

useEffect(() => {
if (isError) {
toast.error('Failed to fetch search result. Please try again later...')
Expand All @@ -40,15 +41,12 @@ export default function Search() {

return (
<>
{isLoading ? (
{!!threadId ? (
<Thread threadId={threadId} />
) : isLoading ? (
<SearchResultPageSkeleton />
) : (
<div className="w-full h-[90vh] flex justify-center items-center">
<div className="w-full max-w-[720px] flex flex-col items-center px-4">
<P className="mb-10 text-2xl xl:text-3xl transition-all duration-300">How can I help you today?</P>
<SearchInput handleSearch={handleSearch} />
</div>
</div>
<NewSearch handleSearch={handleSearch} />
)}
</>
)
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/components/search/new-search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { P } from '@/components/lib/typography'
import SearchInput from '@/components/search/search-input'
import { HTMLAttributes } from 'react'
import { twMerge } from 'tailwind-merge'

type NewSearchProps = HTMLAttributes<HTMLDivElement> & {
handleSearch: () => void
}

export default function NewSearch(props: NewSearchProps) {
return (
<div className={twMerge('w-full h-[90vh] flex justify-center items-center', props.className)}>
<div className="w-full max-w-[720px] flex flex-col items-center px-4">
<P className="mb-10 text-2xl xl:text-3xl transition-all duration-300">How can I help you today?</P>
<SearchInput handleSearch={props.handleSearch} />
</div>
</div>
)
}
56 changes: 56 additions & 0 deletions frontend/src/components/search/old-search-response.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { SearchByIdResponse } from '@/types/search'
import { HTMLAttributes, useEffect, useRef, useState } from 'react'
import SearchActions from './search-actions'
import SearchResponse from './search-response'
import SearchTitle from './search-title'
import SourcesMenu from './sources-menu'
import { Span } from '../lib/typography'

type OldSearchResponseProps = HTMLAttributes<HTMLDivElement> & {
search: SearchByIdResponse
}

export default function OldSearchResponse(props: OldSearchResponseProps) {
const { search: searchResult, sources } = props.search
const answerContainerRef = useRef<HTMLDivElement>(null)
// const sourcesConatinerRef = useRef<HTMLDivElement>(null)
const [answerContainerHeight, setanswerContainerHeight] = useState<number>(0)

useEffect(() => {
const updateHeight = () => {
if (answerContainerRef.current) {
setanswerContainerHeight(Math.min(answerContainerRef.current.offsetHeight, 200))
}
}

updateHeight()

window.addEventListener('resize', updateHeight)
return () => window.removeEventListener('resize', updateHeight)
}, [])

return (
<div className="w-full flex max-h-80">
<div className="w-full flex flex-col justify-between" ref={answerContainerRef}>
<div className="w-full px-10 transition-all duration-300">
<SearchTitle className="mb-6" title={searchResult.query} />
<div className="flex items-center gap-x-3 mb-6">
<img src="images/answer-logo.svg" className="w-10 h-10" alt="answer-logo" />
<Span className="font-light text-white/80 text-xl">Answer</Span>
</div>
<SearchResponse className="mb-6" response={searchResult.result} />
<SearchActions
searchHistoryId={searchResult.search_id}
reaction={searchResult.reaction}
response={searchResult.result}
/>
</div>
</div>
<SourcesMenu
className="w-60 xl:w-96 p-3 transition-all duration-300 bg-white/2 rounded-l-2xl"
sources={sources}
style={{ maxHeight: `calc(100vh - ${answerContainerHeight}px)` }}
/>
</div>
)
}
14 changes: 5 additions & 9 deletions frontend/src/components/search/search-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ type SearchActionsProps = HTMLAttributes<HTMLDivElement> & {
export default function SearchActions(props: SearchActionsProps) {
const queryClient = useQueryClient()
const [isCopied, setIsCopied] = useState(false)
const { isPending: isReacting, mutate: saveReaction } = useSearchReactionMutation()
const [reaction, setReaction] = useState<boolean | null>(props.reaction)
const { isPending: isReacting, mutate: saveReaction } = useSearchReactionMutation(setReaction)

const handleReaction = async (reaction: boolean) => {
saveReaction({ search_history_id: props.searchHistoryId, reaction })
saveReaction({ search_id: props.searchHistoryId, reaction })
}

const handleCopyResponse = () => {
Expand All @@ -48,18 +49,13 @@ export default function SearchActions(props: SearchActionsProps) {
<div className={twMerge('w-full flex gap-x-2.5', props.className)}>
<IconButton
className={buttonClassname}
icon={
<ThumbsUpIcon size={14} className={props.reaction === true ? iconClassName.pressed : iconClassName.deafult} />
}
icon={<ThumbsUpIcon size={14} className={reaction === true ? iconClassName.pressed : iconClassName.deafult} />}
onClick={e => handleReaction(true)}
/>
<IconButton
className={buttonClassname}
icon={
<ThumbsDownIcon
size={14}
className={props.reaction === false ? iconClassName.pressed : iconClassName.deafult}
/>
<ThumbsDownIcon size={14} className={reaction === false ? iconClassName.pressed : iconClassName.deafult} />
}
onClick={e => handleReaction(false)}
/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/search/search-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function SearchInput(props: SearchInputProps) {
return (
<Textarea
ref={textAreaRef}
containerClass="max-w-[880px] grow rounded-2.5xl p-2.5 bg-background-dark/4 dark:bg-background-light/4"
containerClass="max-w-[840px] grow rounded-2.5xl p-2.5 bg-background-dark/4 dark:bg-background-light/4"
innerContainerClass={classNames(
'rounded-2xl bg-background-light/80 dark:bg-background-dark/80 border border-background-dark/40 dark:border-background-light/40 pr-2 focus-within:border-0 focus-within:outline-none focus-within:ring-1 focus-within:ring-primary focus-within:ring-offset-0',
{
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/search/search-response.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ export default function SearchResponse(props: SearchResponseProps) {
onMouseLeave={() => setShowScrollbar(false)}
>
{props.response.split('\n').map((paragraph, index) => (
<P className="pr-4" style={{ animation: `fade-in ${1 + index}s` }} key={`response-paragraph-${index}`}>
<P
className="pr-4 text-sm text-white/80"
style={{ animation: `fade-in ${1 + index}s` }}
key={`response-paragraph-${index}`}
>
{paragraph}
</P>
))}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/search/search-title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type SearchTitleProps = HTMLAttributes<HTMLDivElement> & {

export default function SearchTitle(props: SearchTitleProps) {
return (
<P className={twMerge('text-lg xl:text-2xl font-semibold px-6 py-1 border-l-6 border-primary', props.className)}>
<P className={twMerge('text-lg xl:text-2xl font-medium px-6 py-1 border-l-6 border-primary', props.className)}>
{props.title}
</P>
)
Expand Down
13 changes: 6 additions & 7 deletions frontend/src/components/search/sources-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ type SourcesMenuProps = HTMLAttributes<HTMLDivElement> & { sources: Source[] }
export default function SourcesMenu(props: SourcesMenuProps) {
return (
<div className={twMerge('w-full', props.className)}>
<div className="flex items-center gap-x-2 pl-2 mb-4">
<LayersIcon className="text-typography-light dark:text-typography-dark" size={20} />
<H2 className="font-medium">Sources</H2>
<div className="flex items-center justify-center gap-x-2 py-2 mb-2 mr-2.5 bg-white/10 border border-white/10 rounded-lg">
<LayersIcon className="text-typography-light dark:text-typography-dark" size={14} />
<H2 className="font-medium text-[#DDDDE3] text-sm">Sources</H2>
</div>
<div className="flex flex-col gap-y-2.5">
<div className="flex flex-col gap-y-2.5 overflow-y-scroll scrollbar-visible h-5/6 pr-1">
{props.sources.map((source, index) => (
<LinkPreview
style={{ animation: `fade-in ${Math.min(500 + index * 500, 3000)}ms` }}
className="h-32 w-auto flex flex-col items-stretch justify-center p-2 xl:p-3 rounded-2xl border border-background-dark/20 dark:border-background-light/20 bg-gradient-source-card hover:bg-background-dark/20 dark:hover:bg-background-light/20"
className="h-32 w-auto flex flex-col items-stretch justify-center p-2 xl:p-3 rounded-lg border border-white/10 bg-white/5 hover:bg-background-dark/20 dark:hover:bg-white/15"
key={`source-preview-${index}`}
url={source.url}
metadata={source.metadata}
source={source}
/>
))}
</div>
Expand Down
45 changes: 42 additions & 3 deletions frontend/src/components/search/thread.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
import React from 'react'
import { useFetchThreadByIdQuery } from '@/queries/search/fetch-thread-by-id-query'
import { Fragment, HTMLAttributes, useEffect } from 'react'
import { twMerge } from 'tailwind-merge'
import { H1 } from '../lib/typography'
import OldSearchResponse from './old-search-response'
import SearchInput from './search-input'
import { useSearchQuery } from '@/queries/search/search-query'

export default function Thread() {
return <div>Thread</div>
type ThreadProps = HTMLAttributes<HTMLDivElement> & {
threadId: string
}

export default function Thread(props: ThreadProps) {
const { data } = useFetchThreadByIdQuery({ threadId: props.threadId })
const { data: newSearchResult, isLoading, isSuccess, isError, refetch: fetchSearchResult } = useSearchQuery()
const handleSearch = () => {
fetchSearchResult().then(r => r)
}

useEffect(() => {
console.log(data)
}, [data])

if (!data) {
return null
}

return (
<div className={twMerge('w-full', props.className)}>
<H1 className="px-10 py-4 text-2xl xl:text-3xl font-semibold">{data?.thread.title}</H1>
<div className="pt-4 flex flex-col gap-y-4">
{data?.searches.map((search, index) => (
<Fragment key={`search-response-${data.thread.thread_id}-${index}`}>
<OldSearchResponse search={search} />
<div className="h-0.5 bg-white/20 ml-10 mr-6"></div>
</Fragment>
))}
</div>
<div className="sticky bottom-0 pb-4 px-8 -mb-4 flex justify-start backdrop-blur-sm">
<SearchInput handleSearch={handleSearch} />
</div>
</div>
)
}
Loading

0 comments on commit cddf923

Please sign in to comment.