mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-28 04:33:42 +08:00
[resumes][feat] Add filter counts on browse page (#446)
This commit is contained in:
@ -24,7 +24,12 @@ import ResumeFilterPill from '~/components/resumes/browse/ResumeFilterPill';
|
||||
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
|
||||
import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton';
|
||||
|
||||
import type { Filter, FilterId, Shortcut } from '~/utils/resumes/resumeFilters';
|
||||
import type {
|
||||
Filter,
|
||||
FilterId,
|
||||
FilterLabel,
|
||||
Shortcut,
|
||||
} from '~/utils/resumes/resumeFilters';
|
||||
import {
|
||||
BROWSE_TABS_VALUES,
|
||||
EXPERIENCES,
|
||||
@ -177,6 +182,13 @@ export default function ResumeHomePage() {
|
||||
isSearchOptionsInit,
|
||||
]);
|
||||
|
||||
const filterCountsQuery = trpc.useQuery(
|
||||
['resumes.resume.getTotalFilterCounts'],
|
||||
{
|
||||
staleTime: STALE_TIME,
|
||||
},
|
||||
);
|
||||
|
||||
const allResumesQuery = trpc.useQuery(
|
||||
[
|
||||
'resumes.resume.findAll',
|
||||
@ -237,6 +249,14 @@ export default function ResumeHomePage() {
|
||||
},
|
||||
);
|
||||
|
||||
const getFilterCount = (filter: FilterLabel, value: string) => {
|
||||
if (filterCountsQuery.isLoading) {
|
||||
return 0;
|
||||
}
|
||||
const filterCountsData = filterCountsQuery.data!;
|
||||
return filterCountsData[filter][value];
|
||||
};
|
||||
|
||||
const onSubmitResume = () => {
|
||||
if (sessionData === null) {
|
||||
router.push('/api/auth/signin');
|
||||
@ -495,7 +515,10 @@ export default function ResumeHomePage() {
|
||||
key={option.value}
|
||||
className="[&>div>div:nth-child(1)>input]:text-primary-600 [&>div>div:nth-child(1)>input]:ring-primary-500 px-1 [&>div>div:nth-child(2)>label]:font-normal">
|
||||
<CheckboxInput
|
||||
label={option.label}
|
||||
label={`${option.label} (${getFilterCount(
|
||||
filter.label,
|
||||
option.label,
|
||||
)})`}
|
||||
value={userFilters[filter.id].includes(
|
||||
option.value,
|
||||
)}
|
||||
|
@ -168,6 +168,9 @@ export default function SubmitResumeForm({
|
||||
onSuccess() {
|
||||
if (isNewForm) {
|
||||
trpcContext.invalidateQueries('resumes.resume.findAll');
|
||||
trpcContext.invalidateQueries(
|
||||
'resumes.resume.getTotalFilterCounts',
|
||||
);
|
||||
router.push('/resumes/browse');
|
||||
} else {
|
||||
onClose();
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
import { Vote } from '@prisma/client';
|
||||
|
||||
import { EXPERIENCES, LOCATIONS, ROLES } from '~/utils/resumes/resumeFilters';
|
||||
|
||||
import { createRouter } from '../context';
|
||||
|
||||
import type { Resume } from '~/types/resume';
|
||||
@ -251,4 +253,72 @@ export const resumesRouter = createRouter()
|
||||
|
||||
return topUpvotedCommentCount;
|
||||
},
|
||||
})
|
||||
.query('getTotalFilterCounts', {
|
||||
async resolve({ ctx }) {
|
||||
const roleCounts = await ctx.prisma.resumesResume.groupBy({
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
by: ['role'],
|
||||
});
|
||||
const mappedRoleCounts = Object.fromEntries(
|
||||
roleCounts.map((rc) => [rc.role, rc._count._all]),
|
||||
);
|
||||
const zeroRoleCounts = Object.fromEntries(
|
||||
ROLES.filter((r) => !(r.value in mappedRoleCounts)).map((r) => [
|
||||
r.value,
|
||||
0,
|
||||
]),
|
||||
);
|
||||
const processedRoleCounts = {
|
||||
...mappedRoleCounts,
|
||||
...zeroRoleCounts,
|
||||
};
|
||||
|
||||
const experienceCounts = await ctx.prisma.resumesResume.groupBy({
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
by: ['experience'],
|
||||
});
|
||||
const mappedExperienceCounts = Object.fromEntries(
|
||||
experienceCounts.map((ec) => [ec.experience, ec._count._all]),
|
||||
);
|
||||
const zeroExperienceCounts = Object.fromEntries(
|
||||
EXPERIENCES.filter((e) => !(e.value in mappedExperienceCounts)).map(
|
||||
(e) => [e.value, 0],
|
||||
),
|
||||
);
|
||||
const processedExperienceCounts = {
|
||||
...mappedExperienceCounts,
|
||||
...zeroExperienceCounts,
|
||||
};
|
||||
|
||||
const locationCounts = await ctx.prisma.resumesResume.groupBy({
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
by: ['location'],
|
||||
});
|
||||
const mappedLocationCounts = Object.fromEntries(
|
||||
locationCounts.map((lc) => [lc.location, lc._count._all]),
|
||||
);
|
||||
const zeroLocationCounts = Object.fromEntries(
|
||||
LOCATIONS.filter((l) => !(l.value in mappedLocationCounts)).map((l) => [
|
||||
l.value,
|
||||
0,
|
||||
]),
|
||||
);
|
||||
const processedLocationCounts = {
|
||||
...mappedLocationCounts,
|
||||
...zeroLocationCounts,
|
||||
};
|
||||
|
||||
return {
|
||||
Experience: processedExperienceCounts,
|
||||
Location: processedLocationCounts,
|
||||
Role: processedRoleCounts,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
export type FilterId = 'experience' | 'location' | 'role';
|
||||
export type FilterLabel = 'Experience' | 'Location' | 'Role';
|
||||
|
||||
export type CustomFilter = {
|
||||
numComments: number;
|
||||
@ -29,7 +30,7 @@ export type FilterOption<T> = {
|
||||
|
||||
export type Filter = {
|
||||
id: FilterId;
|
||||
label: string;
|
||||
label: FilterLabel;
|
||||
options: Array<FilterOption<FilterValue>>;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user