mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2026-04-24 18:41:52 +08:00
[resumes][feat] Add dynamic filters and unreviewed shortcut (#455)
This commit is contained in:
@@ -11,8 +11,8 @@ export const resumesRouter = createRouter()
|
||||
.query('findAll', {
|
||||
input: z.object({
|
||||
experienceFilters: z.string().array(),
|
||||
isUnreviewed: z.boolean(),
|
||||
locationFilters: z.string().array(),
|
||||
numComments: z.number().optional(),
|
||||
roleFilters: z.string().array(),
|
||||
searchValue: z.string(),
|
||||
skip: z.number(),
|
||||
@@ -25,7 +25,7 @@ export const resumesRouter = createRouter()
|
||||
locationFilters,
|
||||
experienceFilters,
|
||||
sortOrder,
|
||||
numComments,
|
||||
isUnreviewed,
|
||||
skip,
|
||||
searchValue,
|
||||
take,
|
||||
@@ -33,12 +33,8 @@ export const resumesRouter = createRouter()
|
||||
const userId = ctx.session?.user?.id;
|
||||
const totalRecords = await ctx.prisma.resumesResume.count({
|
||||
where: {
|
||||
...(numComments === 0 && {
|
||||
comments: {
|
||||
none: {},
|
||||
},
|
||||
}),
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
@@ -81,12 +77,8 @@ export const resumesRouter = createRouter()
|
||||
skip,
|
||||
take,
|
||||
where: {
|
||||
...(numComments === 0 && {
|
||||
comments: {
|
||||
none: {},
|
||||
},
|
||||
}),
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
@@ -110,7 +102,105 @@ export const resumesRouter = createRouter()
|
||||
};
|
||||
return resume;
|
||||
});
|
||||
return { mappedResumeData, totalRecords };
|
||||
|
||||
// Group by role and count, taking into account all role/experience/location/isUnreviewed filters and search value
|
||||
const roleCounts = await ctx.prisma.resumesResume.groupBy({
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
by: ['role'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
},
|
||||
});
|
||||
|
||||
// Map all nonzero counts from array to object where key = role and value = count
|
||||
const mappedRoleCounts = Object.fromEntries(
|
||||
roleCounts.map((rc) => [rc.role, rc._count._all]),
|
||||
);
|
||||
|
||||
// Filter out roles with zero counts and map to object where key = role and value = 0
|
||||
const zeroRoleCounts = Object.fromEntries(
|
||||
ROLES.filter((r) => !(r.value in mappedRoleCounts)).map((r) => [
|
||||
r.value,
|
||||
0,
|
||||
]),
|
||||
);
|
||||
|
||||
// Combine to form singular role counts object
|
||||
const processedRoleCounts = {
|
||||
...mappedRoleCounts,
|
||||
...zeroRoleCounts,
|
||||
};
|
||||
|
||||
const experienceCounts = await ctx.prisma.resumesResume.groupBy({
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
by: ['experience'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
},
|
||||
});
|
||||
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'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
},
|
||||
});
|
||||
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,
|
||||
};
|
||||
|
||||
const filterCounts = {
|
||||
Experience: processedExperienceCounts,
|
||||
Location: processedLocationCounts,
|
||||
Role: processedRoleCounts,
|
||||
};
|
||||
|
||||
return {
|
||||
filterCounts,
|
||||
mappedResumeData,
|
||||
totalRecords,
|
||||
};
|
||||
},
|
||||
})
|
||||
.query('findOne', {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { EXPERIENCES, LOCATIONS, ROLES } from '~/utils/resumes/resumeFilters';
|
||||
|
||||
import { createProtectedRouter } from '../context';
|
||||
|
||||
import type { Resume } from '~/types/resume';
|
||||
@@ -64,8 +66,8 @@ export const resumesResumeUserRouter = createProtectedRouter()
|
||||
.query('findUserStarred', {
|
||||
input: z.object({
|
||||
experienceFilters: z.string().array(),
|
||||
isUnreviewed: z.boolean(),
|
||||
locationFilters: z.string().array(),
|
||||
numComments: z.number().optional(),
|
||||
roleFilters: z.string().array(),
|
||||
searchValue: z.string(),
|
||||
skip: z.number(),
|
||||
@@ -80,19 +82,15 @@ export const resumesResumeUserRouter = createProtectedRouter()
|
||||
experienceFilters,
|
||||
searchValue,
|
||||
sortOrder,
|
||||
numComments,
|
||||
isUnreviewed,
|
||||
skip,
|
||||
take,
|
||||
} = input;
|
||||
const totalRecords = await ctx.prisma.resumesStar.count({
|
||||
where: {
|
||||
resume: {
|
||||
...(numComments === 0 && {
|
||||
comments: {
|
||||
none: {},
|
||||
},
|
||||
}),
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
@@ -144,12 +142,8 @@ export const resumesResumeUserRouter = createProtectedRouter()
|
||||
take,
|
||||
where: {
|
||||
resume: {
|
||||
...(numComments === 0 && {
|
||||
comments: {
|
||||
none: {},
|
||||
},
|
||||
}),
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
@@ -176,14 +170,116 @@ export const resumesResumeUserRouter = createProtectedRouter()
|
||||
};
|
||||
return resume;
|
||||
});
|
||||
return { mappedResumeData, totalRecords };
|
||||
|
||||
const roleCounts = await ctx.prisma.resumesResume.groupBy({
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
by: ['role'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
stars: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
},
|
||||
});
|
||||
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'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
stars: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
},
|
||||
});
|
||||
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'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
stars: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
},
|
||||
});
|
||||
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,
|
||||
};
|
||||
|
||||
const filterCounts = {
|
||||
Experience: processedExperienceCounts,
|
||||
Location: processedLocationCounts,
|
||||
Role: processedRoleCounts,
|
||||
};
|
||||
|
||||
return { filterCounts, mappedResumeData, totalRecords };
|
||||
},
|
||||
})
|
||||
.query('findUserCreated', {
|
||||
input: z.object({
|
||||
experienceFilters: z.string().array(),
|
||||
isUnreviewed: z.boolean(),
|
||||
locationFilters: z.string().array(),
|
||||
numComments: z.number().optional(),
|
||||
roleFilters: z.string().array(),
|
||||
searchValue: z.string(),
|
||||
skip: z.number(),
|
||||
@@ -198,18 +294,14 @@ export const resumesResumeUserRouter = createProtectedRouter()
|
||||
experienceFilters,
|
||||
sortOrder,
|
||||
searchValue,
|
||||
numComments,
|
||||
isUnreviewed,
|
||||
take,
|
||||
skip,
|
||||
} = input;
|
||||
const totalRecords = await ctx.prisma.resumesResume.count({
|
||||
where: {
|
||||
...(numComments === 0 && {
|
||||
comments: {
|
||||
none: {},
|
||||
},
|
||||
}),
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
@@ -250,12 +342,8 @@ export const resumesResumeUserRouter = createProtectedRouter()
|
||||
skip,
|
||||
take,
|
||||
where: {
|
||||
...(numComments === 0 && {
|
||||
comments: {
|
||||
none: {},
|
||||
},
|
||||
}),
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
@@ -280,6 +368,96 @@ export const resumesResumeUserRouter = createProtectedRouter()
|
||||
};
|
||||
return resume;
|
||||
});
|
||||
return { mappedResumeData, totalRecords };
|
||||
|
||||
const roleCounts = await ctx.prisma.resumesResume.groupBy({
|
||||
_count: {
|
||||
_all: true,
|
||||
},
|
||||
by: ['role'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
userId,
|
||||
},
|
||||
});
|
||||
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'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
userId,
|
||||
},
|
||||
});
|
||||
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'],
|
||||
where: {
|
||||
experience: { in: experienceFilters },
|
||||
isResolved: isUnreviewed ? false : {},
|
||||
location: { in: locationFilters },
|
||||
role: { in: roleFilters },
|
||||
title: { contains: searchValue, mode: 'insensitive' },
|
||||
userId,
|
||||
},
|
||||
});
|
||||
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,
|
||||
};
|
||||
|
||||
const filterCounts = {
|
||||
Experience: processedExperienceCounts,
|
||||
Location: processedLocationCounts,
|
||||
Role: processedRoleCounts,
|
||||
};
|
||||
|
||||
return { filterCounts, mappedResumeData, totalRecords };
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user