mirror of
https://github.com/jorgev259/soc_site-astro.git
synced 2025-06-29 07:57:41 +00:00
Implement search page
This commit is contained in:
parent
5294748076
commit
301854d8da
12 changed files with 180 additions and 17 deletions
|
|
@ -59,8 +59,7 @@ export default defineConfig({
|
||||||
'/studio/[slug]': { status: 307, destination: '/maintenance' },
|
'/studio/[slug]': { status: 307, destination: '/maintenance' },
|
||||||
'/studio/list': { status: 307, destination: '/maintenance' },
|
'/studio/list': { status: 307, destination: '/maintenance' },
|
||||||
'/holy12': { status: 307, destination: '/maintenance' },
|
'/holy12': { status: 307, destination: '/maintenance' },
|
||||||
'/request': { status: 307, destination: '/maintenance' },
|
'/request': { status: 307, destination: '/maintenance' }
|
||||||
'/search': { status: 307, destination: '/maintenance' }
|
|
||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
checkOrigin: false
|
checkOrigin: false
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE FULLTEXT INDEX `albums_title_subTitle_idx` ON `albums`(`title`, `subTitle`);
|
||||||
|
|
@ -153,6 +153,8 @@ model albums {
|
||||||
relatedAlbums related_album[] @relation("related_album_albumIdToalbums")
|
relatedAlbums related_album[] @relation("related_album_albumIdToalbums")
|
||||||
relatedTo related_album[] @relation("related_album_relatedIdToalbums")
|
relatedTo related_album[] @relation("related_album_relatedIdToalbums")
|
||||||
stores stores[]
|
stores stores[]
|
||||||
|
|
||||||
|
@@fulltext([title, subTitle])
|
||||||
}
|
}
|
||||||
|
|
||||||
model animation {
|
model animation {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import Toggler from './header/Toggler.astro'
|
||||||
import NavButton from './header/NavButton.astro'
|
import NavButton from './header/NavButton.astro'
|
||||||
import LoginNav from './header/LoginNav.astro'
|
import LoginNav from './header/LoginNav.astro'
|
||||||
import prismaClient from 'utils/prisma-client.js'
|
import prismaClient from 'utils/prisma-client.js'
|
||||||
|
import { Icon } from 'astro-icon/components'
|
||||||
|
import SearchBar from './search/SearchBar.astro'
|
||||||
|
|
||||||
const { value: bannerId } = (await prismaClient.config.findUnique({ where: { name: 'banner' } })) ?? {}
|
const { value: bannerId } = (await prismaClient.config.findUnique({ where: { name: 'banner' } })) ?? {}
|
||||||
const { value: bannerPosition } = (await prismaClient.config.findUnique({ where: { name: 'banner-position' } })) ?? {}
|
const { value: bannerPosition } = (await prismaClient.config.findUnique({ where: { name: 'banner-position' } })) ?? {}
|
||||||
|
|
@ -42,7 +44,7 @@ const { session } = Astro.locals
|
||||||
<LoginNav />
|
<LoginNav />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav class='relative w-full md:h-[55px] bg-dark z-40'>
|
<div class='relative w-full md:h-[55px] bg-dark z-40'>
|
||||||
<Toggler>
|
<Toggler>
|
||||||
<a href='/'><NavButton>{m.home()}</NavButton></a>
|
<a href='/'><NavButton>{m.home()}</NavButton></a>
|
||||||
<a href='/last-added'><NavButton>{m.lastaddednav()}</NavButton></a>
|
<a href='/last-added'><NavButton>{m.lastaddednav()}</NavButton></a>
|
||||||
|
|
@ -95,6 +97,8 @@ const { session } = Astro.locals
|
||||||
</>
|
</>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<SearchBar />
|
||||||
</Toggler>
|
</Toggler>
|
||||||
</nav>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
---
|
---
|
||||||
import Button from '../Button'
|
const { class: className, ...rest } = Astro.props
|
||||||
import clsx from 'clsx'
|
|
||||||
|
|
||||||
const { class: className } = Astro.props
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
@ -10,6 +7,7 @@ const { class: className } = Astro.props
|
||||||
'w-full md:w-fit md:h-full bg-dark hover:bg-gray py-3.5 md:py-1 px-2 rounded-none text-left md:text-center',
|
'w-full md:w-fit md:h-full bg-dark hover:bg-gray py-3.5 md:py-1 px-2 rounded-none text-left md:text-center',
|
||||||
className
|
className
|
||||||
]}
|
]}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,11 @@
|
||||||
import { Icon } from 'astro-icon/components'
|
import { Icon } from 'astro-icon/components'
|
||||||
---
|
---
|
||||||
|
|
||||||
<nav-toggler
|
<nav-toggler class='flex flex-col md:flex-row max-[990px]:justify-center min-[990px]:px-12 size-full'>
|
||||||
class='flex flex-col md:flex-row max-[990px]:justify-center min-[990px]:ps-20 size-full'
|
|
||||||
>
|
|
||||||
<div class='md:hidden h-[52px]'>
|
<div class='md:hidden h-[52px]'>
|
||||||
<Icon
|
<Icon id='nav-toggler' class='py-3.5 px-3.5 h-full w-auto fill-white cursor-pointer' name='hamburger' />
|
||||||
id='nav-toggler'
|
|
||||||
class='py-3.5 px-3.5 h-full w-auto fill-white cursor-pointer'
|
|
||||||
name='hamburger'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class='nav-items hidden md:block'>
|
<div class='nav-items hidden md:flex w-full'>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</nav-toggler>
|
</nav-toggler>
|
||||||
|
|
|
||||||
72
src/components/search/AlbumSearch.astro
Normal file
72
src/components/search/AlbumSearch.astro
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
import type { Prisma } from '@prisma/client'
|
||||||
|
import type { DefaultArgs } from '@prisma/client/runtime/library'
|
||||||
|
import { Image } from 'astro:assets'
|
||||||
|
|
||||||
|
import prismaClient from 'utils/prisma-client'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
query: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const take = 20
|
||||||
|
const { query } = Astro.props
|
||||||
|
const findQuery: Prisma.albumsFindManyArgs = {
|
||||||
|
select: { title: true, releaseDate: true, id: true },
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
search: query
|
||||||
|
.toLowerCase()
|
||||||
|
.split('_')
|
||||||
|
.map((w) => `+${w}`)
|
||||||
|
.join(' ')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subTitle: {
|
||||||
|
search: query
|
||||||
|
.toLowerCase()
|
||||||
|
.split('_')
|
||||||
|
.map((w) => `+${w}`)
|
||||||
|
.join(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
orderBy: { publishedAt: 'desc' }
|
||||||
|
}
|
||||||
|
const countQuery: Prisma.albumsCountArgs<DefaultArgs> = {
|
||||||
|
where: findQuery.where
|
||||||
|
}
|
||||||
|
|
||||||
|
const [count, search] = await Promise.all([
|
||||||
|
prismaClient.albums.count(countQuery),
|
||||||
|
prismaClient.albums.findMany({ ...findQuery, take })
|
||||||
|
])
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class='text-xl'>Albums ({count}) {count > take ? `/ Showing first ${take} results` : null}</div>
|
||||||
|
<div class='grid sm:grid-cols-1 md:grid-cols-3 mt-1.5 gap-4'>
|
||||||
|
{
|
||||||
|
search.map((album) => (
|
||||||
|
<a class='flex bg-gray h-40 group hover:no-underline' href={`/album/${album.id}`}>
|
||||||
|
<div class='h-full w-40 shrink-0'>
|
||||||
|
<Image
|
||||||
|
class='object-cover'
|
||||||
|
src={`https://cdn.sittingonclouds.net/album/${album.id}.png`}
|
||||||
|
alt={`${album.title} cover`}
|
||||||
|
quality='low'
|
||||||
|
height={160}
|
||||||
|
width={160}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class='flex flex-col p-2.5 justify-center text-left'>
|
||||||
|
<div class=' text-link group-hover:underline'>{album.title}</div>
|
||||||
|
<div class='mt-0.5'>{album.releaseDate?.toISOString().split('T')[0]}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
52
src/components/search/SearchBar.astro
Normal file
52
src/components/search/SearchBar.astro
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
import { Icon } from 'astro-icon/components'
|
||||||
|
import NavButton from 'components/header/NavButton.astro'
|
||||||
|
import { getRandom } from 'utils/form'
|
||||||
|
|
||||||
|
const placeholders = ['Tekken', 'Kingdom Hearts', 'The World Ends with You', 'Persona', 'Splatoon']
|
||||||
|
const placeholder = getRandom(placeholders)
|
||||||
|
---
|
||||||
|
|
||||||
|
<NavButton id='search-open' class='px-3.5 ml-auto' style={{ width: '55px', height: '55px' }}>
|
||||||
|
<Icon name='search' class='size-full' />
|
||||||
|
</NavButton>
|
||||||
|
<div id='search-bar' class='absolute bg-gray-hover/90 left-0 w-full h-full hidden text-white px-12 text-xl'>
|
||||||
|
<form id='search-form' class='grow pe-4'>
|
||||||
|
<input class='h-full w-full' type='text' placeholder={placeholder} required name='query' />
|
||||||
|
</form>
|
||||||
|
<NavButton id='search-close' class='shrink-0' style={{ width: '55px', height: '55px', backgroundColor: 'unset' }}>
|
||||||
|
<Icon name='close' class='size-full stroke-white' />
|
||||||
|
</NavButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const openButton = document.getElementById('search-open')
|
||||||
|
const searchBar = document.getElementById('search-bar')
|
||||||
|
const searchForm = document.getElementById('search-form') as HTMLFormElement
|
||||||
|
const closeButton = document.getElementById('search-close')
|
||||||
|
|
||||||
|
openButton?.addEventListener('click', () => {
|
||||||
|
searchBar?.classList.add('flex')
|
||||||
|
searchBar?.classList.remove('hidden')
|
||||||
|
|
||||||
|
openButton?.classList.add('hidden')
|
||||||
|
openButton?.classList.remove('block')
|
||||||
|
})
|
||||||
|
|
||||||
|
closeButton?.addEventListener('click', () => {
|
||||||
|
searchBar?.classList.add('hidden')
|
||||||
|
searchBar?.classList.remove('flex')
|
||||||
|
|
||||||
|
openButton?.classList.add('block')
|
||||||
|
openButton?.classList.remove('hidden')
|
||||||
|
})
|
||||||
|
|
||||||
|
searchForm?.addEventListener('submit', (ev: Event) => {
|
||||||
|
ev.preventDefault()
|
||||||
|
const formData = new FormData(searchForm)
|
||||||
|
const url = new URL('/search', window.location.origin)
|
||||||
|
url.searchParams.append('q', formData.get('query') as string)
|
||||||
|
|
||||||
|
window.location.href = url.toString()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
13
src/img/icons/close.svg
Normal file
13
src/img/icons/close.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_429_11083)">
|
||||||
|
<path d="M7 7.00006L17 17.0001M7 17.0001L17 7.00006" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_429_11083">
|
||||||
|
<rect width="24" height="24" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 496 B |
4
src/img/icons/search.svg
Normal file
4
src/img/icons/search.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11 6C13.7614 6 16 8.23858 16 11M16.6588 16.6549L21 21M19 11C19 15.4183 15.4183 19 11 19C6.58172 19 3 15.4183 3 11C3 6.58172 6.58172 3 11 3C15.4183 3 19 6.58172 19 11Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 488 B |
18
src/pages/search.astro
Normal file
18
src/pages/search.astro
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
import AlbumSearch from 'components/search/AlbumSearch.astro'
|
||||||
|
import BaseLayout from 'layouts/base.astro'
|
||||||
|
|
||||||
|
const query = Astro.url.searchParams.get('q')
|
||||||
|
if (!query) return Astro.redirect(404)
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout>
|
||||||
|
<div class='w-full bg-dark'>
|
||||||
|
<div class='flex max-w-[1200px] mx-auto justify-center px-8 py-3 flex-col'>
|
||||||
|
<div class='py-4 px-4 bg-gray text-3xl text- w-full font-normal mt-2'>Search results for: {query}</div>
|
||||||
|
<div class='mt-5'>
|
||||||
|
<AlbumSearch query={query} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
|
@ -21,3 +21,8 @@ export async function parseForm(request: Request) {
|
||||||
const data = JSON.parse(dataInput)
|
const data = JSON.parse(dataInput)
|
||||||
return { ...data, ...rest }
|
return { ...data, ...rest }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRandom<T>(array: T[]): T {
|
||||||
|
const randomIndex = Math.floor(Math.random() * array.length)
|
||||||
|
return array[randomIndex]
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue