switch to only query DB for the current page

This commit is contained in:
Brandon Egger 2023-04-10 00:29:58 -05:00
parent 024abe2548
commit a069f4a03a
4 changed files with 98 additions and 50 deletions

View File

@ -4,9 +4,10 @@ import { ClipboardDocumentListIcon } from '@heroicons/react/24/outline';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { translateEnumPlatform, translateEnumSkill } from '~/utils/enumWordLut'; import { translateEnumPlatform, translateEnumSkill } from '~/utils/enumWordLut';
import { type ChangeEvent, type Dispatch, type SetStateAction, useState } from 'react'; import { type ChangeEvent } from 'react';
import { ChevronDownIcon } from '@heroicons/react/24/outline'; import { ChevronDownIcon } from '@heroicons/react/24/outline';
import { type ParsedUrlQuery, type ParsedUrlQueryInput } from 'querystring'; import { type ParsedUrlQuery, type ParsedUrlQueryInput } from 'querystring';
import { useRouter } from 'next/router';
export const ResourceInfo = ({resource, showMoreInfo}: {resource: AuditoryResource, showMoreInfo?: boolean}) => { export const ResourceInfo = ({resource, showMoreInfo}: {resource: AuditoryResource, showMoreInfo?: boolean}) => {
const PriceIcons = ({type}: {type: PaymentType}) => { const PriceIcons = ({type}: {type: PaymentType}) => {
@ -169,10 +170,10 @@ interface PagesNavigationProps {
currentPage: number; currentPage: number;
pageCount: number; pageCount: number;
resultsPerPage: number; resultsPerPage: number;
updateResultsPerPage: Dispatch<SetStateAction<number>>;
} }
const PagesNavigation = ({query, currentPage, pageCount, resultsPerPage, updateResultsPerPage}: PagesNavigationProps) => { const PagesNavigation = ({query, currentPage, pageCount, resultsPerPage}: PagesNavigationProps) => {
const router = useRouter();
const PageButton = ({number}: {number: number}) => { const PageButton = ({number}: {number: number}) => {
const redirectQueryData: ParsedUrlQueryInput = {...query}; const redirectQueryData: ParsedUrlQueryInput = {...query};
redirectQueryData.page = number; redirectQueryData.page = number;
@ -194,7 +195,28 @@ const PagesNavigation = ({query, currentPage, pageCount, resultsPerPage, updateR
}); });
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => { const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
updateResultsPerPage(parseInt(event.target.value)); if (!query) {
router.push({
pathname: '/resources',
query: {
perPage: event.target.value,
}
}).catch((reason) => {
console.error(reason);
});
return;
}
query['perPage'] = event.target.value;
router.push({
pathname: '/resources',
query: {
...query,
}
}).catch((reason) => {
console.error(reason);
});
}; };
return ( return (
@ -225,21 +247,23 @@ const PagesNavigation = ({query, currentPage, pageCount, resultsPerPage, updateR
) )
} }
const ResourceTable = ({resources, currentPage, query}: {resources?: AuditoryResource[], currentPage: number, query?: ParsedUrlQuery}) => { const ResourceTable = ({resources, resourcesPerPage, currentPage, totalPages, query}: {
const [resourcesPerPage, setResourcesPerPage] = useState<number>(10); resources: AuditoryResource[],
resourcesPerPage: number,
const totalPages = Math.ceil((resources?.length ?? 0) / resourcesPerPage); currentPage: number,
const pageResources = resources?.slice(resourcesPerPage*(currentPage-1), (resourcesPerPage*currentPage)) ?? []; totalPages: number,
const resourceElements = pageResources?.map((resource, index) => { query: ParsedUrlQuery
return (<ResourceEntry key={index} resource={resource} />); }) => {
const resourceElements = resources.map((resource, index) => {
return (<ResourceEntry key={index} resource={resource} />);
}) ?? []; }) ?? [];
return( return(
<div className="w-full"> <div className="w-full">
<div className="mx-auto rounded-xl overflow-hidden border border-neutral-400"> <div className="mx-auto rounded-xl overflow-hidden border border-neutral-400 drop-shadow-md overflow-hidden">
<PagesNavigation query={query} updateResultsPerPage={setResourcesPerPage} resultsPerPage={resourcesPerPage} currentPage={currentPage} pageCount={totalPages} /> <PagesNavigation query={query} resultsPerPage={resourcesPerPage} currentPage={currentPage} pageCount={totalPages} />
<table className="w-full table-fixed bg-neutral-200 drop-shadow-md"> <table className="w-full table-fixed bg-neutral-200 border-b border-neutral-400">
<thead className="bg-gradient-to-t from-neutral-900 to-neutral-700 rounded-xl overflow-hidden"> <thead className="bg-gradient-to-t from-neutral-900 to-neutral-700 drop-shadow-md">
<tr> <tr>
<th className="w-1/3 max-w-xs"> <th className="w-1/3 max-w-xs">
<span className="text-gray-300 block px-4 py-2 text-left"> <span className="text-gray-300 block px-4 py-2 text-left">
@ -262,6 +286,9 @@ const ResourceTable = ({resources, currentPage, query}: {resources?: AuditoryRes
{resourceElements} {resourceElements}
</tbody> </tbody>
</table> </table>
{(resources && resources.length > 4) ?
<PagesNavigation query={query} resultsPerPage={resourcesPerPage} currentPage={currentPage} pageCount={totalPages} />
: undefined}
</div> </div>
</div> </div>
); );

View File

@ -12,17 +12,21 @@ const Resources = () => {
const queryData = parseQueryData(router.query); const queryData = parseQueryData(router.query);
const currentPage = queryData.page; const currentPage = queryData.page;
const query = api.auditoryResource.search.useQuery({ const resourceQuery = api.auditoryResource.search.useQuery({
ages: queryData.age, skip: (queryData.page - 1) * queryData.perPage,
platforms: queryData.platforms, take: queryData.perPage,
skill_levels: queryData.skill_levels, ages: queryData.age,
skills: queryData.skills, platforms: queryData.platforms,
skill_levels: queryData.skill_levels,
skills: queryData.skills,
}); });
if (!query.data) { if (!resourceQuery.data) {
return <></> return <></>
} }
const totalPages = Math.ceil(resourceQuery.data.count / queryData.perPage);
return ( return (
<> <>
<Head> <Head>
@ -43,7 +47,7 @@ const Resources = () => {
<p className="inline"> for specific resource recommendations.</p> <p className="inline"> for specific resource recommendations.</p>
</div> </div>
</div> </div>
<ResourceTable query={router.query} resources={query.data} currentPage={currentPage} /> <ResourceTable resourcesPerPage={queryData.perPage} resources={resourceQuery.data.resources} totalPages={totalPages} query={router.query} currentPage={currentPage} />
</main> </main>
</> </>
); );

View File

@ -1,4 +1,4 @@
import { SkillLevel, Skill, Platform, AuditoryResource } from "@prisma/client"; import { SkillLevel, Skill, Platform, type AuditoryResource } from "@prisma/client";
import { z } from "zod"; import { z } from "zod";
import { import {
@ -24,6 +24,8 @@ export const auditoryResourceRouter = createTRPCRouter({
search: publicProcedure search: publicProcedure
.input(z.object({ .input(z.object({
take: z.number().int(),
skip: z.number().int(),
ages: z.object({ ages: z.object({
min: z.number().int(), min: z.number().int(),
max: z.number().int(), max: z.number().int(),
@ -32,34 +34,47 @@ export const auditoryResourceRouter = createTRPCRouter({
skill_levels: z.nativeEnum(SkillLevel).array().optional(), skill_levels: z.nativeEnum(SkillLevel).array().optional(),
skills: z.nativeEnum(Skill).array().optional(), skills: z.nativeEnum(Skill).array().optional(),
})) }))
.query(({ input, ctx}) => { .query(async ({ input, ctx}) => {
const search = {
return ctx.prisma.auditoryResource.findMany({ ages: {
where: { is: {
ages: { min: {
is: { lte: input.ages?.min,
min: { },
lte: input.ages?.min, max: {
}, gte: input.ages?.max,
max: {
gte: input.ages?.max,
}
} }
}, }
skill_levels: { },
hasEvery: input.skill_levels ?? [], skill_levels: {
}, hasEvery: input.skill_levels ?? [],
skills: { },
hasEvery: input.skills ?? [], skills: {
}, hasEvery: input.skills ?? [],
platform_links: { },
some: { platform_links: {
platform: { some: {
in: input.platforms, platform: {
} in: input.platforms,
} }
} }
} }
}) }
const [count, resources] = await ctx.prisma.$transaction([
ctx.prisma.auditoryResource.count({
where: search,
}),
ctx.prisma.auditoryResource.findMany({
skip: input.skip,
take: input.take,
where: search,
})
]);
return {
count,
resources,
}
}), }),
}); });

View File

@ -3,6 +3,7 @@ import { type ParsedUrlQuery } from "querystring";
export interface ViewDetails { export interface ViewDetails {
page: number; page: number;
perPage: number;
} }
export interface SearchQuery { export interface SearchQuery {
@ -17,6 +18,7 @@ export type ParsedQueryData = SearchQuery & ViewDetails;
export const parseQueryData = (query: ParsedUrlQuery): ParsedQueryData => { export const parseQueryData = (query: ParsedUrlQuery): ParsedQueryData => {
const view = { const view = {
page: Number(query["page"] ?? 1), page: Number(query["page"] ?? 1),
perPage: Number(query["perPage"] ?? 10),
} }
const filter: SearchQuery = {}; const filter: SearchQuery = {};