[questions][feat] tweak card UI

pull/546/head
Yangshun Tay 3 years ago
parent 8a73925601
commit 362c8391d9

@ -106,7 +106,7 @@ export default function AddToListDropdown({
const CustomMenuButton = ({ children }: PropsWithChildren<unknown>) => ( const CustomMenuButton = ({ children }: PropsWithChildren<unknown>) => (
<button <button
className="focus:ring-primary-500 inline-flex w-full justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-slate-100" className="focus:ring-primary-500 inline-flex w-full items-center justify-center rounded-md border border-slate-300 bg-white px-2.5 py-1.5 text-xs font-medium text-slate-700 shadow-sm hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-slate-100"
type="button" type="button"
onClick={handleMenuButtonClick}> onClick={handleMenuButtonClick}>
{children} {children}

@ -1,19 +1,16 @@
import type { ComponentProps } from 'react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { usePopperTooltip } from 'react-popper-tooltip'; import { usePopperTooltip } from 'react-popper-tooltip';
import { Badge } from '@tih/ui';
import 'react-popper-tooltip/dist/styles.css'; import 'react-popper-tooltip/dist/styles.css';
type BadgeProps = ComponentProps<typeof Badge>; export type QuestionAggregateBadgeProps = Readonly<{
icon: (props: React.ComponentProps<'svg'>) => JSX.Element;
export type QuestionAggregateBadgeProps = Omit<BadgeProps, 'label'> & {
statistics: Record<string, number>; statistics: Record<string, number>;
}; }>;
export default function QuestionAggregateBadge({ export default function QuestionAggregateBadge({
icon: Icon,
statistics, statistics,
...badgeProps
}: QuestionAggregateBadgeProps) { }: QuestionAggregateBadgeProps) {
const { getTooltipProps, setTooltipRef, setTriggerRef, visible } = const { getTooltipProps, setTooltipRef, setTriggerRef, visible } =
usePopperTooltip({ usePopperTooltip({
@ -56,8 +53,16 @@ export default function QuestionAggregateBadge({
return ( return (
<> <>
<button ref={setTriggerRef} className="rounded-full" type="button"> <button
<Badge label={label} {...badgeProps} /> ref={setTriggerRef}
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">
<Icon
aria-hidden={true}
className="mr-1.5 h-5 w-5 flex-shrink-0 text-slate-400"
/>
{label}
</button> </button>
{visible && ( {visible && (
<div ref={setTooltipRef} {...getTooltipProps()} className="z-10"> <div ref={setTooltipRef} {...getTooltipProps()} className="z-10">

@ -1,8 +1,7 @@
import clsx from 'clsx';
import React from 'react'; import React from 'react';
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline'; import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
import type { Vote } from '@prisma/client'; import type { Vote } from '@prisma/client';
import type { ButtonSize } from '@tih/ui';
import { Button } from '@tih/ui';
import { useProtectedCallback } from '~/utils/questions/useProtectedCallback'; import { useProtectedCallback } from '~/utils/questions/useProtectedCallback';
@ -18,7 +17,7 @@ export type VotingButtonsCallbackProps = {
}; };
export type VotingButtonsProps = VotingButtonsCallbackProps & { export type VotingButtonsProps = VotingButtonsCallbackProps & {
size?: ButtonSize; size?: 'md' | 'sm';
upvoteCount: number; upvoteCount: number;
}; };
@ -29,11 +28,6 @@ export default function VotingButtons({
upvoteCount, upvoteCount,
size = 'md', size = 'md',
}: VotingButtonsProps) { }: VotingButtonsProps) {
const upvoteButtonVariant =
vote?.vote === 'UPVOTE' ? 'secondary' : 'tertiary';
const downvoteButtonVariant =
vote?.vote === 'DOWNVOTE' ? 'secondary' : 'tertiary';
const handleUpvoteClick = useProtectedCallback(() => { const handleUpvoteClick = useProtectedCallback(() => {
onUpvote(); onUpvote();
}); });
@ -44,31 +38,45 @@ export default function VotingButtons({
return ( return (
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
<Button <button
icon={ChevronUpIcon} aria-label="Upvote"
isLabelHidden={true} className="rounded-full p-1 hover:bg-slate-100"
label="Upvote" type="button"
size={size}
variant={upvoteButtonVariant}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
handleUpvoteClick(); handleUpvoteClick();
}} }}>
/> <ChevronUpIcon
className={clsx(
size === 'sm' && 'h-5 w-5',
size === 'md' && 'h-8 w-8',
vote?.vote === 'UPVOTE'
? 'text-primary-500'
: 'hover:text-primary-500 text-slate-400',
)}
/>
</button>
<p>{upvoteCount}</p> <p>{upvoteCount}</p>
<Button <button
icon={ChevronDownIcon} aria-label="Downvote"
isLabelHidden={true} className="rounded-full p-1 hover:bg-slate-100"
label="Downvote" type="button"
size={size}
variant={downvoteButtonVariant}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
handleDownvoteClick(); handleDownvoteClick();
}} }}>
/> <ChevronDownIcon
className={clsx(
size === 'sm' && 'h-5 w-5',
size === 'md' && 'h-8 w-8',
vote?.vote === 'DOWNVOTE'
? 'text-danger-500'
: 'hover:text-danger-500 text-slate-400',
)}
/>
</button>
</div> </div>
); );
} }

@ -1,5 +1,10 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import {
BuildingOfficeIcon,
MapPinIcon,
UserCircleIcon,
} from '@heroicons/react/20/solid';
import { import {
ChatBubbleBottomCenterTextIcon, ChatBubbleBottomCenterTextIcon,
CheckIcon, CheckIcon,
@ -198,21 +203,24 @@ export default function BaseQuestionCard({
</div> </div>
</> </>
)} )}
<div className="flex flex-1 flex-col items-start gap-2"> <div className="flex flex-1 flex-col items-start gap-4">
<div className="flex items-baseline justify-between self-stretch"> <div className="flex items-center justify-between self-stretch">
<div className="flex flex-wrap items-center gap-2 text-slate-500"> <div className="flex flex-wrap items-center gap-3 text-slate-500">
{showAggregateStatistics && ( {showAggregateStatistics && (
<> <>
<QuestionTypeBadge type={type} /> <QuestionTypeBadge type={type} />
<QuestionAggregateBadge <QuestionAggregateBadge
icon={BuildingOfficeIcon}
statistics={companies} statistics={companies}
variant="primary"
/> />
<QuestionAggregateBadge <QuestionAggregateBadge
icon={MapPinIcon}
statistics={locations!} statistics={locations!}
variant="success"
/> />
<QuestionAggregateBadge statistics={roles} variant="danger" /> <QuestionAggregateBadge
icon={UserCircleIcon}
statistics={roles}
/>
</> </>
)} )}
{timestamp !== null && ( {timestamp !== null && (
@ -240,59 +248,43 @@ export default function BaseQuestionCard({
</div> </div>
<p <p
className={clsx( className={clsx(
'whitespace-pre-line font-semibold', 'whitespace-pre-line text-base font-medium leading-6 text-slate-900 md:text-lg',
truncateContent && 'line-clamp-2 text-ellipsis', truncateContent && 'line-clamp-2 text-ellipsis',
)}> )}>
{content} {content}
</p> </p>
{!showReceivedForm && {!showReceivedForm &&
(showAnswerStatistics || (showAnswerStatistics ||
showReceivedStatistics || showReceivedStatistics ||
showCreateEncounterButton) && ( showCreateEncounterButton) && (
<div className="flex gap-2"> <div className="flex w-full gap-4">
{showAnswerStatistics && ( {showAnswerStatistics && (
<> <div>
<div className="sm:hidden"> <button
<Button className="-my-1 flex items-center rounded-md px-2
addonPosition="start" py-1 text-xs font-medium
icon={ChatBubbleBottomCenterTextIcon} text-slate-500 hover:bg-slate-100 hover:text-slate-600"
label={`${answerCount}`} type="button">
size="sm" <ChatBubbleBottomCenterTextIcon
variant="tertiary" aria-hidden={true}
className="mr-2 h-5 w-5"
/> />
</div> {answerCount} {answerCount === 1 ? 'answer' : 'answers'}
<div className="hidden sm:block"> </button>
<Button </div>
addonPosition="start"
icon={ChatBubbleBottomCenterTextIcon}
label={`${answerCount} answers`}
size="sm"
variant="tertiary"
/>
</div>
</>
)} )}
{showReceivedStatistics && ( {showReceivedStatistics && (
<> <div>
<div className="sm:hidden"> <button
<Button className="-my-1 flex items-center rounded-md px-2
addonPosition="start" py-1 text-xs font-medium
icon={EyeIcon} text-slate-500 hover:bg-slate-100 hover:text-slate-600"
label={`${receivedCount}`} type="button">
size="sm" <EyeIcon aria-hidden={true} className="mr-2 h-5 w-5" />
variant="tertiary" {receivedCount} received this
/> </button>
</div> </div>
<div className="hidden sm:block">
<Button
addonPosition="start"
icon={EyeIcon}
label={`${receivedCount} received this`}
size="sm"
variant="tertiary"
/>
</div>
</>
)} )}
{showCreateEncounterButton && ( {showCreateEncounterButton && (
<Button <Button
@ -324,8 +316,8 @@ export default function BaseQuestionCard({
<article <article
className={clsx( className={clsx(
'group flex gap-4 border-slate-300', 'group flex gap-4 border-slate-300',
showHover && 'transition hover:bg-slate-50', showHover && 'hover:border-primary-500 transition',
!hideCard && 'rounded-md border bg-white p-4', !hideCard && 'rounded-md border bg-white p-4 sm:rounded-lg sm:p-6',
)}> )}>
{cardContent} {cardContent}
{showDeleteButton && ( {showDeleteButton && (

@ -541,7 +541,7 @@ export default function QuestionsBrowsePage() {
onSortTypeChange={setSortType} onSortTypeChange={setSortType}
/> />
</div> </div>
<div className="flex flex-col gap-2 pb-4"> <div className="flex flex-col gap-4 pb-4">
{(questionsQueryData?.pages ?? []).flatMap( {(questionsQueryData?.pages ?? []).flatMap(
({ data: questions }) => ({ data: questions }) =>
questions.map((question) => { questions.map((question) => {

Loading…
Cancel
Save