mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-27 20:22:33 +08:00
[questions][feat] update question filter (#384)
* [questions][chore] refactor question queries * [questions][chore] destructure values from input * [questions][feat] add sorting * [question][fix] fix frontend * [questions][feat] add sorting * [questions][feat] add sorting index * [questions][chore] push migration file * [questions][fix] fix ci issues * [questions][fix] fix import errors Co-authored-by: Jeff Sieu <jeffsy00@gmail.com>
This commit is contained in:
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `upvotes` to the `QuestionsQuestion` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "QuestionsQuestion" ADD COLUMN "lastSeenAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "upvotes" INTEGER NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "QuestionsQuestionEncounter" ADD COLUMN "netVotes" INTEGER NOT NULL DEFAULT 0;
|
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `netVotes` on the `QuestionsQuestionEncounter` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "QuestionsQuestion" ALTER COLUMN "lastSeenAt" DROP DEFAULT,
|
||||
ALTER COLUMN "upvotes" SET DEFAULT 0;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "QuestionsQuestionEncounter" DROP COLUMN "netVotes";
|
@ -0,0 +1,5 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX "QuestionsQuestion_lastSeenAt_id_idx" ON "QuestionsQuestion"("lastSeenAt", "id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "QuestionsQuestion_upvotes_id_idx" ON "QuestionsQuestion"("upvotes", "id");
|
@ -404,6 +404,8 @@ model QuestionsQuestion {
|
||||
userId String?
|
||||
content String @db.Text
|
||||
questionType QuestionsQuestionType
|
||||
lastSeenAt DateTime
|
||||
upvotes Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@ -412,6 +414,9 @@ model QuestionsQuestion {
|
||||
votes QuestionsQuestionVote[]
|
||||
comments QuestionsQuestionComment[]
|
||||
answers QuestionsAnswer[]
|
||||
|
||||
@@index([lastSeenAt, id])
|
||||
@@index([upvotes, id])
|
||||
}
|
||||
|
||||
model QuestionsQuestionEncounter {
|
||||
|
@ -26,6 +26,8 @@ import {
|
||||
} from '~/utils/questions/useSearchFilter';
|
||||
import { trpc } from '~/utils/trpc';
|
||||
|
||||
import { SortOrder, SortType } from '~/types/questions.d';
|
||||
|
||||
export default function QuestionsHomePage() {
|
||||
const router = useRouter();
|
||||
|
||||
@ -70,6 +72,9 @@ export default function QuestionsHomePage() {
|
||||
locations: selectedLocations,
|
||||
questionTypes: selectedQuestionTypes,
|
||||
roles: [],
|
||||
// TODO: Implement sort order and sort type choices
|
||||
sortOrder: SortOrder.DESC,
|
||||
sortType: SortType.NEW,
|
||||
startDate,
|
||||
},
|
||||
],
|
||||
|
@ -5,18 +5,33 @@ import { TRPCError } from '@trpc/server';
|
||||
import { createProtectedRouter } from './context';
|
||||
|
||||
import type { Question } from '~/types/questions';
|
||||
import { SortOrder, SortType } from '~/types/questions.d';
|
||||
|
||||
const TWO_WEEK_IN_MS = 12096e5;
|
||||
|
||||
export const questionsQuestionRouter = createProtectedRouter()
|
||||
.query('getQuestionsByFilter', {
|
||||
input: z.object({
|
||||
companyNames: z.string().array(),
|
||||
endDate: z.date(),
|
||||
endDate: z.date().default(new Date()),
|
||||
locations: z.string().array(),
|
||||
pageSize: z.number().default(50),
|
||||
questionTypes: z.nativeEnum(QuestionsQuestionType).array(),
|
||||
roles: z.string().array(),
|
||||
startDate: z.date().optional(),
|
||||
sortOrder: z.nativeEnum(SortOrder),
|
||||
sortType: z.nativeEnum(SortType),
|
||||
startDate: z.date().default(new Date(Date.now() - TWO_WEEK_IN_MS)),
|
||||
}),
|
||||
async resolve({ ctx, input }) {
|
||||
const sortCondition =
|
||||
input.sortType === SortType.TOP
|
||||
? {
|
||||
upvotes: input.sortOrder,
|
||||
}
|
||||
: {
|
||||
lastSeenAt: input.sortOrder,
|
||||
};
|
||||
|
||||
const questionsData = await ctx.prisma.questionsQuestion.findMany({
|
||||
include: {
|
||||
_count: {
|
||||
@ -41,7 +56,7 @@ export const questionsQuestionRouter = createProtectedRouter()
|
||||
votes: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
...sortCondition,
|
||||
},
|
||||
where: {
|
||||
...(input.questionTypes.length > 0
|
||||
@ -53,6 +68,10 @@ export const questionsQuestionRouter = createProtectedRouter()
|
||||
: {}),
|
||||
encounters: {
|
||||
some: {
|
||||
seenAt: {
|
||||
gte: input.startDate,
|
||||
lte: input.endDate,
|
||||
},
|
||||
...(input.companyNames.length > 0
|
||||
? {
|
||||
company: {
|
||||
@ -204,24 +223,23 @@ export const questionsQuestionRouter = createProtectedRouter()
|
||||
data: {
|
||||
content: input.content,
|
||||
encounters: {
|
||||
create: [
|
||||
{
|
||||
company: {
|
||||
connect: {
|
||||
id: input.companyId,
|
||||
},
|
||||
},
|
||||
location: input.location,
|
||||
role: input.role,
|
||||
seenAt: input.seenAt,
|
||||
user: {
|
||||
connect: {
|
||||
id: userId,
|
||||
},
|
||||
create: {
|
||||
company: {
|
||||
connect: {
|
||||
id: input.companyId,
|
||||
},
|
||||
},
|
||||
],
|
||||
location: input.location,
|
||||
role: input.role,
|
||||
seenAt: input.seenAt,
|
||||
user: {
|
||||
connect: {
|
||||
id: userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
lastSeenAt: input.seenAt,
|
||||
questionType: input.questionType,
|
||||
userId,
|
||||
},
|
||||
@ -316,13 +334,28 @@ export const questionsQuestionRouter = createProtectedRouter()
|
||||
const userId = ctx.session?.user?.id;
|
||||
const { questionId, vote } = input;
|
||||
|
||||
return await ctx.prisma.questionsQuestionVote.create({
|
||||
data: {
|
||||
questionId,
|
||||
userId,
|
||||
vote,
|
||||
},
|
||||
});
|
||||
const incrementValue = vote === Vote.UPVOTE ? 1 : -1;
|
||||
|
||||
const [questionVote] = await ctx.prisma.$transaction([
|
||||
ctx.prisma.questionsQuestionVote.create({
|
||||
data: {
|
||||
questionId,
|
||||
userId,
|
||||
vote,
|
||||
},
|
||||
}),
|
||||
ctx.prisma.questionsQuestion.update({
|
||||
data: {
|
||||
upvotes: {
|
||||
increment: incrementValue,
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: questionId,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
return questionVote;
|
||||
},
|
||||
})
|
||||
.mutation('updateVote', {
|
||||
@ -347,14 +380,30 @@ export const questionsQuestionRouter = createProtectedRouter()
|
||||
});
|
||||
}
|
||||
|
||||
return await ctx.prisma.questionsQuestionVote.update({
|
||||
data: {
|
||||
vote,
|
||||
},
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
const incrementValue = vote === Vote.UPVOTE ? 2 : -2;
|
||||
|
||||
const [questionVote] = await ctx.prisma.$transaction([
|
||||
ctx.prisma.questionsQuestionVote.update({
|
||||
data: {
|
||||
vote,
|
||||
},
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
}),
|
||||
ctx.prisma.questionsQuestion.update({
|
||||
data: {
|
||||
upvotes: {
|
||||
increment: incrementValue,
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: voteToUpdate.questionId,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return questionVote;
|
||||
},
|
||||
})
|
||||
.mutation('deleteVote', {
|
||||
@ -377,10 +426,25 @@ export const questionsQuestionRouter = createProtectedRouter()
|
||||
});
|
||||
}
|
||||
|
||||
return await ctx.prisma.questionsQuestionVote.delete({
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
});
|
||||
const incrementValue = voteToDelete.vote === Vote.UPVOTE ? -1 : 1;
|
||||
|
||||
const [questionVote] = await ctx.prisma.$transaction([
|
||||
ctx.prisma.questionsQuestionVote.delete({
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
}),
|
||||
ctx.prisma.questionsQuestion.update({
|
||||
data: {
|
||||
upvotes: {
|
||||
increment: incrementValue,
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: voteToDelete.questionId,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
return questionVote;
|
||||
},
|
||||
});
|
||||
|
12
apps/portal/src/types/questions.d.ts
vendored
12
apps/portal/src/types/questions.d.ts
vendored
@ -20,7 +20,7 @@ export type AggregatedQuestionEncounter = {
|
||||
companyCounts: Record<string, number>;
|
||||
locationCounts: Record<string, number>;
|
||||
roleCounts: Record<string, number>;
|
||||
}
|
||||
};
|
||||
|
||||
export type AnswerComment = {
|
||||
content: string;
|
||||
@ -50,3 +50,13 @@ export type QuestionComment = {
|
||||
user: string;
|
||||
userImage: string;
|
||||
};
|
||||
|
||||
export enum SortOrder {
|
||||
ASC = 'asc',
|
||||
DESC = 'desc',
|
||||
}
|
||||
|
||||
export enum SortType {
|
||||
TOP,
|
||||
NEW,
|
||||
}
|
||||
|
Reference in New Issue
Block a user