[questions][feat] add similar questions ui

pull/468/head
Jeff Sieu 3 years ago
parent d2a8ede590
commit c52f24ecc9

@ -48,6 +48,20 @@ type AnswerStatisticsProps =
showAnswerStatistics?: false;
};
type AggregateStatisticsProps =
| {
companies: Record<string, number>;
locations: Record<string, number>;
roles: Record<string, number>;
showAggregateStatistics: true;
}
| {
companies?: never;
locations?: never;
roles?: never;
showAggregateStatistics?: false;
};
type ActionButtonProps =
| {
actionButtonLabel: string;
@ -90,16 +104,14 @@ type AddToListProps =
export type BaseQuestionCardProps = ActionButtonProps &
AddToListProps &
AggregateStatisticsProps &
AnswerStatisticsProps &
CreateEncounterProps &
DeleteProps &
ReceivedStatisticsProps &
UpvoteProps & {
companies: Record<string, number>;
content: string;
locations: Record<string, number>;
questionId: string;
roles: Record<string, number>;
showHover?: boolean;
timestamp: string;
truncateContent?: boolean;
@ -114,6 +126,7 @@ export default function BaseQuestionCard({
receivedCount,
type,
showVoteButtons,
showAggregateStatistics,
showAnswerStatistics,
showReceivedStatistics,
showCreateEncounterButton,
@ -145,12 +158,22 @@ export default function BaseQuestionCard({
/>
)}
<div className="flex flex-col items-start gap-2">
<div className="flex items-baseline justify-between">
<div className="flex items-baseline justify-between self-stretch">
<div className="flex items-center gap-2 text-slate-500">
<QuestionTypeBadge type={type} />
<QuestionAggregateBadge statistics={companies} variant="primary" />
<QuestionAggregateBadge statistics={locations} variant="success" />
<QuestionAggregateBadge statistics={roles} variant="danger" />
{showAggregateStatistics && (
<>
<QuestionTypeBadge type={type} />
<QuestionAggregateBadge
statistics={companies}
variant="primary"
/>
<QuestionAggregateBadge
statistics={locations}
variant="success"
/>
<QuestionAggregateBadge statistics={roles} variant="danger" />
</>
)}
<p className="text-xs">{timestamp}</p>
{showAddToList && (
<div className="pl-4">
@ -162,7 +185,7 @@ export default function BaseQuestionCard({
<Button
label={actionButtonLabel}
size="sm"
variant="tertiary"
variant="secondary"
onClick={onActionButtonClick}
/>
)}

@ -5,6 +5,7 @@ export type QuestionOverviewCardProps = Omit<
BaseQuestionCardProps & {
showActionButton: false;
showAddToList: true;
showAggregateStatistics: true;
showAnswerStatistics: false;
showCreateEncounterButton: true;
showDeleteButton: false;
@ -15,6 +16,7 @@ export type QuestionOverviewCardProps = Omit<
| 'onActionButtonClick'
| 'showActionButton'
| 'showAddToList'
| 'showAggregateStatistics'
| 'showAnswerStatistics'
| 'showCreateEncounterButton'
| 'showDeleteButton'
@ -28,6 +30,7 @@ export default function FullQuestionCard(props: QuestionOverviewCardProps) {
{...props}
showActionButton={false}
showAddToList={true}
showAggregateStatistics={true}
showAnswerStatistics={false}
showCreateEncounterButton={true}
showReceivedStatistics={false}

@ -6,6 +6,7 @@ import BaseQuestionCard from './BaseQuestionCard';
export type QuestionListCardProps = Omit<
BaseQuestionCardProps & {
showActionButton: false;
showAggregateStatistics: true;
showAnswerStatistics: false;
showDeleteButton: true;
showVoteButtons: false;
@ -13,6 +14,7 @@ export type QuestionListCardProps = Omit<
| 'actionButtonLabel'
| 'onActionButtonClick'
| 'showActionButton'
| 'showAggregateStatistics'
| 'showAnswerStatistics'
| 'showDeleteButton'
| 'showVoteButtons'
@ -24,6 +26,7 @@ function QuestionListCardWithoutHref(props: QuestionListCardProps) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{...(props as any)}
showActionButton={false}
showAggregateStatistics={true}
showAnswerStatistics={false}
showDeleteButton={true}
showHover={true}

@ -6,6 +6,7 @@ import BaseQuestionCard from './BaseQuestionCard';
export type QuestionOverviewCardProps = Omit<
BaseQuestionCardProps & {
showActionButton: false;
showAggregateStatistics: true;
showAnswerStatistics: true;
showCreateEncounterButton: false;
showDeleteButton: false;
@ -16,6 +17,7 @@ export type QuestionOverviewCardProps = Omit<
| 'onActionButtonClick'
| 'onDelete'
| 'showActionButton'
| 'showAggregateStatistics'
| 'showAnswerStatistics'
| 'showCreateEncounterButton'
| 'showDeleteButton'
@ -28,6 +30,7 @@ function QuestionOverviewCardWithoutHref(props: QuestionOverviewCardProps) {
<BaseQuestionCard
{...props}
showActionButton={false}
showAggregateStatistics={true}
showAnswerStatistics={true}
showCreateEncounterButton={false}
showDeleteButton={false}

@ -4,7 +4,8 @@ import BaseQuestionCard from './BaseQuestionCard';
export type SimilarQuestionCardProps = Omit<
BaseQuestionCardProps & {
showActionButton: true;
showAnswerStatistics: true;
showAggregateStatistics: false;
showAnswerStatistics: false;
showCreateEncounterButton: false;
showDeleteButton: false;
showHover: true;
@ -14,6 +15,7 @@ export type SimilarQuestionCardProps = Omit<
| 'actionButtonLabel'
| 'onActionButtonClick'
| 'showActionButton'
| 'showAggregateStatistics'
| 'showAnswerStatistics'
| 'showCreateEncounterButton'
| 'showDeleteButton'
@ -30,12 +32,13 @@ export default function SimilarQuestionCard(props: SimilarQuestionCardProps) {
<BaseQuestionCard
actionButtonLabel="Yes, this is my question"
showActionButton={true}
showAnswerStatistics={true}
showAggregateStatistics={false}
showAnswerStatistics={false}
showCreateEncounterButton={false}
showDeleteButton={false}
showHover={true}
showReceivedStatistics={true}
showVoteButtons={true}
showReceivedStatistics={false}
showVoteButtons={false}
onActionButtonClick={onSimilarQuestionClick}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{...(rest as any)}

@ -1,6 +1,7 @@
import { startOfMonth } from 'date-fns';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { ArrowPathIcon } from '@heroicons/react/20/solid';
import type { QuestionsQuestionType } from '@prisma/client';
import {
Button,
@ -15,7 +16,9 @@ import {
useFormRegister,
useSelectRegister,
} from '~/utils/questions/useFormRegister';
import { trpc } from '~/utils/trpc';
import SimilarQuestionCard from '../card/question/SimilarQuestionCard';
import CompanyTypeahead from '../typeahead/CompanyTypeahead';
import LocationTypeahead from '../typeahead/LocationTypeahead';
import RoleTypeahead from '../typeahead/RoleTypeahead';
@ -45,18 +48,39 @@ export default function ContributeQuestionForm({
control,
register: formRegister,
handleSubmit,
watch,
} = useForm<ContributeQuestionData>({
defaultValues: {
date: startOfMonth(new Date()),
},
});
const [contentToCheck, setContentToCheck] = useState('');
const { data: similarQuestions } = trpc.useQuery(
[
'questions.questions.getRelatedQuestionsByContent',
{ content: contentToCheck },
],
{
keepPreviousData: true,
},
);
const questionContent = watch('questionContent');
const register = useFormRegister(formRegister);
const selectRegister = useSelectRegister(formRegister);
const [canSubmit, setCanSubmit] = useState<boolean>(false);
const [checkedSimilar, setCheckedSimilar] = useState<boolean>(false);
const handleCheckSimilarQuestions = (checked: boolean) => {
setCanSubmit(checked);
setCheckedSimilar(checked);
};
useEffect(() => {
if (questionContent !== contentToCheck) {
setCheckedSimilar(false);
}
}, [questionContent, contentToCheck]);
return (
<div className="flex flex-col justify-between gap-4">
<form
@ -153,35 +177,55 @@ export default function ContributeQuestionForm({
/>
</div>
</div>
{/* <div className="w-full">
<HorizontalDivider />
</div>
<h1 className="mb-3">
Are these questions the same as yours? TODO:Change to list
</h1>
<div>
<SimilarQuestionCard
content="Given an array of integers nums and an integer target, return indices of the two numbers such that they add up. Given an array of integers nums and an integer target, return indices"
location="Menlo Park, CA"
receivedCount={0}
role="Senior Engineering Manager"
timestamp="Today"
onSimilarQuestionClick={() => {
// eslint-disable-next-line no-console
console.log('hi!');
<div className="w-full">
<HorizontalDivider />
</div>
<h2
className="text-primary-900 mb-3
text-lg font-semibold
">
Are these questions the same as yours?
</h2>
<Button
addonPosition="start"
disabled={questionContent === contentToCheck}
icon={ArrowPathIcon}
label="Refresh similar questions"
variant="primary"
onClick={() => {
setContentToCheck(questionContent);
}}
/>
</div> */}
<div className="flex flex-col gap-y-2">
{similarQuestions?.map((question) => (
<SimilarQuestionCard
key={question.id}
content="Given an array of integers nums and an integer target, return indices of the two numbers such that they add up. Given an array of integers nums and an integer target, return indices"
questionId={question.id}
timestamp="Today"
type="CODING"
onSimilarQuestionClick={() => {
// eslint-disable-next-line no-console
console.log('hi!');
}}
/>
))}
</div>
<div
className="bg-primary-50 flex w-full flex-col gap-y-2 py-3 shadow-[0_0_0_100vmax_theme(colors.primary.50)] sm:flex-row sm:justify-between"
style={{
// Hack to make the background bleed outside the container
clipPath: 'inset(0 -100vmax)',
}}>
<div className="my-2 flex sm:my-0">
<div className="my-2 flex items-center sm:my-0">
<CheckboxInput
label="I have checked that my question is new"
value={canSubmit}
disabled={questionContent !== contentToCheck}
label={
questionContent !== contentToCheck
? 'I have checked that my question is new (Refresh similar questions to proceed)'
: 'I have checked that my question is new'
}
value={checkedSimilar}
onChange={handleCheckSimilarQuestions}
/>
</div>
@ -194,7 +238,7 @@ export default function ContributeQuestionForm({
</button>
<Button
className="bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 inline-flex w-full justify-center rounded-md border border-transparent px-4 py-2 text-base font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:bg-slate-400 sm:ml-3 sm:w-auto sm:text-sm"
disabled={!canSubmit}
disabled={!checkedSimilar}
label="Contribute"
type="submit"
variant="primary"></Button>

@ -1,4 +1,5 @@
import { z } from 'zod';
import type { QuestionsQuestion } from '@prisma/client';
import { QuestionsQuestionType, Vote } from '@prisma/client';
import { TRPCError } from '@trpc/server';
@ -207,14 +208,18 @@ export const questionsQuestionRouter = createProtectedRouter()
.split(/\s+/)
.join(' | ');
const relatedQuestions = await ctx.prisma.$queryRaw`
const relatedQuestions = (await ctx.prisma.$queryRaw`
SELECT * FROM "QuestionsQuestion"
WHERE
"contentSearch" @@ to_tsquery('english', ${query})
ORDER BY ts_rank("textSearch", to_tsquery('english', ${query})) DESC
`;
WHERE "contentSearch" @@ to_tsquery(${query})
ORDER BY ts_rank("contentSearch", to_tsquery(${query})) DESC
`) as Array<QuestionsQuestion>;
return relatedQuestions;
// Dummy data to make this return something
return await ctx.prisma.questionsQuestion.findMany({
take: 5,
});
// Return relatedQuestions;
},
})
.mutation('create', {

Loading…
Cancel
Save