Skip to content

Commit

Permalink
chore: added search integration on app (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bendomey authored Feb 11, 2025
1 parent 7ce381c commit 22b82c4
Show file tree
Hide file tree
Showing 15 changed files with 653 additions and 92 deletions.
44 changes: 44 additions & 0 deletions apps/client/app/api/contents/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,50 @@ export const useGetContents = ({
retry: retryQuery,
})

export const searchTextualContents = async (
query: FetchMultipleDataInputParams<FetchContentFilter>,
apiConfig?: ApiConfigForServerConfig,
) => {
try {
const removeAllNullableValues = getQueryParams<FetchContentFilter>(query)
const params = new URLSearchParams(removeAllNullableValues)
const response = await fetchClient<
ApiResponse<FetchMultipleDataResponse<Content>>
>(`/v1/contents/search/textual?${params.toString()}`, {
...(apiConfig ? apiConfig : {}),
})

if (!response.parsedBody.status && response.parsedBody.errorMessage) {
throw new Error(response.parsedBody.errorMessage)
}

return response.parsedBody.data
} catch (error: unknown) {
if (error instanceof Error) {
throw error
}

// Error from server.
if (error instanceof Response) {
const response = await error.json()
throw new Error(response.errorMessage)
}
}
}

export const useSearchTextualContents = ({
query,
retryQuery,
}: {
query: FetchMultipleDataInputParams<FetchContentFilter>
retryQuery?: boolean
}) =>
useQuery({
queryKey: [QUERY_KEYS.CONTENTS, 'textual-search', query],
queryFn: () => searchTextualContents(query),
retry: retryQuery,
})

interface UpdateContentInput {
title?: string
visibility?: string
Expand Down
33 changes: 33 additions & 0 deletions apps/client/app/api/creators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,36 @@ export const useGetRelatedCreators = (
queryKey: [QUERY_KEYS.CREATORS, 'related', username, query],
queryFn: () => getRelatedCreators(username, query),
})

export const getCreators = async (
props: FetchMultipleDataInputParams<unknown>,
apiConfig?: ApiConfigForServerConfig,
) => {
try {
const removeAllNullableValues = getQueryParams<unknown>(props)
const params = new URLSearchParams(removeAllNullableValues)
const response = await fetchClient<
ApiResponse<FetchMultipleDataResponse<EnhancedCreator>>
>(`/v1/creators?${params.toString()}`, {
...(apiConfig ? apiConfig : {}),
})

return response.parsedBody.data
} catch (error: unknown) {
// Error from server.
if (error instanceof Response) {
const response = await error.json()
throw new Error(response.errorMessage)
}

if (error instanceof Error) {
throw error
}
}
}

export const useGetCreators = (query: FetchMultipleDataInputParams<unknown>) =>
useQuery({
queryKey: [QUERY_KEYS.CREATORS, query],
queryFn: () => getCreators(query),
})
10 changes: 10 additions & 0 deletions apps/client/app/lib/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@ export const isALink = (url: string) => {
return false
}
}

export const validateLicense = (license: string) => {
const validLicenses = ['ALL', 'FREE', 'PREMIUM']
return validLicenses.includes(license) ? license : 'ALL'
}

