[questions][feat] add similar questions

pull/468/head
Jeff Sieu 3 years ago
parent 5adc9d4a15
commit 04464f927f

@ -1,6 +1,6 @@
import { Fragment, useState } from 'react'; import { Fragment, useState } from 'react';
import { Dialog, Transition } from '@headlessui/react'; import { Dialog, Transition } from '@headlessui/react';
import { HorizontalDivider } from '@tih/ui'; import { HorizontalDivider, useToast } from '@tih/ui';
import DiscardDraftDialog from './DiscardDraftDialog'; import DiscardDraftDialog from './DiscardDraftDialog';
import type { ContributeQuestionFormProps } from './forms/ContributeQuestionForm'; import type { ContributeQuestionFormProps } from './forms/ContributeQuestionForm';
@ -21,6 +21,8 @@ export default function ContributeQuestionDialog({
}: ContributeQuestionDialogProps) { }: ContributeQuestionDialogProps) {
const [showDiscardDialog, setShowDiscardDialog] = useState(false); const [showDiscardDialog, setShowDiscardDialog] = useState(false);
const { showToast } = useToast();
const handleDraftDiscard = () => { const handleDraftDiscard = () => {
setShowDiscardDialog(false); setShowDiscardDialog(false);
onCancel(); onCancel();
@ -75,6 +77,14 @@ export default function ContributeQuestionDialog({
<div className="mt-2"> <div className="mt-2">
<ContributeQuestionForm <ContributeQuestionForm
onDiscard={() => setShowDiscardDialog(true)} onDiscard={() => setShowDiscardDialog(true)}
onSimilarQuestionFound={() => {
onCancel();
showToast({
title:
'Your response has been recorded. Draft discarded.',
variant: 'success',
});
}}
onSubmit={(data) => { onSubmit={(data) => {
onSubmit(data); onSubmit(data);
onCancel(); onCancel();

@ -88,10 +88,12 @@ type ReceivedStatisticsProps =
type CreateEncounterProps = type CreateEncounterProps =
| { | {
createEncounterButtonText: string;
onReceivedSubmit: (data: CreateQuestionEncounterData) => void; onReceivedSubmit: (data: CreateQuestionEncounterData) => void;
showCreateEncounterButton: true; showCreateEncounterButton: true;
} }
| { | {
createEncounterButtonText?: never;
onReceivedSubmit?: never; onReceivedSubmit?: never;
showCreateEncounterButton?: false; showCreateEncounterButton?: false;
}; };
@ -132,6 +134,7 @@ export default function BaseQuestionCard({
showAnswerStatistics, showAnswerStatistics,
showReceivedStatistics, showReceivedStatistics,
showCreateEncounterButton, showCreateEncounterButton,
createEncounterButtonText,
showActionButton, showActionButton,
actionButtonLabel, actionButtonLabel,
onActionButtonClick, onActionButtonClick,
@ -238,7 +241,7 @@ export default function BaseQuestionCard({
<Button <Button
addonPosition="start" addonPosition="start"
icon={CheckIcon} icon={CheckIcon}
label="I received this too" label={createEncounterButtonText}
size="sm" size="sm"
variant="tertiary" variant="tertiary"
onClick={(event) => { onClick={(event) => {

@ -3,10 +3,10 @@ import BaseQuestionCard from './BaseQuestionCard';
export type SimilarQuestionCardProps = Omit< export type SimilarQuestionCardProps = Omit<
BaseQuestionCardProps & { BaseQuestionCardProps & {
showActionButton: true; showActionButton: false;
showAggregateStatistics: false; showAggregateStatistics: true;
showAnswerStatistics: false; showAnswerStatistics: false;
showCreateEncounterButton: false; showCreateEncounterButton: true;
showDeleteButton: false; showDeleteButton: false;
showHover: true; showHover: true;
showReceivedStatistics: false; showReceivedStatistics: false;
@ -22,26 +22,20 @@ export type SimilarQuestionCardProps = Omit<
| 'showHover' | 'showHover'
| 'showReceivedStatistics' | 'showReceivedStatistics'
| 'showVoteButtons' | 'showVoteButtons'
> & { >;
onSimilarQuestionClick: () => void;
};
export default function SimilarQuestionCard(props: SimilarQuestionCardProps) { export default function SimilarQuestionCard(props: SimilarQuestionCardProps) {
const { onSimilarQuestionClick, ...rest } = props;
return ( return (
<BaseQuestionCard <BaseQuestionCard
actionButtonLabel="Yes, this is my question" showActionButton={false}
showActionButton={true} showAggregateStatistics={true}
showAggregateStatistics={false}
showAnswerStatistics={false} showAnswerStatistics={false}
showCreateEncounterButton={false} showCreateEncounterButton={true}
showDeleteButton={false} showDeleteButton={false}
showHover={true} showHover={true}
showReceivedStatistics={false} showReceivedStatistics={false}
showVoteButtons={false} showVoteButtons={false}
onActionButtonClick={onSimilarQuestionClick} {...props}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{...(rest as any)}
/> />
); );
} }

@ -8,6 +8,7 @@ import { CheckboxInput } from '@tih/ui';
import { Button, HorizontalDivider, Select, TextArea } from '@tih/ui'; import { Button, HorizontalDivider, Select, TextArea } from '@tih/ui';
import { QUESTION_TYPES } from '~/utils/questions/constants'; import { QUESTION_TYPES } from '~/utils/questions/constants';
import relabelQuestionAggregates from '~/utils/questions/relabelQuestionAggregates';
import { import {
useFormRegister, useFormRegister,
useSelectRegister, useSelectRegister,
@ -35,12 +36,14 @@ export type ContributeQuestionData = {
export type ContributeQuestionFormProps = { export type ContributeQuestionFormProps = {
onDiscard: () => void; onDiscard: () => void;
onSimilarQuestionFound: () => void;
onSubmit: (data: ContributeQuestionData) => void; onSubmit: (data: ContributeQuestionData) => void;
}; };
export default function ContributeQuestionForm({ export default function ContributeQuestionForm({
onSubmit,
onDiscard, onDiscard,
onSimilarQuestionFound,
onSubmit,
}: ContributeQuestionFormProps) { }: ContributeQuestionFormProps) {
const { const {
control, control,
@ -61,6 +64,21 @@ export default function ContributeQuestionForm({
keepPreviousData: true, keepPreviousData: true,
}, },
); );
const utils = trpc.useContext();
const { mutateAsync: addEncounterAsync } = trpc.useMutation(
'questions.questions.encounters.user.create',
{
onSuccess: () => {
utils.invalidateQueries(
'questions.questions.encounters.getAggregatedEncounters',
);
utils.invalidateQueries('questions.questions.getQuestionById');
},
},
);
const questionContent = watch('questionContent'); const questionContent = watch('questionContent');
const register = useFormRegister(formRegister); const register = useFormRegister(formRegister);
const selectRegister = useSelectRegister(formRegister); const selectRegister = useSelectRegister(formRegister);
@ -192,24 +210,42 @@ export default function ContributeQuestionForm({
}} }}
/> />
<div className="flex flex-col gap-y-2"> <div className="flex flex-col gap-y-2">
{similarQuestions?.map((question) => ( {similarQuestions?.map((question) => {
<SimilarQuestionCard const { companyCounts, countryCounts, roleCounts } =
key={question.id} relabelQuestionAggregates(question.aggregatedQuestionEncounters);
content={question.content}
questionId={question.id} return (
timestamp={ <SimilarQuestionCard
question.seenAt.toLocaleDateString(undefined, { key={question.id}
month: 'short', companies={companyCounts}
year: 'numeric', content={question.content}
}) ?? null countries={countryCounts}
} createEncounterButtonText="Yes, this is my question"
type={question.type} questionId={question.id}
onSimilarQuestionClick={() => { roles={roleCounts}
// eslint-disable-next-line no-console timestamp={
console.log('hi!'); question.seenAt.toLocaleDateString(undefined, {
}} month: 'short',
/> year: 'numeric',
))} }) ?? null
}
type={question.type}
onReceivedSubmit={async (data) => {
await addEncounterAsync({
cityId: data.cityId,
companyId: data.company,
countryId: data.countryId,
questionId: question.id,
role: data.role,
seenAt: data.seenAt,
stateId: data.stateId,
});
onSimilarQuestionFound();
}}
/>
);
})}
{similarQuestions?.length === 0 && ( {similarQuestions?.length === 0 && (
<p className="font-semibold text-slate-900"> <p className="font-semibold text-slate-900">
No similar questions found. No similar questions found.
@ -217,7 +253,7 @@ export default function ContributeQuestionForm({
)} )}
</div> </div>
<div <div
className="bg-primary-50 flex w-full justify-end gap-y-2 py-3 shadow-[0_0_0_100vmax_theme(colors.primary.50)]" className="bg-primary-50 flex w-full justify-between gap-y-2 py-3 shadow-[0_0_0_100vmax_theme(colors.primary.50)]"
style={{ style={{
// Hack to make the background bleed outside the container // Hack to make the background bleed outside the container
clipPath: 'inset(0 -100vmax)', clipPath: 'inset(0 -100vmax)',

@ -193,6 +193,7 @@ export default function QuestionPage() {
{...question} {...question}
companies={relabeledAggregatedEncounters?.companyCounts ?? {}} companies={relabeledAggregatedEncounters?.companyCounts ?? {}}
countries={relabeledAggregatedEncounters?.countryCounts ?? {}} countries={relabeledAggregatedEncounters?.countryCounts ?? {}}
createEncounterButtonText="I received this too"
questionId={question.id} questionId={question.id}
receivedCount={undefined} receivedCount={undefined}
roles={relabeledAggregatedEncounters?.roleCounts ?? {}} roles={relabeledAggregatedEncounters?.roleCounts ?? {}}

@ -1,8 +1,6 @@
import { z } from 'zod'; import { z } from 'zod';
import { TRPCError } from '@trpc/server'; import { TRPCError } from '@trpc/server';
import { JobTitleLabels } from '~/components/shared/JobTitles';
import { createProtectedRouter } from '../context'; import { createProtectedRouter } from '../context';
import { SortOrder } from '~/types/questions.d'; import { SortOrder } from '~/types/questions.d';
@ -14,7 +12,7 @@ export const questionsQuestionEncounterUserRouter = createProtectedRouter()
companyId: z.string(), companyId: z.string(),
countryId: z.string(), countryId: z.string(),
questionId: z.string(), questionId: z.string(),
role: z.nativeEnum(JobTitleLabels), role: z.string(),
seenAt: z.date(), seenAt: z.date(),
stateId: z.string().nullish(), stateId: z.string().nullish(),
}), }),

Loading…
Cancel
Save