[questions][ui] restyle landing, question pages (#517)

pull/520/head
Jeff Sieu 2 years ago committed by GitHub
parent f752264101
commit e1e585df23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,7 +25,7 @@ export default function AnswerCommentListItem({
useAnswerCommentVote(answerCommentId);
return (
<div className="flex gap-4 border bg-white p-2 ">
<div className="flex gap-4 rounded-md border bg-white p-2">
<VotingButtons
size="sm"
upvoteCount={upvoteCount}

@ -20,15 +20,13 @@ export type LandingQueryData = {
companySlug: string;
location: string;
questionType: QuestionsQuestionType;
};
} | null;
export type LandingComponentProps = {
onLanded: (data: LandingQueryData) => void;
};
export default function LandingComponent({
onLanded: handleLandingQuery,
}: LandingComponentProps) {
export default function LandingComponent({ onLanded }: LandingComponentProps) {
const defaultCompany = useDefaultCompany();
const defaultLocation = useDefaultLocation();
@ -70,17 +68,17 @@ export default function LandingComponent({
<main className="flex flex-1 flex-col items-center overflow-y-auto bg-white">
<div className="flex flex-1 flex-col items-start justify-center gap-12 px-4">
<header className="flex flex-col items-start gap-16">
<div className="flex flex-col items-center self-stretch">
<div className="flex flex-col items-center">
<img
alt="Questions Bank"
className="h-40 w-40"
src="/bank-logo.png"
/>
<h1 className="text-center text-4xl font-bold text-slate-900">
<h1 className="text-primary-700 text-center text-5xl font-bold">
Tech Interview Question Bank
</h1>
</div>
<p className="mb-2 max-w-lg text-5xl font-semibold text-slate-900 sm:max-w-3xl">
<p className="mb-2 max-w-lg text-4xl font-semibold text-slate-900 sm:max-w-3xl">
Know the{' '}
<span className="text-primary-700">
latest SWE interview questions
@ -118,22 +116,34 @@ export default function LandingComponent({
}}
/>
</div>
<Button
addonPosition="end"
icon={ArrowSmallRightIcon}
label="Go"
size="md"
variant="primary"
onClick={() => {
if (company !== undefined && location !== undefined) {
return handleLandingQuery({
companySlug: companyOptionToSlug(company),
location: locationOptionToSlug(location),
questionType,
});
}
}}
/>
<div className="flex items-center gap-2">
<Button
addonPosition="end"
icon={ArrowSmallRightIcon}
label="Go"
size="md"
variant="primary"
onClick={() => {
if (company !== undefined && location !== undefined) {
onLanded({
companySlug: companyOptionToSlug(company),
location: locationOptionToSlug(location),
questionType,
});
}
}}
/>
<Button
addonPosition="end"
icon={ArrowSmallRightIcon}
label="View all questions"
size="md"
variant="secondary"
onClick={() => {
onLanded(null);
}}
/>
</div>
</div>
<div className="flex justify-center">
<iframe

@ -60,7 +60,7 @@ export default function QuestionAggregateBadge({
<Badge label={label} {...badgeProps} />
</button>
{visible && (
<div ref={setTooltipRef} {...getTooltipProps()}>
<div ref={setTooltipRef} {...getTooltipProps()} className="z-10">
<div className="flex flex-col gap-2 rounded-md bg-white p-2 shadow-md">
<ul>
{sortedStatistics.map(({ key, value }) => (

@ -116,6 +116,7 @@ export type BaseQuestionCardProps = ActionButtonProps &
ReceivedStatisticsProps &
UpvoteProps & {
content: string;
hideCard?: boolean;
questionId: string;
showHover?: boolean;
timestamp: string | null;
@ -140,6 +141,7 @@ export default function BaseQuestionCard({
actionButtonLabel,
onActionButtonClick,
upvoteCount,
hideCard,
timestamp,
roles,
countries,
@ -152,7 +154,6 @@ export default function BaseQuestionCard({
}: BaseQuestionCardProps) {
const [showReceivedForm, setShowReceivedForm] = useState(false);
const { handleDownvote, handleUpvote, vote } = useQuestionVote(questionId);
const hoverClass = showHover ? 'hover:bg-slate-50' : '';
const locations = useMemo(() => {
if (countries === undefined) {
@ -185,7 +186,7 @@ export default function BaseQuestionCard({
)}
<div className="flex flex-1 flex-col items-start gap-2">
<div className="flex items-baseline justify-between self-stretch">
<div className="z-10 flex items-center gap-2 text-slate-500">
<div className="flex items-center gap-2 text-slate-500">
{showAggregateStatistics && (
<>
<QuestionTypeBadge type={type} />
@ -274,7 +275,11 @@ export default function BaseQuestionCard({
return (
<article
className={`group flex gap-4 rounded-md border border-slate-300 bg-white p-4 ${hoverClass}`}>
className={clsx(
'group flex gap-4 border-slate-300',
showHover && 'hover:bg-slate-50',
!hideCard && 'rounded-md border bg-white p-4',
)}>
{cardContent}
{showDeleteButton && (
<div className="fill-danger-700 invisible self-center group-hover:visible">

@ -28,6 +28,7 @@ export default function FullQuestionCard(props: QuestionOverviewCardProps) {
return (
<BaseQuestionCard
{...props}
hideCard={true}
showActionButton={false}
showAddToList={true}
showAggregateStatistics={true}

@ -0,0 +1,30 @@
import type { PropsWithChildren } from 'react';
import { ArrowSmallLeftIcon } from '@heroicons/react/24/outline';
import { Button } from '@tih/ui';
export type BackButtonLayoutProps = PropsWithChildren<{
href: string;
}>;
export default function BackButtonLayout({
href,
children,
}: BackButtonLayoutProps) {
return (
<div className="flex w-full flex-1 flex-col items-stretch gap-4 p-4 lg:flex-row">
<div>
<Button
addonPosition="start"
display="inline"
href={href}
icon={ArrowSmallLeftIcon}
label="Back"
variant="secondary"
/>
</div>
<div className="flex w-full justify-center overflow-y-auto">
{children}
</div>
</div>
);
}

@ -2,12 +2,12 @@ import Head from 'next/head';
import { useRouter } from 'next/router';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { ArrowSmallLeftIcon } from '@heroicons/react/24/outline';
import { Button, TextArea } from '@tih/ui';
import AnswerCommentListItem from '~/components/questions/AnswerCommentListItem';
import FullAnswerCard from '~/components/questions/card/FullAnswerCard';
import FullScreenSpinner from '~/components/questions/FullScreenSpinner';
import BackButtonLayout from '~/components/questions/layout/BackButtonLayout';
import PaginationLoadMoreButton from '~/components/questions/PaginationLoadMoreButton';
import SortOptionsSelect from '~/components/questions/SortOptionsSelect';
@ -104,83 +104,72 @@ export default function QuestionPage() {
{answer.content} - {APP_TITLE}
</title>
</Head>
<div className="flex w-full flex-1 items-stretch pb-4">
<div className="flex items-baseline gap-2 py-4 pl-4">
<Button
addonPosition="start"
display="inline"
href={`/questions/${router.query.questionId}/${router.query.questionSlug}`}
icon={ArrowSmallLeftIcon}
label="Back"
variant="secondary"
<BackButtonLayout
href={`/questions/${router.query.questionId}/${router.query.questionSlug}`}>
<div className="flex max-w-7xl flex-1 flex-col gap-2">
<FullAnswerCard
answerId={answer.id}
authorImageUrl={answer.userImage}
authorName={answer.user}
content={answer.content}
createdAt={answer.createdAt}
upvoteCount={answer.numVotes}
/>
</div>
<div className="flex w-full justify-center overflow-y-auto py-4 px-5">
<div className="flex max-w-7xl flex-1 flex-col gap-2">
<FullAnswerCard
answerId={answer.id}
authorImageUrl={answer.userImage}
authorName={answer.user}
content={answer.content}
createdAt={answer.createdAt}
upvoteCount={answer.numVotes}
/>
<div className="mx-2">
<form
className="mb-2"
onSubmit={handleCommentSubmit(handleSubmitComment)}>
<TextArea
{...commentRegister('commentContent', {
minLength: 1,
required: true,
})}
label="Post a comment"
required={true}
resize="vertical"
rows={2}
<div className="mx-2">
<form
className="mb-2"
onSubmit={handleCommentSubmit(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 className="my-3 flex justify-between">
<Button
disabled={!isCommentDirty || !isCommentValid}
label="Post"
type="submit"
variant="primary"
</div>
</form>
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between gap-2">
<p className="text-lg">Comments</p>
<div className="flex items-end gap-2">
<SortOptionsSelect
sortOrderValue={commentSortOrder}
sortTypeValue={commentSortType}
onSortOrderChange={setCommentSortOrder}
onSortTypeChange={setCommentSortType}
/>
</div>
</form>
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between gap-2">
<p className="text-lg">Comments</p>
<div className="flex items-end gap-2">
<SortOptionsSelect
sortOrderValue={commentSortOrder}
sortTypeValue={commentSortType}
onSortOrderChange={setCommentSortOrder}
onSortTypeChange={setCommentSortType}
/>
</div>
</div>
{/* TODO: Allow to load more pages */}
{(answerCommentsData?.pages ?? []).flatMap(
({ processedQuestionAnswerCommentsData: comments }) =>
comments.map((comment) => (
<AnswerCommentListItem
key={comment.id}
answerCommentId={comment.id}
authorImageUrl={comment.userImage}
authorName={comment.user}
content={comment.content}
createdAt={comment.createdAt}
upvoteCount={comment.numVotes}
/>
)),
)}
<PaginationLoadMoreButton query={answerCommentInfiniteQuery} />
</div>
{/* TODO: Allow to load more pages */}
{(answerCommentsData?.pages ?? []).flatMap(
({ processedQuestionAnswerCommentsData: comments }) =>
comments.map((comment) => (
<AnswerCommentListItem
key={comment.id}
answerCommentId={comment.id}
authorImageUrl={comment.userImage}
authorName={comment.user}
content={comment.content}
createdAt={comment.createdAt}
upvoteCount={comment.numVotes}
/>
)),
)}
<PaginationLoadMoreButton query={answerCommentInfiniteQuery} />
</div>
</div>
</div>
</div>
</BackButtonLayout>
</>
);
}

@ -2,13 +2,13 @@ import Head from 'next/head';
import { useRouter } from 'next/router';
import { useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
import { ArrowSmallLeftIcon } from '@heroicons/react/24/outline';
import { Button, Collapsible, HorizontalDivider, TextArea } from '@tih/ui';
import AnswerCommentListItem from '~/components/questions/AnswerCommentListItem';
import FullQuestionCard from '~/components/questions/card/question/FullQuestionCard';
import QuestionAnswerCard from '~/components/questions/card/QuestionAnswerCard';
import FullScreenSpinner from '~/components/questions/FullScreenSpinner';
import BackButtonLayout from '~/components/questions/layout/BackButtonLayout';
import PaginationLoadMoreButton from '~/components/questions/PaginationLoadMoreButton';
import SortOptionsSelect from '~/components/questions/SortOptionsSelect';
@ -182,19 +182,9 @@ export default function QuestionPage() {
{question.content} - {APP_TITLE}
</title>
</Head>
<div className="flex w-full flex-1 items-stretch pb-4">
<div className="flex items-baseline gap-2 py-4 pl-4">
<Button
addonPosition="start"
display="inline"
href="/questions/browse"
icon={ArrowSmallLeftIcon}
label="Back"
variant="secondary"
/>
</div>
<div className="flex w-full justify-center overflow-y-auto py-4 px-5">
<div className="flex max-w-7xl flex-1 flex-col gap-2">
<BackButtonLayout href="/questions/browse">
<div className="flex max-w-7xl flex-1 flex-col gap-2">
<div className="flex flex-col gap-2 rounded-md border bg-white p-4">
<FullQuestionCard
{...question}
companies={relabeledAggregatedEncounters?.companyCounts ?? {}}
@ -220,35 +210,15 @@ export default function QuestionPage() {
});
}}
/>
<div className="mx-2">
<Collapsible label={`View ${question.numComments} comment(s)`}>
<div className="mt-4 px-4">
<form
className="mb-2"
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>
{/* TODO: Add button to load more */}
<div className="ml-16 mr-2">
<Collapsible
defaultOpen={true}
label={
<div className="text-primary-700">{`${question.numComments} comment(s)`}</div>
}>
<div className="">
<div className="flex flex-col gap-2 text-black">
<div className="flex items-center justify-between gap-2">
<p className="text-lg">Comments</p>
<div className="flex justify-end gap-2">
<div className="flex items-end gap-2">
<SortOptionsSelect
sortOrderValue={commentSortOrder}
@ -273,65 +243,93 @@ 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>
</Collapsible>
</div>
<HorizontalDivider />
<form onSubmit={handleSubmit(handleSubmitAnswer)}>
</div>
<HorizontalDivider />
<form onSubmit={handleSubmit(handleSubmitAnswer)}>
<div className="flex flex-col gap-2">
<p className="text-md font-semibold">Contribute your answer</p>
<TextArea
{...answerRegister('answerContent', {
minLength: 1,
required: true,
})}
isLabelHidden={true}
label="Contribute your answer"
required={true}
resize="vertical"
rows={5}
/>
<div className="mt-3 mb-1 flex justify-between">
<Button
disabled={!isDirty || !isValid}
label="Contribute"
type="submit"
variant="primary"
/>
</div>
</form>
<div className="flex items-center justify-between gap-2">
<p className="text-xl">{question.numAnswers} answers</p>
<div className="flex items-end gap-2">
<SortOptionsSelect
sortOrderValue={answerSortOrder}
sortTypeValue={answerSortType}
onSortOrderChange={setAnswerSortOrder}
onSortTypeChange={setAnswerSortType}
/>
</div>
</div>
{/* TODO: Add button to load more */}
{(answerData?.pages ?? []).flatMap(
({ processedAnswersData: answers }) =>
answers.map((answer) => (
<QuestionAnswerCard
key={answer.id}
answerId={answer.id}
authorImageUrl={answer.userImage}
authorName={answer.user}
commentCount={answer.numComments}
content={answer.content}
createdAt={answer.createdAt}
href={`${router.asPath}/answer/${answer.id}/${createSlug(
answer.content,
)}`}
upvoteCount={answer.numVotes}
/>
)),
)}
<PaginationLoadMoreButton query={answerInfiniteQuery} />
<div className="mt-3 mb-1 flex justify-between">
<Button
disabled={!isDirty || !isValid}
label="Contribute"
type="submit"
variant="primary"
/>
</div>
</form>
<div className="flex items-center justify-between gap-2">
<p className="text-xl font-semibold">
{question.numAnswers} answers
</p>
<div className="flex items-end gap-2">
<SortOptionsSelect
sortOrderValue={answerSortOrder}
sortTypeValue={answerSortType}
onSortOrderChange={setAnswerSortOrder}
onSortTypeChange={setAnswerSortType}
/>
</div>
</div>
{/* TODO: Add button to load more */}
{(answerData?.pages ?? []).flatMap(
({ processedAnswersData: answers }) =>
answers.map((answer) => (
<QuestionAnswerCard
key={answer.id}
answerId={answer.id}
authorImageUrl={answer.userImage}
authorName={answer.user}
commentCount={answer.numComments}
content={answer.content}
createdAt={answer.createdAt}
href={`${router.asPath}/answer/${answer.id}/${createSlug(
answer.content,
)}`}
upvoteCount={answer.numVotes}
/>
)),
)}
<PaginationLoadMoreButton query={answerInfiniteQuery} />
</div>
</div>
</BackButtonLayout>
</>
);
}

@ -10,6 +10,14 @@ export default function QuestionsHomePage() {
const router = useRouter();
const handleLandingQuery = async (data: LandingQueryData) => {
if (data === null) {
// Go to browse page
router.push({
pathname: '/questions/browse',
});
return;
}
const { companySlug, location, questionType } = data;
// Go to browse page

Loading…
Cancel
Save