[questions][ui] add similar question card

pull/346/head
Jeff Sieu 3 years ago committed by wlren
parent e0addf3b77
commit 4adc6a5013

@ -10,7 +10,7 @@ import {
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { Button, TextInput } from '@tih/ui'; import { Button, TextInput } from '@tih/ui';
import ContributeQuestionModal from './ContributeQuestionModal'; import ContributeQuestionDialog from './ContributeQuestionDialog';
export type ContributeQuestionData = { export type ContributeQuestionData = {
company: string; company: string;
@ -48,7 +48,12 @@ export default function ContributeQuestionCard({
onSubmit, onSubmit,
}: ContributeQuestionCardProps) { }: ContributeQuestionCardProps) {
const { register, handleSubmit } = useForm<ContributeQuestionData>(); const { register, handleSubmit } = useForm<ContributeQuestionData>();
const [isOpen, setOpen] = useState<boolean>(false); const [showDraftDialog, setShowDraftDialog] = useState(false);
const handleDraftDialogCancel = () => {
setShowDraftDialog(false);
};
return ( return (
<> <>
<form <form
@ -89,14 +94,14 @@ export default function ContributeQuestionCard({
label="Contribute" label="Contribute"
type="submit" type="submit"
variant="primary" variant="primary"
onClick={() => setOpen(true)} onClick={() => setShowDraftDialog(true)}
/> />
</div> </div>
</form> </form>
<ContributeQuestionDialog
<ContributeQuestionModal show={showDraftDialog}
contributeState={isOpen} onCancel={handleDraftDialogCancel}
setContributeState={setOpen}></ContributeQuestionModal> />
</> </>
); );
} }

@ -1,39 +1,38 @@
import type { Dispatch, SetStateAction } from 'react';
import { Fragment, useState } from 'react'; import { Fragment, useState } from 'react';
import { Dialog, Transition } from '@headlessui/react'; import { Dialog, Transition } from '@headlessui/react';
import ContributeQuestionForm from './ContributeQuestionForm'; import ContributeQuestionForm from './ContributeQuestionForm';
import DiscardDraftModal from './DiscardDraftModal'; import DiscardDraftDialog from './DiscardDraftDialog';
export type ContributeQuestionModalProps = { export type ContributeQuestionDialogProps = {
contributeState: boolean; onCancel: () => void;
setContributeState: Dispatch<SetStateAction<boolean>>; show: boolean;
}; };
export default function ContributeQuestionModal({ export default function ContributeQuestionDialog({
contributeState, show,
setContributeState, onCancel,
}: ContributeQuestionModalProps) { }: ContributeQuestionDialogProps) {
const [isDiscardOpen, setIsDiscardOpen] = useState<boolean>(false); const [showDiscardDialog, setShowDiscardDialog] = useState(false);
const handleDiscardDraft = () => { const handleDraftDiscard = () => {
// eslint-disable-next-line no-console setShowDiscardDialog(false);
console.log('discarded draft'); onCancel();
setContributeState(false);
setIsDiscardOpen(false);
}; };
const handleCancelDiscard = () => { const handleDiscardCancel = () => {
setIsDiscardOpen(false); setShowDiscardDialog(false);
}; };
return ( return (
<div> <div>
<Transition.Root as={Fragment} show={contributeState}> <Transition.Root as={Fragment} show={show}>
<Dialog <Dialog
as="div" as="div"
className="relative z-10" className="relative z-10"
onClose={() => setContributeState(false)}> onClose={() => {
// Todo: save state
}}>
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
@ -65,7 +64,7 @@ export default function ContributeQuestionModal({
</Dialog.Title> </Dialog.Title>
<div className="mt-2"> <div className="mt-2">
<ContributeQuestionForm <ContributeQuestionForm
onDiscard={() => setIsDiscardOpen(true)} onDiscard={() => setShowDiscardDialog(true)}
onSubmit={(data) => { onSubmit={(data) => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(data); console.log(data);
@ -81,10 +80,10 @@ export default function ContributeQuestionModal({
</div> </div>
</Dialog> </Dialog>
</Transition.Root> </Transition.Root>
<DiscardDraftModal <DiscardDraftDialog
handleCancelDiscard={handleCancelDiscard} show={showDiscardDialog}
handleDiscardDraft={handleDiscardDraft} onCancel={handleDiscardCancel}
isDiscardOpen={isDiscardOpen}></DiscardDraftModal> onDiscard={handleDraftDiscard}></DiscardDraftDialog>
</div> </div>
); );
} }

@ -7,8 +7,15 @@ import {
QuestionMarkCircleIcon, QuestionMarkCircleIcon,
UserIcon, UserIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { Button, HorizontalDivider, TextArea, TextInput } from '@tih/ui'; import {
Button,
Collapsible,
HorizontalDivider,
TextArea,
TextInput,
} from '@tih/ui';
import SimilarQuestionCard from './card/SimilarQuestionCard';
import Checkbox from './ui-patch/Checkbox'; import Checkbox from './ui-patch/Checkbox';
export type ContributeQuestionData = { export type ContributeQuestionData = {
@ -50,7 +57,7 @@ export default function ContributeQuestionForm({
return ( return (
<form <form
className="flex flex-col items-stretch justify-center gap-2" className="flex flex-col items-stretch justify-center gap-2 pb-[100px]"
onSubmit={handleSubmit(onSubmit)}> onSubmit={handleSubmit(onSubmit)}>
<TextArea <TextArea
isLabelHidden={true} isLabelHidden={true}
@ -59,7 +66,7 @@ export default function ContributeQuestionForm({
rows={5} rows={5}
{...register('questionContent')} {...register('questionContent')}
/> />
<div className="flex flex-wrap items-end justify-center gap-x-2"> <div className="flex flex-wrap items-end justify-center gap-2">
<div className="min-w-[150px] flex-1"> <div className="min-w-[150px] flex-1">
<TextInput <TextInput
label="Company" label="Company"
@ -85,10 +92,8 @@ export default function ContributeQuestionForm({
/> />
</div> </div>
</div> </div>
<div className="w-full"> <Collapsible defaultOpen={true} label="Additional info">
<HorizontalDivider /> <div className="flex flex-wrap items-end justify-center gap-2">
</div>
<div className="flex flex-wrap items-end justify-center gap-x-2">
<div className="min-w-[150px] flex-1"> <div className="min-w-[150px] flex-1">
<TextInput <TextInput
label="Location" label="Location"
@ -105,8 +110,13 @@ export default function ContributeQuestionForm({
{...register('position')} {...register('position')}
/> />
</div> </div>
{/* <Button label="Contribute" type="submit" variant="primary" /> */}
</div> </div>
<div className="bg-primary-50 px-4 py-3 sm:flex sm:flex-row sm:justify-between sm:px-6"> </Collapsible>
<div className="w-full">
<HorizontalDivider />
</div>
<div className="bg-primary-50 fixed bottom-0 left-0 w-full px-4 py-3 sm:flex sm:flex-row sm:justify-between sm:px-6">
<div className="mb-1 flex"> <div className="mb-1 flex">
<Checkbox <Checkbox
checked={canSubmit} checked={canSubmit}
@ -128,6 +138,20 @@ export default function ContributeQuestionForm({
variant="primary"></Button> variant="primary"></Button>
</div> </div>
</div> </div>
<h1>Are these questions the same as yours?</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"
role="Senior Engineering Manager"
similarCount={0}
timestamp="Today"
onSimilarQuestionClick={() => {
// eslint-disable-next-line no-console
console.log('hi!');
}}
/>
</div>
</form> </form>
); );
} }

@ -2,19 +2,19 @@ import { Fragment } from 'react';
import { Dialog, Transition } from '@headlessui/react'; import { Dialog, Transition } from '@headlessui/react';
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'; import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
export type DiscardDraftModalProps = { export type DiscardDraftDialogProps = {
handleCancelDiscard: () => void; onCancel: () => void;
handleDiscardDraft: () => void; onDiscard: () => void;
isDiscardOpen: boolean; show: boolean;
}; };
export default function DiscardDraftModal({ export default function DiscardDraftDialog({
isDiscardOpen, show,
handleCancelDiscard, onCancel,
handleDiscardDraft, onDiscard,
}: DiscardDraftModalProps) { }: DiscardDraftDialogProps) {
return ( return (
<Transition.Root as={Fragment} show={isDiscardOpen}> <Transition.Root as={Fragment} show={show}>
<Dialog as="div" className="relative z-10" onClose={handleCancelDiscard}> <Dialog as="div" className="relative z-10" onClose={onCancel}>
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
@ -62,13 +62,13 @@ export default function DiscardDraftModal({
<button <button
className="inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm" className="inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
type="button" type="button"
onClick={handleDiscardDraft}> onClick={onDiscard}>
Discard Discard
</button> </button>
<button <button
className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm" className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm"
type="button" type="button"
onClick={handleCancelDiscard}> onClick={onCancel}>
Cancel Cancel
</button> </button>
</div> </div>

@ -1,72 +0,0 @@
import {
ChatBubbleBottomCenterTextIcon,
ChevronDownIcon,
ChevronUpIcon,
EyeIcon,
} from '@heroicons/react/24/outline';
import { Badge, Button } from '@tih/ui';
export type QuestionOverviewCardProps = {
answerCount: number;
content: string;
location: string;
role: string;
similarCount: number;
timestamp: string;
upvoteCount: number;
};
export default function QuestionOverviewCard({
answerCount,
content,
similarCount,
upvoteCount,
timestamp,
role,
location,
}: QuestionOverviewCardProps) {
return (
<article className="flex gap-2 rounded-md border border-slate-300 p-4">
<div className="flex flex-col items-center">
<Button
icon={ChevronUpIcon}
isLabelHidden={true}
label="Upvote"
variant="tertiary"
/>
<p>{upvoteCount}</p>
<Button
icon={ChevronDownIcon}
isLabelHidden={true}
label="Downvote"
variant="tertiary"
/>
</div>
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2 text-slate-500">
<Badge label="Technical" variant="primary" />
<p className="text-xs">
{timestamp} · {location} · {role}
</p>
</div>
<p className="line-clamp-2 text-ellipsis">{content}</p>
<div className="flex gap-2">
<Button
addonPosition="start"
icon={ChatBubbleBottomCenterTextIcon}
label={`${answerCount} answers`}
size="sm"
variant="tertiary"
/>
<Button
addonPosition="start"
icon={EyeIcon}
label={`${similarCount} received this`}
size="sm"
variant="tertiary"
/>
</div>
</div>
</article>
);
}

@ -0,0 +1,122 @@
import {
ChatBubbleBottomCenterTextIcon,
ChevronDownIcon,
ChevronUpIcon,
EyeIcon,
} from '@heroicons/react/24/outline';
import { Badge, Button } from '@tih/ui';
type UpvoteProps =
| {
showVoteButtons: true;
upvoteCount: number;
}
| {
showVoteButtons?: false;
upvoteCount?: never;
};
type StatisticsProps =
| {
answerCount: number;
showUserStatistics: true;
}
| {
answerCount?: never;
showUserStatistics?: false;
};
type ActionButtonProps =
| {
actionButtonLabel: string;
onActionButtonClick: () => void;
showActionButton: true;
}
| {
actionButtonLabel?: never;
onActionButtonClick?: never;
showActionButton?: false;
};
export type QuestionCardProps = ActionButtonProps &
StatisticsProps &
UpvoteProps & {
content: string;
location: string;
role: string;
similarCount: number;
timestamp: string;
};
export default function QuestionCard({
answerCount,
content,
similarCount,
showVoteButtons,
showUserStatistics,
showActionButton,
actionButtonLabel,
onActionButtonClick,
upvoteCount,
timestamp,
role,
location,
}: QuestionCardProps) {
return (
<article className="flex gap-2 rounded-md border border-slate-300 p-4">
{showVoteButtons && (
<div className="flex flex-col items-center">
<Button
icon={ChevronUpIcon}
isLabelHidden={true}
label="Upvote"
variant="tertiary"
/>
<p>{upvoteCount}</p>
<Button
icon={ChevronDownIcon}
isLabelHidden={true}
label="Downvote"
variant="tertiary"
/>
</div>
)}
<div className="flex flex-col gap-2">
<div className="flex items-start justify-between">
<div className="flex items-center gap-2 text-slate-500">
<Badge label="Technical" variant="primary" />
<p className="text-xs">
{timestamp} · {location} · {role}
</p>
</div>
{showActionButton && (
<Button
label={actionButtonLabel}
variant="tertiary"
onClick={onActionButtonClick}
/>
)}
</div>
<p className="line-clamp-2 text-ellipsis">{content}</p>
{showUserStatistics && (
<div className="flex gap-2">
<Button
addonPosition="start"
icon={ChatBubbleBottomCenterTextIcon}
label={`${answerCount} answers`}
size="sm"
variant="tertiary"
/>
<Button
addonPosition="start"
icon={EyeIcon}
label={`${similarCount} received this`}
size="sm"
variant="tertiary"
/>
</div>
)}
</div>
</article>
);
}

@ -0,0 +1,28 @@
import type { QuestionCardProps } from './QuestionCard';
import QuestionCard from './QuestionCard';
export type QuestionOverviewCardProps = Required<
Omit<
QuestionCardProps & {
showActionButton: false;
showUserStatistics: true;
showVoteButtons: true;
},
| 'actionButtonLabel'
| 'onActionButtonClick'
| 'showActionButton'
| 'showUserStatistics'
| 'showVoteButtons'
>
>;
export default function QuestionOverviewCard(props: QuestionOverviewCardProps) {
return (
<QuestionCard
{...props}
showActionButton={false}
showUserStatistics={true}
showVoteButtons={true}
/>
);
}

@ -0,0 +1,33 @@
import type { QuestionCardProps } from './QuestionCard';
import QuestionCard from './QuestionCard';
export type SimilarQuestionCardProps = Required<
Omit<
QuestionCardProps & {
showActionButton: true;
showUserStatistics: false;
showVoteButtons: false;
},
| 'actionButtonLabel'
| 'answerCount'
| 'onActionButtonClick'
| 'showActionButton'
| 'showUserStatistics'
| 'showVoteButtons'
| 'upvoteCount'
>
> & {
onSimilarQuestionClick: () => void;
};
export default function SimilarQuestionCard(props: SimilarQuestionCardProps) {
const { onSimilarQuestionClick, ...rest } = props;
return (
<QuestionCard
{...rest}
actionButtonLabel="Yes, this is my question"
showActionButton={true}
onActionButtonClick={onSimilarQuestionClick}
/>
);
}

@ -1,9 +1,9 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import QuestionOverviewCard from '~/components/questions/card/QuestionOverviewCard';
import ContributeQuestionCard from '~/components/questions/ContributeQuestionCard'; import ContributeQuestionCard from '~/components/questions/ContributeQuestionCard';
import type { FilterOptions } from '~/components/questions/filter/FilterSection'; import type { FilterOptions } from '~/components/questions/filter/FilterSection';
import FilterSection from '~/components/questions/filter/FilterSection'; import FilterSection from '~/components/questions/filter/FilterSection';
import QuestionOverviewCard from '~/components/questions/QuestionOverviewCard';
import QuestionSearchBar from '~/components/questions/QuestionSearchBar'; import QuestionSearchBar from '~/components/questions/QuestionSearchBar';
type FilterChoices = Array<Omit<FilterOptions, 'checked'>>; type FilterChoices = Array<Omit<FilterOptions, 'checked'>>;

Loading…
Cancel
Save