[resumes][feat] Add top 10 shortcut (#518)

pull/520/head
Su Yin 2 years ago committed by GitHub
parent ec97ee992a
commit c2288ba69c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -187,6 +187,7 @@ export default function ResumeHomePage() {
'resumes.resume.findAll', 'resumes.resume.findAll',
{ {
experienceFilters: userFilters.experience.map(({ value }) => value), experienceFilters: userFilters.experience.map(({ value }) => value),
isTop10: userFilters.isTop10,
isUnreviewed: userFilters.isUnreviewed, isUnreviewed: userFilters.isUnreviewed,
locationFilters: userFilters.location.map(({ value }) => value), locationFilters: userFilters.location.map(({ value }) => value),
roleFilters: userFilters.role.map(({ value }) => value), roleFilters: userFilters.role.map(({ value }) => value),
@ -206,6 +207,7 @@ export default function ResumeHomePage() {
'resumes.resume.user.findUserStarred', 'resumes.resume.user.findUserStarred',
{ {
experienceFilters: userFilters.experience.map(({ value }) => value), experienceFilters: userFilters.experience.map(({ value }) => value),
isTop10: userFilters.isTop10,
isUnreviewed: userFilters.isUnreviewed, isUnreviewed: userFilters.isUnreviewed,
locationFilters: userFilters.location.map(({ value }) => value), locationFilters: userFilters.location.map(({ value }) => value),
roleFilters: userFilters.role.map(({ value }) => value), roleFilters: userFilters.role.map(({ value }) => value),
@ -226,6 +228,7 @@ export default function ResumeHomePage() {
'resumes.resume.user.findUserCreated', 'resumes.resume.user.findUserCreated',
{ {
experienceFilters: userFilters.experience.map(({ value }) => value), experienceFilters: userFilters.experience.map(({ value }) => value),
isTop10: userFilters.isTop10,
isUnreviewed: userFilters.isUnreviewed, isUnreviewed: userFilters.isUnreviewed,
locationFilters: userFilters.location.map(({ value }) => value), locationFilters: userFilters.location.map(({ value }) => value),
roleFilters: userFilters.role.map(({ value }) => value), roleFilters: userFilters.role.map(({ value }) => value),

@ -1,6 +1,9 @@
import { z } from 'zod'; import { z } from 'zod';
import { Vote } from '@prisma/client'; import { Vote } from '@prisma/client';
import type { FilterCounts } from '~/utils/resumes/resumeFilters';
import { resumeGetFilterCounts } from '~/utils/resumes/resumeGetFilterCounts';
import { createRouter } from '../context'; import { createRouter } from '../context';
import type { Resume } from '~/types/resume'; import type { Resume } from '~/types/resume';
@ -9,6 +12,7 @@ export const resumesRouter = createRouter()
.query('findAll', { .query('findAll', {
input: z.object({ input: z.object({
experienceFilters: z.string().array(), experienceFilters: z.string().array(),
isTop10: z.boolean(),
isUnreviewed: z.boolean(), isUnreviewed: z.boolean(),
locationFilters: z.string().array(), locationFilters: z.string().array(),
roleFilters: z.string().array(), roleFilters: z.string().array(),
@ -25,19 +29,14 @@ export const resumesRouter = createRouter()
sortOrder, sortOrder,
isUnreviewed, isUnreviewed,
skip, skip,
isTop10,
searchValue, searchValue,
take, take,
} = input; } = input;
const userId = ctx.session?.user?.id; const userId = ctx.session?.user?.id;
const totalRecords = await ctx.prisma.resumesResume.count({ let totalRecords = 10;
where: { let filterCounts = {} as FilterCounts;
experience: { in: experienceFilters },
isResolved: isUnreviewed ? false : {},
locationId: { in: locationFilters },
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
},
});
const resumesData = await ctx.prisma.resumesResume.findMany({ const resumesData = await ctx.prisma.resumesResume.findMany({
include: { include: {
_count: { _count: {
@ -77,7 +76,7 @@ export const resumesRouter = createRouter()
}, },
} }
: { comments: { _count: 'desc' } }, : { comments: { _count: 'desc' } },
skip, skip: isTop10 ? 0 : skip,
take, take,
where: { where: {
experience: { in: experienceFilters }, experience: { in: experienceFilters },
@ -107,62 +106,76 @@ export const resumesRouter = createRouter()
return resume; return resume;
}); });
// Group by role and count, taking into account all role/experience/locationId/isUnreviewed filters and search value if (isTop10) {
const roleCounts = await ctx.prisma.resumesResume.groupBy({ filterCounts = resumeGetFilterCounts(mappedResumeData);
_count: { } else {
_all: true, totalRecords = await ctx.prisma.resumesResume.count({
}, where: {
by: ['role'], experience: { in: experienceFilters },
where: { isResolved: isUnreviewed ? false : {},
experience: { in: experienceFilters }, locationId: { in: locationFilters },
isResolved: isUnreviewed ? false : {}, role: { in: roleFilters },
locationId: { in: locationFilters }, title: { contains: searchValue, mode: 'insensitive' },
title: { contains: searchValue, mode: 'insensitive' }, },
}, });
});
// Map all nonzero counts from array to object where key = role and value = count // Group by role and count, taking into account all role/experience/locationId/isUnreviewed filters and search value
const mappedRoleCounts = Object.fromEntries( const roleCounts = await ctx.prisma.resumesResume.groupBy({
roleCounts.map((rc) => [rc.role, rc._count._all]), _count: {
); _all: true,
},
by: ['role'],
where: {
experience: { in: experienceFilters },
isResolved: isUnreviewed ? false : {},
locationId: { in: locationFilters },
title: { contains: searchValue, mode: 'insensitive' },
},
});
const experienceCounts = await ctx.prisma.resumesResume.groupBy({ // Map all nonzero counts from array to object where key = role and value = count
_count: { const mappedRoleCounts = Object.fromEntries(
_all: true, roleCounts.map((rc) => [rc.role, rc._count._all]),
}, );
by: ['experience'],
where: {
isResolved: isUnreviewed ? false : {},
locationId: { in: locationFilters },
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
},
});
const mappedExperienceCounts = Object.fromEntries(
experienceCounts.map((ec) => [ec.experience, ec._count._all]),
);
const locationCounts = await ctx.prisma.resumesResume.groupBy({ const experienceCounts = await ctx.prisma.resumesResume.groupBy({
_count: { _count: {
_all: true, _all: true,
}, },
by: ['locationId'], by: ['experience'],
where: { where: {
experience: { in: experienceFilters }, isResolved: isUnreviewed ? false : {},
isResolved: isUnreviewed ? false : {}, locationId: { in: locationFilters },
role: { in: roleFilters }, role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' }, title: { contains: searchValue, mode: 'insensitive' },
}, },
}); });
const mappedLocationCounts = Object.fromEntries( const mappedExperienceCounts = Object.fromEntries(
locationCounts.map((lc) => [lc.locationId, lc._count._all]), experienceCounts.map((ec) => [ec.experience, ec._count._all]),
); );
const filterCounts = { const locationCounts = await ctx.prisma.resumesResume.groupBy({
experience: mappedExperienceCounts, _count: {
location: mappedLocationCounts, _all: true,
role: mappedRoleCounts, },
}; by: ['locationId'],
where: {
experience: { in: experienceFilters },
isResolved: isUnreviewed ? false : {},
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
},
});
const mappedLocationCounts = Object.fromEntries(
locationCounts.map((lc) => [lc.locationId, lc._count._all]),
);
filterCounts = {
experience: mappedExperienceCounts,
location: mappedLocationCounts,
role: mappedRoleCounts,
};
}
return { return {
filterCounts, filterCounts,

@ -1,5 +1,8 @@
import { z } from 'zod'; import { z } from 'zod';
import type { FilterCounts } from '~/utils/resumes/resumeFilters';
import { resumeGetFilterCounts } from '~/utils/resumes/resumeGetFilterCounts';
import { createProtectedRouter } from '../context'; import { createProtectedRouter } from '../context';
import type { Resume } from '~/types/resume'; import type { Resume } from '~/types/resume';
@ -63,6 +66,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
.query('findUserStarred', { .query('findUserStarred', {
input: z.object({ input: z.object({
experienceFilters: z.string().array(), experienceFilters: z.string().array(),
isTop10: z.boolean(),
isUnreviewed: z.boolean(), isUnreviewed: z.boolean(),
locationFilters: z.string().array(), locationFilters: z.string().array(),
roleFilters: z.string().array(), roleFilters: z.string().array(),
@ -78,23 +82,16 @@ export const resumesResumeUserRouter = createProtectedRouter()
locationFilters, locationFilters,
experienceFilters, experienceFilters,
searchValue, searchValue,
isTop10,
sortOrder, sortOrder,
isUnreviewed, isUnreviewed,
skip, skip,
take, take,
} = input; } = input;
const totalRecords = await ctx.prisma.resumesStar.count({
where: { let totalRecords = 10;
resume: { let filterCounts = {} as FilterCounts;
experience: { in: experienceFilters },
isResolved: isUnreviewed ? false : {},
locationId: { in: locationFilters },
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
},
userId,
},
});
const resumeStarsData = await ctx.prisma.resumesStar.findMany({ const resumeStarsData = await ctx.prisma.resumesStar.findMany({
include: { include: {
resume: { resume: {
@ -140,7 +137,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
}, },
}, },
}, },
skip, skip: isTop10 ? 0 : skip,
take, take,
where: { where: {
resume: { resume: {
@ -174,74 +171,91 @@ export const resumesResumeUserRouter = createProtectedRouter()
return resume; return resume;
}); });
const roleCounts = await ctx.prisma.resumesResume.groupBy({ if (isTop10) {
_count: { filterCounts = resumeGetFilterCounts(mappedResumeData);
_all: true, } else {
}, totalRecords = await ctx.prisma.resumesStar.count({
by: ['role'], where: {
where: { resume: {
experience: { in: experienceFilters }, experience: { in: experienceFilters },
isResolved: isUnreviewed ? false : {}, isResolved: isUnreviewed ? false : {},
locationId: { in: locationFilters }, locationId: { in: locationFilters },
stars: { role: { in: roleFilters },
some: { title: { contains: searchValue, mode: 'insensitive' },
userId,
}, },
userId,
}, },
title: { contains: searchValue, mode: 'insensitive' }, });
},
});
const mappedRoleCounts = Object.fromEntries(
roleCounts.map((rc) => [rc.role, rc._count._all]),
);
const experienceCounts = await ctx.prisma.resumesResume.groupBy({ const roleCounts = await ctx.prisma.resumesResume.groupBy({
_count: { _count: {
_all: true, _all: true,
}, },
by: ['experience'], by: ['role'],
where: { where: {
isResolved: isUnreviewed ? false : {}, experience: { in: experienceFilters },
locationId: { in: locationFilters }, isResolved: isUnreviewed ? false : {},
role: { in: roleFilters }, locationId: { in: locationFilters },
stars: { stars: {
some: { some: {
userId, userId,
},
}, },
title: { contains: searchValue, mode: 'insensitive' },
}, },
title: { contains: searchValue, mode: 'insensitive' }, });
}, const mappedRoleCounts = Object.fromEntries(
}); roleCounts.map((rc) => [rc.role, rc._count._all]),
const mappedExperienceCounts = Object.fromEntries( );
experienceCounts.map((ec) => [ec.experience, ec._count._all]),
);
const locationCounts = await ctx.prisma.resumesResume.groupBy({ const experienceCounts = await ctx.prisma.resumesResume.groupBy({
_count: { _count: {
_all: true, _all: true,
}, },
by: ['locationId'], by: ['experience'],
where: { where: {
experience: { in: experienceFilters }, isResolved: isUnreviewed ? false : {},
isResolved: isUnreviewed ? false : {}, locationId: { in: locationFilters },
role: { in: roleFilters }, role: { in: roleFilters },
stars: { stars: {
some: { some: {
userId, userId,
},
}, },
title: { contains: searchValue, mode: 'insensitive' },
}, },
title: { contains: searchValue, mode: 'insensitive' }, });
}, const mappedExperienceCounts = Object.fromEntries(
}); experienceCounts.map((ec) => [ec.experience, ec._count._all]),
const mappedLocationCounts = Object.fromEntries( );
locationCounts.map((lc) => [lc.locationId, lc._count._all]),
); const locationCounts = await ctx.prisma.resumesResume.groupBy({
_count: {
_all: true,
},
by: ['locationId'],
where: {
experience: { in: experienceFilters },
isResolved: isUnreviewed ? false : {},
role: { in: roleFilters },
stars: {
some: {
userId,
},
},
title: { contains: searchValue, mode: 'insensitive' },
},
});
const mappedLocationCounts = Object.fromEntries(
locationCounts.map((lc) => [lc.locationId, lc._count._all]),
);
const filterCounts = { filterCounts = {
experience: mappedExperienceCounts, experience: mappedExperienceCounts,
location: mappedLocationCounts, location: mappedLocationCounts,
role: mappedRoleCounts, role: mappedRoleCounts,
}; };
}
return { filterCounts, mappedResumeData, totalRecords }; return { filterCounts, mappedResumeData, totalRecords };
}, },
@ -249,6 +263,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
.query('findUserCreated', { .query('findUserCreated', {
input: z.object({ input: z.object({
experienceFilters: z.string().array(), experienceFilters: z.string().array(),
isTop10: z.boolean(),
isUnreviewed: z.boolean(), isUnreviewed: z.boolean(),
locationFilters: z.string().array(), locationFilters: z.string().array(),
roleFilters: z.string().array(), roleFilters: z.string().array(),
@ -265,20 +280,15 @@ export const resumesResumeUserRouter = createProtectedRouter()
experienceFilters, experienceFilters,
sortOrder, sortOrder,
searchValue, searchValue,
isTop10,
isUnreviewed, isUnreviewed,
take, take,
skip, skip,
} = input; } = input;
const totalRecords = await ctx.prisma.resumesResume.count({
where: { let totalRecords = 10;
experience: { in: experienceFilters }, let filterCounts = {} as FilterCounts;
isResolved: isUnreviewed ? false : {},
locationId: { in: locationFilters },
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
userId,
},
});
const resumesData = await ctx.prisma.resumesResume.findMany({ const resumesData = await ctx.prisma.resumesResume.findMany({
include: { include: {
_count: { _count: {
@ -315,7 +325,7 @@ export const resumesResumeUserRouter = createProtectedRouter()
}, },
} }
: { comments: { _count: 'desc' } }, : { comments: { _count: 'desc' } },
skip, skip: isTop10 ? 0 : skip,
take, take,
where: { where: {
experience: { in: experienceFilters }, experience: { in: experienceFilters },
@ -346,62 +356,77 @@ export const resumesResumeUserRouter = createProtectedRouter()
return resume; return resume;
}); });
const roleCounts = await ctx.prisma.resumesResume.groupBy({ if (isTop10) {
_count: { filterCounts = resumeGetFilterCounts(mappedResumeData);
_all: true, } else {
}, totalRecords = await ctx.prisma.resumesResume.count({
by: ['role'], where: {
where: { experience: { in: experienceFilters },
experience: { in: experienceFilters }, isResolved: isUnreviewed ? false : {},
isResolved: isUnreviewed ? false : {}, locationId: { in: locationFilters },
locationId: { in: locationFilters }, role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' }, title: { contains: searchValue, mode: 'insensitive' },
userId, userId,
}, },
}); });
const mappedRoleCounts = Object.fromEntries(
roleCounts.map((rc) => [rc.role, rc._count._all]),
);
const experienceCounts = await ctx.prisma.resumesResume.groupBy({ const roleCounts = await ctx.prisma.resumesResume.groupBy({
_count: { _count: {
_all: true, _all: true,
}, },
by: ['experience'], by: ['role'],
where: { where: {
isResolved: isUnreviewed ? false : {}, experience: { in: experienceFilters },
locationId: { in: locationFilters }, isResolved: isUnreviewed ? false : {},
role: { in: roleFilters }, locationId: { in: locationFilters },
title: { contains: searchValue, mode: 'insensitive' }, title: { contains: searchValue, mode: 'insensitive' },
userId, userId,
}, },
}); });
const mappedExperienceCounts = Object.fromEntries( const mappedRoleCounts = Object.fromEntries(
experienceCounts.map((ec) => [ec.experience, ec._count._all]), roleCounts.map((rc) => [rc.role, rc._count._all]),
); );
const locationCounts = await ctx.prisma.resumesResume.groupBy({ const experienceCounts = await ctx.prisma.resumesResume.groupBy({
_count: { _count: {
_all: true, _all: true,
}, },
by: ['locationId'], by: ['experience'],
where: { where: {
experience: { in: experienceFilters }, isResolved: isUnreviewed ? false : {},
isResolved: isUnreviewed ? false : {}, locationId: { in: locationFilters },
role: { in: roleFilters }, role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' }, title: { contains: searchValue, mode: 'insensitive' },
userId, userId,
}, },
}); });
const mappedLocationCounts = Object.fromEntries( const mappedExperienceCounts = Object.fromEntries(
locationCounts.map((lc) => [lc.locationId, lc._count._all]), experienceCounts.map((ec) => [ec.experience, ec._count._all]),
); );
const filterCounts = { const locationCounts = await ctx.prisma.resumesResume.groupBy({
experience: mappedExperienceCounts, _count: {
location: mappedLocationCounts, _all: true,
role: mappedRoleCounts, },
}; by: ['locationId'],
where: {
experience: { in: experienceFilters },
isResolved: isUnreviewed ? false : {},
role: { in: roleFilters },
title: { contains: searchValue, mode: 'insensitive' },
userId,
},
});
const mappedLocationCounts = Object.fromEntries(
locationCounts.map((lc) => [lc.locationId, lc._count._all]),
);
filterCounts = {
experience: mappedExperienceCounts,
location: mappedLocationCounts,
role: mappedRoleCounts,
};
}
return { filterCounts, mappedResumeData, totalRecords }; return { filterCounts, mappedResumeData, totalRecords };
}, },

@ -5,8 +5,10 @@ import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import { JobTitleLabels } from '~/components/shared/JobTitles'; import { JobTitleLabels } from '~/components/shared/JobTitles';
export type FilterId = 'experience' | 'location' | 'role'; export type FilterId = 'experience' | 'location' | 'role';
export type FilterCounts = Record<FilterId, Record<string, number>>;
export type CustomFilter = { export type CustomFilter = {
isTop10: boolean;
isUnreviewed: boolean; isUnreviewed: boolean;
}; };
@ -145,6 +147,7 @@ export const INITIAL_LOCATIONS: Array<TypeaheadOption> = [
export const INITIAL_FILTER_STATE: FilterState = { export const INITIAL_FILTER_STATE: FilterState = {
experience: EXPERIENCES, experience: EXPERIENCES,
isTop10: false,
isUnreviewed: true, isUnreviewed: true,
location: INITIAL_LOCATIONS, location: INITIAL_LOCATIONS,
role: INITIAL_ROLES, role: INITIAL_ROLES,
@ -185,6 +188,7 @@ export const SHORTCUTS: Array<Shortcut> = [
{ {
filters: { filters: {
...INITIAL_FILTER_STATE, ...INITIAL_FILTER_STATE,
isTop10: true,
isUnreviewed: false, isUnreviewed: false,
}, },
name: 'Top 10', name: 'Top 10',

@ -0,0 +1,39 @@
import type { Resume } from '~/types/resume';
export function resumeGetFilterCounts(data: Array<Resume>) {
const roleCounts: Record<string, number> = {};
for (let i = 0; i < data.length; i++) {
const { role } = data[i];
if (!(role in roleCounts)) {
roleCounts[role] = 1;
} else {
roleCounts[role]++;
}
}
const experienceCounts: Record<string, number> = {};
for (let i = 0; i < data.length; i++) {
const { experience } = data[i];
if (!(experience in experienceCounts)) {
experienceCounts[experience] = 1;
} else {
experienceCounts[experience]++;
}
}
const locationCounts: Record<string, number> = {};
for (let i = 0; i < data.length; i++) {
const { locationId } = data[i];
if (!(locationId in locationCounts)) {
locationCounts[locationId] = 1;
} else {
locationCounts[locationId]++;
}
}
return {
experience: experienceCounts,
location: locationCounts,
role: roleCounts,
};
}
Loading…
Cancel
Save