export const validateOrientation = (orientation: string) => {
const validOrientations = ['ALL', 'LANDSCAPE', 'PORTRAIT', 'SQUARE']
return validOrientations.includes(orientation) ? orientation : 'ALL'
}
2 changes: 1 addition & 1 deletion apps/client/app/modules/account/creator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const CreatorContext = createContext<EnhancedCreator | null>(null)
export function CreatorPage() {
const data = useLoaderData<typeof loader>()

if (!data.creator) return null
if (!data?.creator) return null

return (
<CreatorContext.Provider value={data.creator as unknown as EnhancedCreator}>
Expand Down
100 changes: 87 additions & 13 deletions apps/client/app/modules/search/collections/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,94 @@
import { ArrowPathIcon } from '@heroicons/react/24/outline'
import { useParams } from '@remix-run/react'
import { useGetCollections } from '@/api/collections/index.ts'
import { FadeIn, FadeInStagger } from '@/components/animation/FadeIn.tsx'
import { Button } from '@/components/button/index.tsx'
import { CollectionCard } from '@/components/CollectionCard/index.tsx'
import { EmptyState } from '@/components/empty-state/index.tsx'
import { ErrorState } from '@/components/error-state/index.tsx'
import { Loader } from '@/components/loader/index.tsx'
import { NoSearchResultLottie } from '@/components/lotties/no-search-results.tsx'
import { PAGES } from '@/constants/index.ts'
import { safeString } from '@/lib/strings.ts'

export function SearchCollectionsModule() {
const { query: queryParam } = useParams()
return (
<div className="flex h-[50vh] flex-1 items-center justify-center">
<EmptyState
message={`There are no collections found under "${queryParam}". Adjust your search query.`}
title="Search results is empty"
svg={
<div className="mb-5">
<NoSearchResultLottie />
</div>
}
/>
</div>
)
const searchQuery = safeString(queryParam)

const { data, isPending, isError } = useGetCollections({
pagination: { page: 0, per: 50 },
filters: { visibility: 'PUBLIC' },
populate: ['collection.createdBy', 'content'],
search: {
query: searchQuery,
},
})

let content = <></>

if (isPending) {
content = (
<div className="flex h-[60vh] flex-1 items-center justify-center">
<Loader />
</div>
)
}

if (isError) {
content = (
<div className="flex h-[60vh] flex-1 items-center justify-center">
<ErrorState
message="An error occurred searching collections."
title="Something happened."
>
<Button
isLink
variant="outlined"
href={PAGES.SEARCH.COLLECTIONS.replace(':query', searchQuery)}
linkProps={{
reloadDocument: true,
}}
>
<ArrowPathIcon
aria-hidden="true"
className="-ml-0.5 mr-1.5 size-5"
/>
Reload
</Button>
</ErrorState>
</div>
)
}

if (data && !data?.total) {
content = (
<div className="flex h-[60vh] flex-1 items-center justify-center">
<EmptyState
message={`There are no collections found under "${queryParam}". Adjust your search query.`}
title="Search results is empty"
svg={
<div className="mb-5">
<NoSearchResultLottie />
</div>
}
/>
</div>
)
}

if (data?.total) {
content = (
<FadeInStagger faster>
<div className="mt-8 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
{data.rows.map((collection) => (
<FadeIn key={collection.id}>
<CollectionCard collection={collection} />
</FadeIn>
))}
</div>
</FadeInStagger>
)
}

return <>{content}</>
}
96 changes: 85 additions & 11 deletions apps/client/app/modules/search/creators/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import { ArrowPathIcon } from '@heroicons/react/24/outline'
import { useParams } from '@remix-run/react'
import { useGetCollectionContentsBySlug } from '@/api/collections/index.ts'
import { useGetCreators } from '@/api/creators/index.ts'
import { FadeIn, FadeInStagger } from '@/components/animation/FadeIn.tsx'
import { Button } from '@/components/button/index.tsx'
import { EmptyState } from '@/components/empty-state/index.tsx'
import { ErrorState } from '@/components/error-state/index.tsx'
import { Loader } from '@/components/loader/index.tsx'
import { NoSearchResultLottie } from '@/components/lotties/no-search-results.tsx'
import { PAGES } from '@/constants/index.ts'
import { safeString } from '@/lib/strings.ts'
import { CreatorSection } from '@/modules/explore/components/section/creator-section.tsx'

export function SearchCreatorsModule() {
const { query: queryParam } = useParams()
const searchQuery = safeString(queryParam)

const { data, isPending, isError } = useGetCreators({
pagination: { page: 0, per: 50 },
filters: {},
populate: [],
search: {
query: searchQuery,
},
})

const { data: featuredCreators } = useGetCollectionContentsBySlug({
slug: 'featured_creators',
Expand All @@ -16,6 +34,72 @@ export function SearchCreatorsModule() {
},
})

let content = <></>

if (isPending) {
content = (
<div className="flex h-[40vh] flex-1 items-center justify-center">
<Loader />
</div>
)
}

if (isError) {
content = (
<div className="flex h-[60vh] flex-1 items-center justify-center">
<ErrorState
message="An error occurred searching creators."
title="Something happened."
>
<Button
isLink
variant="outlined"
href={PAGES.SEARCH.CREATORS.replace(':query', searchQuery)}
linkProps={{
reloadDocument: true,
}}
>
<ArrowPathIcon
aria-hidden="true"
className="-ml-0.5 mr-1.5 size-5"
/>
Reload
</Button>
</ErrorState>
</div>
)
}

if (data && !data?.total) {
content = (
<div className="flex h-[60vh] flex-1 items-center justify-center">
<EmptyState
message={`There are no creators found under "${queryParam}". Adjust your search query.`}
title="Search results is empty"
svg={
<div className="mb-5">
<NoSearchResultLottie />
</div>
}
/>
</div>
)
}

if (data?.total) {
content = (
<FadeInStagger faster>
<div className="mt-8 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
{data.rows.map((collection) => (
<FadeIn key={collection.id}>
<CreatorSection data={collection} />
</FadeIn>
))}
</div>
</FadeInStagger>
)
}

return (
<div className="mt-5">
{featuredCreators?.total ? (
Expand All @@ -34,17 +118,7 @@ export function SearchCreatorsModule() {
</>
) : null}

<div className="flex h-[40vh] flex-1 items-center justify-center">
<EmptyState
message={`There are no creators found under "${queryParam}". Adjust your search query.`}
title="Search results is empty"
svg={
<div className="mb-5">
<NoSearchResultLottie />
</div>
}
/>
</div>
{content}
</div>
)
}
Loading

0 comments on commit 22b82c4

Please sign in to comment.