Skip to content

Commit

Permalink
feat: add pagination to search API and UI
Browse files Browse the repository at this point in the history
Signed-off-by: tylerslaton <[email protected]>
  • Loading branch information
tylerslaton committed Mar 22, 2024
1 parent 6ea3a24 commit 8b9f456
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 22 deletions.
22 changes: 11 additions & 11 deletions src/lib/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,22 @@ export async function removeToolForUrlIfExists(url: string): Promise<Tool[]> {
return toolEntry.content as Tool[]
}

export async function getToolsForQuery(query: string): Promise<Record<string, Tool[]>> {
const toolEntries = await prisma.toolEntry.findMany({ where: { reference: { contains: query } } })
export async function getToolsForQuery(query: string, page: number, pageSize: number): Promise<{ tools: Record<string, Tool[]>, totalCount: number }> {
const skip = (page - 1) * pageSize
const toolEntries = await prisma.toolEntry.findMany({
where: { reference: { contains: query } },
take: pageSize,
skip: skip > 0 ? skip : undefined,
})

const tools: Record<string, Tool[]> = {}

for (const entry of toolEntries) {
const parsedTool = entry.content as Tool[]

for (const tool of parsedTool) {
if (tools[entry.reference]) {
tools[entry.reference].push(tool)
} else {
tools[entry.reference] = [tool]
}
}
tools[entry.reference] = tools[entry.reference] || []
tools[entry.reference].push(...parsedTool)
}

return tools
const totalCount = await prisma.toolEntry.count({ where: { reference: { contains: query } } })
return { tools, totalCount }
}
33 changes: 24 additions & 9 deletions src/pages/search.vue
Original file line number Diff line number Diff line change
@@ -1,56 +1,66 @@
<script setup lang="ts">
import type { Tool } from '@/lib/types'
const pageSize = 10
const route = useRoute()
const searchResults = ref({} as Record<string, Tool[]>)
const error = ref({ status: 0, message: '' })
const loading = ref(true)
const page = ref(1)
const totalItems = ref(0)
definePageMeta({
middleware: [
(_from, to) => {
// The computed value isn't available yet in a middleware
const q = `${ to.query.q }`.trim()
const q = `${to.query.q}`.trim()
if (!q) {
return navigateTo('/')
}
if (/^(https?:\/\/)?(www\.)?github\.com\/[\w-]+\/[\w-]+(\/[\w-]+)*$/.test(q)) {
return navigateTo(`/${ q }`, { replace: true })
return navigateTo(`/${q}`, { replace: true })
}
},
],
})
const q = computed(() => {
return `${ route.query.q }`.trim()
return `${route.query.q}`.trim()
})
async function fetchData() {
const results = await fetch(`/api/search?q=${ q.value }`)
const results = await fetch(`/api/search?q=${ q.value }&page=${page.value}`)
if (!results.ok) {
error.value = { status: results.status, message: results.statusText }
loading.value = false
return
}
searchResults.value = await results.json() as Record<string, Tool[]>
const { tools, totalCount } = await results.json()
console.log(totalCount)
searchResults.value = tools
totalItems.value = totalCount
loading.value = false
}
watch(() => route.query, () => fetchData())
watch([() => route.query, page], () => fetchData())
onMounted(() => fetchData())
async function onPageChange(newPage: number) {
page.value = newPage
}
</script>

<template>
<Loading v-if="loading" />
<Error v-else-if="error?.status || !searchResults" :title="`${error?.status || 0}`" :message="error?.message || 'Unknown'" />
<div v-else>
<h1 class="text-2xl font-semibold mb-8">
<template v-if="q === '.'">
<template v-if="q === '.' || q === ''">
All Tools
</template>
<template v-else-if="q === ('sys.')">
Expand All @@ -61,6 +71,8 @@ onMounted(() => fetchData())
</template>
</h1>

<UPagination class="my-8" v-if="totalItems > pageSize" v-model="page" :page-count="10" :total="totalItems" @change="onPageChange" />

<MiniCard v-for="(tools, url) in searchResults" :key="url" class="mb-4">
<template #header>
<UButton variant="ghost" class="text-xl font-semibold block w-full" :to="`/${url}`">
Expand All @@ -77,5 +89,8 @@ onMounted(() => fetchData())
</p>
</div>
</MiniCard>

<UPagination class="my-8" v-if="totalItems > pageSize" v-model="page" :page-count="10" :total="totalItems" @change="onPageChange" />

</div>
</template>
6 changes: 4 additions & 2 deletions src/server/api/search.get.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as db from '@/lib/db'

const pageSize = 10

export default defineEventHandler(async (event) => {
setResponseHeader(event, 'Content-Type', 'application/json')

return await db.getToolsForQuery(getQuery(event).q as string)
const {q, page} = getQuery(event)
return await db.getToolsForQuery(q as string, page as number, pageSize)
})

0 comments on commit 8b9f456

Please sign in to comment.