[questions][ui] improve browse sort ui

pull/531/head
Jeff Sieu 2 years ago
parent 2293b6b8c4
commit 964f791e46

@ -1,5 +1,4 @@
import { useState } from 'react'; import { useState } from 'react';
import { TextInput } from '@tih/ui';
import { useProtectedCallback } from '~/utils/questions/useProtectedCallback'; import { useProtectedCallback } from '~/utils/questions/useProtectedCallback';
@ -27,22 +26,13 @@ export default function ContributeQuestionCard({
return ( return (
<div className="w-full"> <div className="w-full">
<button <button
className="flex w-full flex-1 justify-between gap-2 rounded-md border border-slate-300 bg-white p-4 text-left hover:bg-slate-100" className="flex w-full flex-1 justify-between gap-2 rounded-md border border-slate-300 bg-white p-4 text-left transition hover:bg-slate-100"
type="button" type="button"
onClick={handleOpenContribute}> onClick={handleOpenContribute}>
<div className="w-full"> <div className="w-full rounded-md border border-slate-300 bg-slate-100 py-2 px-4 text-slate-400">
<TextInput <p className="font-semibold">
disabled={true} Just completed your interview? Contribute your questions
isLabelHidden={true} </p>
label="Question"
placeholder="Contribute a question"
onChange={handleOpenContribute}
/>
</div>
<div className="flex flex-wrap items-end justify-start gap-2">
<h1 className="bg-primary-600 hover:bg-primary-700 rounded-full px-3 py-2 text-white">
Contribute
</h1>
</div> </div>
</button> </button>
<ContributeQuestionDialog <ContributeQuestionDialog

@ -2,10 +2,13 @@ import {
AdjustmentsHorizontalIcon, AdjustmentsHorizontalIcon,
MagnifyingGlassIcon, MagnifyingGlassIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { Button, TextInput } from '@tih/ui'; import { Button, Tabs, TextInput } from '@tih/ui';
import { SORT_ORDERS } from '~/utils/questions/constants';
import type { SortOptionsSelectProps } from './SortOptionsSelect'; import type { SortOptionsSelectProps } from './SortOptionsSelect';
import SortOptionsSelect from './SortOptionsSelect';
import { SortOrder, SortType } from '~/types/questions.d';
export type QuestionSearchBarProps = SortOptionsSelectProps & { export type QuestionSearchBarProps = SortOptionsSelectProps & {
onFilterOptionsToggle: () => void; onFilterOptionsToggle: () => void;
@ -13,6 +16,22 @@ export type QuestionSearchBarProps = SortOptionsSelectProps & {
query: string; query: string;
}; };
function getSortOrderLabel(sortOrder: SortOrder, sortType: SortType): string {
switch (sortType) {
case SortType.NEW:
return sortOrder === SortOrder.ASC ? 'Oldest first' : 'Newest first';
case SortType.TOP:
return sortOrder === SortOrder.ASC
? 'Least upvotes first'
: 'Most upvotes first';
case SortType.ENCOUNTERS:
return sortOrder === SortOrder.ASC
? 'Least received first'
: 'Most received first';
}
return '';
}
export default function QuestionSearchBar({ export default function QuestionSearchBar({
onFilterOptionsToggle, onFilterOptionsToggle,
onQueryChange, onQueryChange,
@ -20,44 +39,75 @@ export default function QuestionSearchBar({
...sortOptionsSelectProps ...sortOptionsSelectProps
}: QuestionSearchBarProps) { }: QuestionSearchBarProps) {
return ( return (
<div className="flex flex-col items-stretch gap-x-4 gap-y-4 lg:flex-row lg:items-end"> <div className="flex flex-col gap-4">
<div className="flex flex-1 gap-2"> <div className="flex flex-col items-stretch gap-x-2 gap-y-4 lg:flex-row lg:items-end">
<div className="flex-1"> <div className="flex flex-1 gap-2">
<TextInput <div className="flex-1">
isLabelHidden={true} <TextInput
label="Search by content" isLabelHidden={true}
placeholder="Search by content" label="Search by content"
startAddOn={MagnifyingGlassIcon} placeholder="Search by content"
startAddOnType="icon" startAddOn={MagnifyingGlassIcon}
value={query} startAddOnType="icon"
onChange={(value) => { value={query}
onQueryChange(value); onChange={(value) => {
}} onQueryChange(value);
/> }}
/>
</div>
<div className="sm:hidden">
<Button
addonPosition="start"
icon={AdjustmentsHorizontalIcon}
isLabelHidden={true}
label="Filters"
variant="tertiary"
onClick={onFilterOptionsToggle}
/>
</div>
<div className="hidden sm:block lg:hidden">
<Button
addonPosition="start"
icon={AdjustmentsHorizontalIcon}
label="Filters"
variant="tertiary"
onClick={onFilterOptionsToggle}
/>
</div>
</div> </div>
<div className="sm:hidden"> {/* <SortOptionsSelect {...sortOptionsSelectProps} /> */}
<Button </div>
addonPosition="start" <div className="flex justify-start gap-4">
icon={AdjustmentsHorizontalIcon} <div>
isLabelHidden={true} <Tabs
label="Filters" label="Sort by"
variant="tertiary" tabs={sortOptionsSelectProps.sortTypeOptions ?? []}
onClick={onFilterOptionsToggle} value={sortOptionsSelectProps.sortTypeValue}
onChange={sortOptionsSelectProps.onSortTypeChange}
/> />
</div> </div>
<div className="hidden sm:block lg:hidden"> <div className="border-l" />
<Button <div>
addonPosition="start" <Tabs
icon={AdjustmentsHorizontalIcon} label="Order by"
label="Filters" tabs={(sortOptionsSelectProps.sortOrderOptions ?? SORT_ORDERS).map(
variant="tertiary" (option) => {
onClick={onFilterOptionsToggle} const newLabel = getSortOrderLabel(
option.value,
sortOptionsSelectProps.sortTypeValue,
);
return {
...option,
label: newLabel,
};
},
)}
value={sortOptionsSelectProps.sortOrderValue}
onChange={sortOptionsSelectProps.onSortOrderChange}
/> />
</div> </div>
</div> </div>
<div className="flex items-end justify-end gap-4">
<SortOptionsSelect {...sortOptionsSelectProps} />
</div>
</div> </div>
); );
} }

@ -35,7 +35,7 @@ export default function SortOptionsSelect({
const sortOrders = sortOrderOptions ?? SORT_ORDERS; const sortOrders = sortOrderOptions ?? SORT_ORDERS;
return ( return (
<div className="flex items-end justify-end gap-4"> <div className="flex items-end justify-end gap-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Select <Select
display="inline" display="inline"

@ -119,7 +119,7 @@ export type BaseQuestionCardProps = ActionButtonProps &
hideCard?: boolean; hideCard?: boolean;
questionId: string; questionId: string;
showHover?: boolean; showHover?: boolean;
timestamp: string | null; timestamp: Date | null;
truncateContent?: boolean; truncateContent?: boolean;
type: QuestionsQuestionType; type: QuestionsQuestionType;
}; };
@ -215,7 +215,14 @@ export default function BaseQuestionCard({
<QuestionAggregateBadge statistics={roles} variant="danger" /> <QuestionAggregateBadge statistics={roles} variant="danger" />
</> </>
)} )}
{timestamp !== null && <p className="text-xs">{timestamp}</p>} {timestamp !== null && (
<p className="text-xs">
{timestamp.toLocaleDateString(undefined, {
month: 'short',
year: 'numeric',
})}
</p>
)}
{showAddToList && ( {showAddToList && (
<div className="pl-4"> <div className="pl-4">
<AddToListDropdown questionId={questionId} /> <AddToListDropdown questionId={questionId} />
@ -317,7 +324,7 @@ 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 && 'hover:bg-slate-50', showHover && 'transition hover:bg-slate-50',
!hideCard && 'rounded-md border bg-white p-4', !hideCard && 'rounded-md border bg-white p-4',
)}> )}>
{cardContent} {cardContent}

@ -221,12 +221,7 @@ export default function ContributeQuestionForm({
createEncounterButtonText="Yes, this is my question" createEncounterButtonText="Yes, this is my question"
questionId={question.id} questionId={question.id}
roles={roleCounts} roles={roleCounts}
timestamp={ timestamp={question.seenAt}
question.seenAt.toLocaleDateString(undefined, {
month: 'short',
year: 'numeric',
}) ?? null
}
type={question.type} type={question.type}
onReceivedSubmit={async (data) => { onReceivedSubmit={async (data) => {
await addEncounterAsync({ await addEncounterAsync({

@ -210,10 +210,7 @@ export default function QuestionPage() {
questionId={question.id} questionId={question.id}
receivedCount={undefined} receivedCount={undefined}
roles={relabeledAggregatedEncounters?.roleCounts ?? {}} roles={relabeledAggregatedEncounters?.roleCounts ?? {}}
timestamp={question.seenAt.toLocaleDateString(undefined, { timestamp={question.seenAt}
month: 'short',
year: 'numeric',
})}
upvoteCount={question.numVotes} upvoteCount={question.numVotes}
onReceivedSubmit={async (data) => { onReceivedSubmit={async (data) => {
await addEncounterAsync({ await addEncounterAsync({

@ -558,13 +558,9 @@ export default function QuestionsBrowsePage() {
questionId={question.id} questionId={question.id}
receivedCount={question.receivedCount} receivedCount={question.receivedCount}
roles={roleCounts} roles={roleCounts}
timestamp={question.seenAt.toLocaleDateString( timestamp={
undefined, question.aggregatedQuestionEncounters.latestSeenAt
{ }
month: 'short',
year: 'numeric',
},
)}
type={question.type} type={question.type}
upvoteCount={question.numVotes} upvoteCount={question.numVotes}
/> />

@ -220,13 +220,7 @@ export default function ListPage() {
questionId={question.id} questionId={question.id}
receivedCount={question.receivedCount} receivedCount={question.receivedCount}
roles={roleCounts} roles={roleCounts}
timestamp={question.seenAt.toLocaleDateString( timestamp={question.seenAt}
undefined,
{
month: 'short',
year: 'numeric',
},
)}
type={question.type} type={question.type}
onDelete={() => { onDelete={() => {
deleteQuestionEntry({ id: entryId }); deleteQuestionEntry({ id: entryId });

@ -69,38 +69,38 @@ export const QUESTION_AGES: FilterChoices<QuestionAge> = [
] as const; ] as const;
export const SORT_ORDERS = [ export const SORT_ORDERS = [
{
label: 'Ascending',
value: SortOrder.ASC,
},
{ {
label: 'Descending', label: 'Descending',
value: SortOrder.DESC, value: SortOrder.DESC,
}, },
{
label: 'Ascending',
value: SortOrder.ASC,
},
]; ];
export const SORT_TYPES = [ export const SORT_TYPES = [
{ {
label: 'New', label: 'Upvotes',
value: SortType.NEW, value: SortType.TOP,
}, },
{ {
label: 'Top', label: 'Date',
value: SortType.TOP, value: SortType.NEW,
}, },
]; ];
export const QUESTION_SORT_TYPES = [ export const QUESTION_SORT_TYPES = [
{ {
label: 'New', label: 'Upvotes',
value: SortType.NEW, value: SortType.TOP,
}, },
{ {
label: 'Top', label: 'Age',
value: SortType.TOP, value: SortType.NEW,
}, },
{ {
label: 'Encounters', label: 'Received',
value: SortType.ENCOUNTERS, value: SortType.ENCOUNTERS,
}, },
]; ];

Loading…
Cancel
Save