[questions][feat] tweak comment and answer pages

pull/546/head
Yangshun Tay 3 years ago
parent 362c8391d9
commit 10435bad40

@ -36,7 +36,7 @@ export default function SortOptionsSelect({
return ( return (
<div className="flex items-end justify-end gap-2"> <div className="flex items-end justify-end gap-2">
<div className="flex items-center gap-2"> <div className="flex items-center">
<Select <Select
display="inline" display="inline"
label="Sort by" label="Sort by"
@ -52,7 +52,7 @@ export default function SortOptionsSelect({
}} }}
/> />
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center">
<Select <Select
display="inline" display="inline"
label="Order by" label="Order by"

@ -1,4 +1,5 @@
import { format } from 'date-fns'; import clsx from 'clsx';
import { formatDistanceToNow } from 'date-fns';
import { ChatBubbleLeftRightIcon } from '@heroicons/react/24/outline'; import { ChatBubbleLeftRightIcon } from '@heroicons/react/24/outline';
import useAnswerVote from '~/utils/questions/vote/useAnswerVote'; import useAnswerVote from '~/utils/questions/vote/useAnswerVote';
@ -30,11 +31,13 @@ export default function AnswerCard({
showHover, showHover,
}: AnswerCardProps) { }: AnswerCardProps) {
const { handleUpvote, handleDownvote, vote } = useAnswerVote(answerId); const { handleUpvote, handleDownvote, vote } = useAnswerVote(answerId);
const hoverClass = showHover ? 'hover:bg-slate-50' : '';
return ( return (
<article <article
className={`flex gap-4 rounded-md border bg-white p-2 ${hoverClass}`}> className={clsx(
'flex gap-4 rounded-md border border-slate-200 bg-white p-4 sm:rounded-lg',
showHover && 'hover:bg-slate-50',
)}>
<VotingButtons <VotingButtons
size={votingButtonsSize} size={votingButtonsSize}
upvoteCount={upvoteCount} upvoteCount={upvoteCount}
@ -42,26 +45,37 @@ export default function AnswerCard({
onDownvote={handleDownvote} onDownvote={handleDownvote}
onUpvote={handleUpvote} onUpvote={handleUpvote}
/> />
<div className="flex flex-col gap-2"> <div className="flex w-full flex-col gap-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<img <img
alt={`${authorName} profile picture`} alt={authorName}
className="h-8 w-8 rounded-full" className="h-7 w-7 rounded-full"
src={authorImageUrl}></img> src={authorImageUrl}
<h1 className="font-bold">{authorName}</h1> />
<p className="text-xs font-extralight"> <p className="text-sm font-medium text-slate-900">{authorName}</p>
Posted on: {format(createdAt, 'h:mm a, MMMM dd, yyyy')} <span className="font-medium text-slate-500">·</span>
<p className="text-xs text-slate-500">
{formatDistanceToNow(createdAt, {
addSuffix: true,
})}
</p> </p>
</div> </div>
<p className="whitespace-pre-line">{content}</p> <p className="whitespace-pre-wrap text-xs sm:text-sm">{content}</p>
{commentCount !== undefined && ( <div className="-ml-2">
<div className="flex items-center gap-2 text-slate-500"> {commentCount !== undefined && (
<ChatBubbleLeftRightIcon className="h-6 w-6" /> <button
<p className="text-sm font-medium"> className="-my-1 flex items-center rounded-md px-2
py-1 text-xs font-medium
text-slate-500 hover:bg-slate-100 hover:text-slate-600"
type="button">
<ChatBubbleLeftRightIcon
aria-hidden={true}
className="mr-2 h-5 w-5"
/>
{commentCount} {commentCount === 1 ? 'comment' : 'comments'} {commentCount} {commentCount === 1 ? 'comment' : 'comments'}
</p> </button>
</div> )}
)} </div>
</div> </div>
</article> </article>
); );

@ -315,7 +315,7 @@ export default function BaseQuestionCard({
return ( return (
<article <article
className={clsx( className={clsx(
'group flex gap-4 border-slate-300', 'group flex gap-4 border-slate-200',
showHover && 'hover:border-primary-500 transition', showHover && 'hover:border-primary-500 transition',
!hideCard && 'rounded-md border bg-white p-4 sm:rounded-lg sm:p-6', !hideCard && 'rounded-md border bg-white p-4 sm:rounded-lg sm:p-6',
)}> )}>

@ -1,4 +1,4 @@
import { format } from 'date-fns'; import { formatDistanceToNow } from 'date-fns';
import type { BackendVote } from '../VotingButtons'; import type { BackendVote } from '../VotingButtons';
import VotingButtons from '../VotingButtons'; import VotingButtons from '../VotingButtons';
@ -25,7 +25,7 @@ export default function CommentListItem({
onUpvote, onUpvote,
}: CommentListItemProps) { }: CommentListItemProps) {
return ( return (
<div className="flex gap-4 rounded-md border bg-white p-2"> <div className="flex gap-4 rounded-md border border-slate-200 bg-white p-2">
<VotingButtons <VotingButtons
size="sm" size="sm"
upvoteCount={upvoteCount} upvoteCount={upvoteCount}
@ -36,15 +36,19 @@ export default function CommentListItem({
<div className="mt-1 flex flex-col gap-1"> <div className="mt-1 flex flex-col gap-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<img <img
alt={`${authorName} profile picture`} alt={authorName}
className="h-8 w-8 rounded-full" className="h-7 w-7 rounded-full"
src={authorImageUrl}></img> src={authorImageUrl}
<h1 className="font-bold">{authorName}</h1> />
<p className="pt-1 text-xs font-extralight"> <p className="text-sm font-medium text-slate-900">{authorName}</p>
Posted on: {format(createdAt, 'h:mm a, MMMM dd, yyyy')} <span className="font-medium text-slate-500">·</span>
<p className="text-xs text-slate-500">
{formatDistanceToNow(createdAt, {
addSuffix: true,
})}
</p> </p>
</div> </div>
<p className="whitespace-pre-line">{content}</p> <p className="whitespace-pre-wrap text-xs sm:text-sm">{content}</p>
</div> </div>
</div> </div>
); );

@ -142,6 +142,7 @@ export default function CreateQuestionEncounterForm({
(step === 2 && selectedRole === null) (step === 2 && selectedRole === null)
} }
label="Next" label="Next"
size="sm"
variant="primary" variant="primary"
onClick={() => { onClick={() => {
setStep(step + 1); setStep(step + 1);
@ -152,6 +153,7 @@ export default function CreateQuestionEncounterForm({
<Button <Button
isLoading={loading} isLoading={loading}
label="Submit" label="Submit"
size="sm"
variant="primary" variant="primary"
onClick={async () => { onClick={async () => {
if ( if (
@ -181,6 +183,7 @@ export default function CreateQuestionEncounterForm({
)} )}
<Button <Button
label="Cancel" label="Cancel"
size="sm"
variant="tertiary" variant="tertiary"
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();

@ -2,6 +2,8 @@ import type { PropsWithChildren } from 'react';
import { ArrowSmallLeftIcon } from '@heroicons/react/24/outline'; import { ArrowSmallLeftIcon } from '@heroicons/react/24/outline';
import { Button } from '@tih/ui'; import { Button } from '@tih/ui';
import Container from '~/components/shared/Container';
export type BackButtonLayoutProps = PropsWithChildren<{ export type BackButtonLayoutProps = PropsWithChildren<{
href: string; href: string;
}>; }>;
@ -11,7 +13,7 @@ export default function BackButtonLayout({
children, children,
}: BackButtonLayoutProps) { }: BackButtonLayoutProps) {
return ( return (
<div className="flex w-full flex-1 flex-col items-stretch gap-4 p-4 lg:flex-row"> <Container className="flex flex-col gap-4 pt-4 pb-12" variant="sm">
<div> <div>
<Button <Button
addonPosition="start" addonPosition="start"
@ -19,12 +21,13 @@ export default function BackButtonLayout({
href={href} href={href}
icon={ArrowSmallLeftIcon} icon={ArrowSmallLeftIcon}
label="Back" label="Back"
size="sm"
variant="secondary" variant="secondary"
/> />
</div> </div>
<div className="flex w-full justify-center overflow-y-auto"> <div className="flex w-full justify-center overflow-y-auto">
{children} {children}
</div> </div>
</div> </Container>
); );
} }

@ -44,11 +44,12 @@ export default function ExpandedTypeahead({
); );
return ( return (
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap items-center gap-2">
{suggestions.map((suggestion) => ( {suggestions.map((suggestion) => (
<div key={suggestion.id} className="hidden lg:block"> <div key={suggestion.id} className="hidden lg:block">
<Button <Button
label={suggestion.label} label={suggestion.label}
size="sm"
variant="tertiary" variant="tertiary"
onClick={() => { onClick={() => {
onSuggestionClick?.(suggestion); onSuggestionClick?.(suggestion);

@ -113,7 +113,7 @@ export default function QuestionPage() {
</Head> </Head>
<BackButtonLayout <BackButtonLayout
href={`/questions/${router.query.questionId}/${router.query.questionSlug}`}> href={`/questions/${router.query.questionId}/${router.query.questionSlug}`}>
<div className="flex max-w-7xl flex-1 flex-col gap-2"> <div className="flex max-w-7xl flex-1 flex-col gap-4">
<FullAnswerCard <FullAnswerCard
answerId={answer.id} answerId={answer.id}
authorImageUrl={answer.userImage} authorImageUrl={answer.userImage}
@ -122,7 +122,7 @@ export default function QuestionPage() {
createdAt={answer.createdAt} createdAt={answer.createdAt}
upvoteCount={answer.numVotes} upvoteCount={answer.numVotes}
/> />
<div className="mx-2"> <div>
<form <form
className="mb-2" className="mb-2"
onSubmit={handleCommentSubmit(handleSubmitComment)}> onSubmit={handleCommentSubmit(handleSubmitComment)}>
@ -136,7 +136,7 @@ export default function QuestionPage() {
resize="vertical" resize="vertical"
rows={2} rows={2}
/> />
<div className="my-3 flex justify-between"> <div className="my-3 flex justify-end">
<Button <Button
disabled={!isCommentDirty || !isCommentValid} disabled={!isCommentDirty || !isCommentValid}
label="Post" label="Post"
@ -145,7 +145,7 @@ export default function QuestionPage() {
/> />
</div> </div>
</form> </form>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-4">
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<p className="text-lg">Comments</p> <p className="text-lg">Comments</p>
<div className="flex items-end gap-2"> <div className="flex items-end gap-2">

@ -200,8 +200,8 @@ export default function QuestionPage() {
</title> </title>
</Head> </Head>
<BackButtonLayout href="/questions/browse"> <BackButtonLayout href="/questions/browse">
<div className="flex max-w-7xl flex-1 flex-col gap-2"> <div className="flex flex-1 flex-col gap-4">
<div className="flex flex-col gap-2 rounded-md border bg-white p-4"> <div className="flex flex-col gap-4 rounded-md border bg-white p-4">
<FullQuestionCard <FullQuestionCard
{...question} {...question}
companies={relabeledAggregatedEncounters?.companyCounts ?? {}} companies={relabeledAggregatedEncounters?.companyCounts ?? {}}
@ -224,24 +224,24 @@ export default function QuestionPage() {
}); });
}} }}
/> />
<div className="ml-16 mr-2"> <div className="sm:ml-13 ml-11 mr-2 md:ml-14">
<Collapsible <Collapsible
defaultOpen={true} defaultOpen={true}
label={ label={
<div className="text-primary-700">{`${question.numComments} comment(s)`}</div> <div className="text-primary-700">{`${question.numComments} comment(s)`}</div>
}> }>
<div className=""> <div className="flex flex-col gap-4 text-black">
<div className="flex flex-col gap-2 text-black"> <div className="flex justify-end gap-2">
<div className="flex justify-end gap-2"> <div className="flex items-end gap-2">
<div className="flex items-end gap-2"> <SortOptionsSelect
<SortOptionsSelect sortOrderValue={commentSortOrder}
sortOrderValue={commentSortOrder} sortTypeValue={commentSortType}
sortTypeValue={commentSortType} onSortOrderChange={setCommentSortOrder}
onSortOrderChange={setCommentSortOrder} onSortTypeChange={setCommentSortType}
onSortTypeChange={setCommentSortType} />
/>
</div>
</div> </div>
</div>
<div className="flex flex-col gap-4">
{(commentData?.pages ?? []).flatMap(({ data: comments }) => {(commentData?.pages ?? []).flatMap(({ data: comments }) =>
comments.map((comment) => ( comments.map((comment) => (
<QuestionCommentListItem <QuestionCommentListItem
@ -255,30 +255,30 @@ export default function QuestionPage() {
/> />
)), )),
)} )}
<PaginationLoadMoreButton query={commentInfiniteQuery} />
<form
className="mt-4"
onSubmit={handleCommentSubmitClick(handleSubmitComment)}>
<TextArea
{...commentRegister('commentContent', {
minLength: 1,
required: true,
})}
label="Post a comment"
required={true}
resize="vertical"
rows={2}
/>
<div className="my-3 flex justify-between">
<Button
disabled={!isCommentDirty || !isCommentValid}
label="Post"
type="submit"
variant="primary"
/>
</div>
</form>
</div> </div>
<PaginationLoadMoreButton query={commentInfiniteQuery} />
<form
className="mt-4"
onSubmit={handleCommentSubmitClick(handleSubmitComment)}>
<TextArea
{...commentRegister('commentContent', {
minLength: 1,
required: true,
})}
label="Post a comment"
required={true}
resize="vertical"
rows={2}
/>
<div className="my-3 flex justify-end">
<Button
disabled={!isCommentDirty || !isCommentValid}
label="Post"
type="submit"
variant="primary"
/>
</div>
</form>
</div> </div>
</Collapsible> </Collapsible>
</div> </div>
@ -299,7 +299,7 @@ export default function QuestionPage() {
rows={5} rows={5}
/> />
</div> </div>
<div className="mt-3 mb-1 flex justify-between"> <div className="mt-3 mb-1 flex justify-end">
<Button <Button
disabled={!isDirty || !isValid} disabled={!isDirty || !isValid}
label="Contribute" label="Contribute"
@ -308,11 +308,11 @@ export default function QuestionPage() {
/> />
</div> </div>
</form> </form>
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-4">
<p className="text-xl font-semibold"> <p className="text-xl font-semibold">
{question.numAnswers} answers {question.numAnswers} answers
</p> </p>
<div className="flex items-end gap-2"> <div className="flex items-end gap-4">
<SortOptionsSelect <SortOptionsSelect
sortOrderValue={answerSortOrder} sortOrderValue={answerSortOrder}
sortTypeValue={answerSortType} sortTypeValue={answerSortType}
@ -321,25 +321,27 @@ export default function QuestionPage() {
/> />
</div> </div>
</div> </div>
{/* TODO: Add button to load more */} <div className="flex flex-col gap-4">
{(answerData?.pages ?? []).flatMap(({ data: answers }) => {/* TODO: Add button to load more */}
answers.map((answer) => ( {(answerData?.pages ?? []).flatMap(({ data: answers }) =>
<QuestionAnswerCard answers.map((answer) => (
key={answer.id} <QuestionAnswerCard
answerId={answer.id} key={answer.id}
authorImageUrl={answer.userImage} answerId={answer.id}
authorName={answer.user} authorImageUrl={answer.userImage}
commentCount={answer.numComments} authorName={answer.user}
content={answer.content} commentCount={answer.numComments}
createdAt={answer.createdAt} content={answer.content}
href={`${router.asPath}/answer/${answer.id}/${createSlug( createdAt={answer.createdAt}
answer.content, href={`${router.asPath}/answer/${answer.id}/${createSlug(
)}`} answer.content,
upvoteCount={answer.numVotes} )}`}
/> upvoteCount={answer.numVotes}
)), />
)} )),
<PaginationLoadMoreButton query={answerInfiniteQuery} /> )}
<PaginationLoadMoreButton query={answerInfiniteQuery} />
</div>
</div> </div>
</BackButtonLayout> </BackButtonLayout>
</> </>

Loading…
Cancel
Save