mirror of
https://github.com/koreanbots/core.git
synced 2025-12-16 14:30:22 +00:00
feat: added category route
close: https://github.com/koreanbots/v2-testing/issues/16
This commit is contained in:
parent
88c452fa26
commit
6bd13bb45f
63
pages/categories/[category].tsx
Normal file
63
pages/categories/[category].tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { NextPage, NextPageContext } from 'next'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ParsedUrlQuery } from 'querystring'
|
||||||
|
|
||||||
|
import { get } from '@utils/Query'
|
||||||
|
import { BotList } from '@types'
|
||||||
|
import { botCategoryListArgumentSchema } from '@utils/Yup'
|
||||||
|
import NotFound from 'pages/404'
|
||||||
|
|
||||||
|
const Hero = dynamic(() => import('@components/Hero'))
|
||||||
|
const Advertisement = dynamic(() => import('@components/Advertisement'))
|
||||||
|
const SEO = dynamic(() => import('@components/SEO'))
|
||||||
|
const BotCard = dynamic(() => import('@components/BotCard'))
|
||||||
|
const Container = dynamic(() => import('@components/Container'))
|
||||||
|
const Paginator = dynamic(() => import('@components/Paginator'))
|
||||||
|
|
||||||
|
const Category: NextPage<CategoryProps> = ({ data, query }) => {
|
||||||
|
if(!data || data.data.length === 0 || data.totalPage < Number(query.page)) return <NotFound />
|
||||||
|
return <>
|
||||||
|
<Hero header={`${query.category} 카테고리 봇들`} description={`다양한 "${query.category}" 카테고리의 봇들을 만나보세요.`} />
|
||||||
|
<SEO title={`${query.category} 카테고리 봇들`} description={`다양한 ${query.category} 카테고리의 봇들을 만나보세요.`} />
|
||||||
|
|
||||||
|
<Container>
|
||||||
|
<Advertisement />
|
||||||
|
<div className='grid gap-4 2xl:grid-cols-4 md:grid-cols-2 mt-20'>
|
||||||
|
{
|
||||||
|
data.data.map(bot => <BotCard key={bot.id} bot={bot} /> )
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<Paginator totalPage={data.totalPage} currentPage={data.currentPage} pathname={`/categories/${query.category}`} />
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getServerSideProps = async (ctx: Context) => {
|
||||||
|
let data: BotList
|
||||||
|
if(!ctx.query.page) ctx.query.page = '1'
|
||||||
|
const validate = await botCategoryListArgumentSchema.validate(ctx.query).then(el => el).catch(() => null)
|
||||||
|
if(!validate || isNaN(Number(ctx.query.page))) data = null
|
||||||
|
else data = await get.list.category.load(JSON.stringify({ page: Number(ctx.query.page), category: ctx.query.category }))
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
data,
|
||||||
|
query: ctx.query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CategoryProps {
|
||||||
|
data: BotList
|
||||||
|
query: URLQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Context extends NextPageContext {
|
||||||
|
query: URLQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
interface URLQuery extends ParsedUrlQuery {
|
||||||
|
category: string
|
||||||
|
page: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Category
|
||||||
@ -259,6 +259,13 @@ export const get = {
|
|||||||
(await Promise.all(ids.map(async (el: string) => await getUser(el, false)))).map(row => ({ ...row }))
|
(await Promise.all(ids.map(async (el: string) => await getUser(el, false)))).map(row => ({ ...row }))
|
||||||
, { cacheMap: new TLRU({ maxStoreSize: 50, maxAgeMs: 60000 }) }),
|
, { cacheMap: new TLRU({ maxStoreSize: 50, maxAgeMs: 60000 }) }),
|
||||||
list: {
|
list: {
|
||||||
|
category: new DataLoader(
|
||||||
|
async (key: string[]) =>
|
||||||
|
(await Promise.all(key.map(async (k: string) => {
|
||||||
|
const json = JSON.parse(k)
|
||||||
|
return await getBotList('CATEGORY', json.page, json.category)
|
||||||
|
}))).map(row => ({ ...row }))
|
||||||
|
, { cacheMap: new TLRU({ maxStoreSize: 50, maxAgeMs: 3000000 }) }),
|
||||||
votes: new DataLoader(
|
votes: new DataLoader(
|
||||||
async (pages: number[]) =>
|
async (pages: number[]) =>
|
||||||
(await Promise.all(pages.map(async (page: number) => await getBotList('VOTE', page)))).map(row => ({ ...row }))
|
(await Promise.all(pages.map(async (page: number) => await getBotList('VOTE', page)))).map(row => ({ ...row }))
|
||||||
|
|||||||
23
utils/Yup.ts
23
utils/Yup.ts
@ -6,8 +6,8 @@ import { HTTPProtocol, ID, Prefix, Url, Vanity } from './Regex'
|
|||||||
|
|
||||||
Yup.setLocale(YupKorean)
|
Yup.setLocale(YupKorean)
|
||||||
|
|
||||||
export const botListArgumentSchema = Yup.object({
|
export const botListArgumentSchema: Yup.SchemaOf<botListArgument> = Yup.object({
|
||||||
type: Yup.string().oneOf(['VOTE', 'TRUSTED', 'NEW', 'PARTNERED', 'CATEGORY', 'SEARCH']).required(),
|
type: Yup.mixed().oneOf(['VOTE', 'TRUSTED', 'NEW', 'PARTNERED', 'CATEGORY', 'SEARCH']).required(),
|
||||||
page: Yup.number().positive().integer().notRequired().default(1),
|
page: Yup.number().positive().integer().notRequired().default(1),
|
||||||
query: Yup.string().notRequired()
|
query: Yup.string().notRequired()
|
||||||
})
|
})
|
||||||
@ -33,16 +33,33 @@ interface ImageOptions {
|
|||||||
type ext = 'webp' | 'png' | 'gif'
|
type ext = 'webp' | 'png' | 'gif'
|
||||||
type ImageSize = '128' | '256' | '512'
|
type ImageSize = '128' | '256' | '512'
|
||||||
|
|
||||||
export const PageCount = Yup.number().integer().positive()
|
export const PageCount = Yup.number().integer().positive().required()
|
||||||
|
|
||||||
export const OauthCallbackSchema: Yup.SchemaOf<OauthCallback> = Yup.object({
|
export const OauthCallbackSchema: Yup.SchemaOf<OauthCallback> = Yup.object({
|
||||||
code: Yup.string().required()
|
code: Yup.string().required()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const botCategoryListArgumentSchema: Yup.SchemaOf<botCategoryListArgument> = Yup.object({
|
||||||
|
page: PageCount,
|
||||||
|
category: Yup.mixed().oneOf(categories).required()
|
||||||
|
})
|
||||||
|
|
||||||
|
interface botCategoryListArgument {
|
||||||
|
page: number
|
||||||
|
category: string
|
||||||
|
}
|
||||||
interface OauthCallback {
|
interface OauthCallback {
|
||||||
code: string
|
code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SearchQuerySchema: Yup.SchemaOf<SearchQuery> = Yup.object({
|
||||||
|
q: Yup.string().min(2, '최소 2글자 이상 입력해주세요.').required()
|
||||||
|
})
|
||||||
|
|
||||||
|
interface SearchQuery {
|
||||||
|
q: string
|
||||||
|
}
|
||||||
|
|
||||||
export const AddBotSubmitSchema = Yup.object({
|
export const AddBotSubmitSchema = Yup.object({
|
||||||
agree: Yup.boolean().oneOf([true], '상단의 체크박스를 클릭해주세요.').required('상단의 체크박스를 클릭해주세요.'),
|
agree: Yup.boolean().oneOf([true], '상단의 체크박스를 클릭해주세요.').required('상단의 체크박스를 클릭해주세요.'),
|
||||||
id: Yup.string().matches(ID, '올바른 봇 ID를 입력해주세요.').required('봇 ID는 필수 항목입니다.'),
|
id: Yup.string().matches(ID, '올바른 봇 ID를 입력해주세요.').required('봇 ID는 필수 항목입니다.'),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user