[questions][ui] update create question encounter form

pull/411/head
Jeff Sieu 3 years ago
parent 8880e29b2d
commit 634a41a48a

@ -5,6 +5,7 @@ export type QuestionOverviewCardProps = Omit<
QuestionCardProps & { QuestionCardProps & {
showActionButton: false; showActionButton: false;
showAnswerStatistics: false; showAnswerStatistics: false;
showDeleteButton: false;
showReceivedStatistics: true; showReceivedStatistics: true;
showVoteButtons: true; showVoteButtons: true;
}, },
@ -24,6 +25,7 @@ export default function FullQuestionCard(props: QuestionOverviewCardProps) {
showAnswerStatistics={false} showAnswerStatistics={false}
showReceivedStatistics={true} showReceivedStatistics={true}
showVoteButtons={true} showVoteButtons={true}
truncateContent={false}
/> />
); );
} }

@ -1,6 +1,8 @@
import clsx from 'clsx';
import { useState } from 'react'; import { useState } from 'react';
import { import {
ChatBubbleBottomCenterTextIcon, ChatBubbleBottomCenterTextIcon,
CheckIcon,
EyeIcon, EyeIcon,
TrashIcon, TrashIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
@ -80,6 +82,7 @@ export type QuestionCardProps = ActionButtonProps &
role: string; role: string;
showHover?: boolean; showHover?: boolean;
timestamp: string; timestamp: string;
truncateContent?: boolean;
type: QuestionsQuestionType; type: QuestionsQuestionType;
}; };
@ -104,6 +107,7 @@ export default function QuestionCard({
onReceivedSubmit, onReceivedSubmit,
showDeleteButton, showDeleteButton,
onDelete, onDelete,
truncateContent = true,
}: QuestionCardProps) { }: QuestionCardProps) {
const [showReceivedForm, setShowReceivedForm] = useState(false); const [showReceivedForm, setShowReceivedForm] = useState(false);
const { handleDownvote, handleUpvote, vote } = useQuestionVote(questionId); const { handleDownvote, handleUpvote, vote } = useQuestionVote(questionId);
@ -147,7 +151,9 @@ export default function QuestionCard({
)} )}
</div> </div>
<div className="ml-2"> <div className="ml-2">
<p className="line-clamp-2 text-ellipsis ">{content}</p> <p className={clsx(truncateContent && 'line-clamp-2 text-ellipsis')}>
{content}
</p>
</div> </div>
{(showAnswerStatistics || showReceivedStatistics) && ( {(showAnswerStatistics || showReceivedStatistics) && (
<div className="flex gap-2"> <div className="flex gap-2">
@ -161,17 +167,26 @@ export default function QuestionCard({
/> />
)} )}
{showReceivedStatistics && ( {showReceivedStatistics && (
<Button <>
addonPosition="start" <Button
icon={EyeIcon} addonPosition="start"
label={`${receivedCount} received this`} icon={EyeIcon}
size="sm" label={`${receivedCount} received this`}
variant="tertiary" size="sm"
onClick={(event) => { variant="tertiary"
event.preventDefault(); />
setShowReceivedForm(true); <Button
}} addonPosition="start"
/> icon={CheckIcon}
label="I received this too"
size="sm"
variant="tertiary"
onClick={(event) => {
event.preventDefault();
setShowReceivedForm(true);
}}
/>
</>
)} )}
</div> </div>
)} )}

@ -6,15 +6,15 @@ import QuestionCard from './QuestionCard';
export type QuestionListCardProps = Omit< export type QuestionListCardProps = Omit<
QuestionCardProps & { QuestionCardProps & {
showActionButton: false; showActionButton: false;
showAnswerStatistics: false;
showDeleteButton: true; showDeleteButton: true;
showUserStatistics: false;
showVoteButtons: false; showVoteButtons: false;
}, },
| 'actionButtonLabel' | 'actionButtonLabel'
| 'onActionButtonClick' | 'onActionButtonClick'
| 'showActionButton' | 'showActionButton'
| 'showAnswerStatistics'
| 'showDeleteButton' | 'showDeleteButton'
| 'showUserStatistics'
| 'showVoteButtons' | 'showVoteButtons'
>; >;
@ -23,9 +23,9 @@ function QuestionListCardWithoutHref(props: QuestionListCardProps) {
<QuestionCard <QuestionCard
{...props} {...props}
showActionButton={false} showActionButton={false}
showAnswerStatistics={false}
showDeleteButton={true} showDeleteButton={true}
showHover={true} showHover={true}
showUserStatistics={false}
showVoteButtons={false} showVoteButtons={false}
/> />
); );

@ -8,7 +8,6 @@ import {
HorizontalDivider, HorizontalDivider,
Select, Select,
TextArea, TextArea,
Typeahead,
} from '@tih/ui'; } from '@tih/ui';
import { LOCATIONS, QUESTION_TYPES, ROLES } from '~/utils/questions/constants'; import { LOCATIONS, QUESTION_TYPES, ROLES } from '~/utils/questions/constants';

@ -8,6 +8,7 @@ import MonthYearPicker from '~/components/shared/MonthYearPicker';
import LocationTypeahead from '../typeahead/LocationTypeahead'; import LocationTypeahead from '../typeahead/LocationTypeahead';
import RoleTypeahead from '../typeahead/RoleTypeahead'; import RoleTypeahead from '../typeahead/RoleTypeahead';
import CompanyTypeahead from '../typeahead/CompanyTypeahead';
export type CreateQuestionEncounterData = { export type CreateQuestionEncounterData = {
company: string; company: string;
@ -25,6 +26,8 @@ export default function CreateQuestionEncounterForm({
onCancel, onCancel,
onSubmit, onSubmit,
}: CreateQuestionEncounterFormProps) { }: CreateQuestionEncounterFormProps) {
const [step, setStep] = useState(0);
const [selectedCompany, setSelectedCompany] = useState<string | null>(null); const [selectedCompany, setSelectedCompany] = useState<string | null>(null);
const [selectedLocation, setSelectedLocation] = useState<string | null>(null); const [selectedLocation, setSelectedLocation] = useState<string | null>(null);
const [selectedRole, setSelectedRole] = useState<string | null>(null); const [selectedRole, setSelectedRole] = useState<string | null>(null);
@ -43,67 +46,104 @@ export default function CreateQuestionEncounterForm({
}} }}
/> />
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<p className="font-md text-xl text-slate-600">I saw this question at</p> <p className="font-md text-md text-slate-600">I saw this question at</p>
<div> {step === 0 && (
<CompaniesTypeahead <div>
isLabelHidden={true} <CompanyTypeahead
onSelect={({ value: company }) => { placeholder="Other company"
setSelectedCompany(company); suggestedCount={3}
onSuggestionClick={({ value: company }) => {
setSelectedCompany(company);
setStep(step + 1);
}}
isLabelHidden={true}
onSelect={({ value: company }) => {
setSelectedCompany(company);
}}
/>
</div>
)}
{step === 1 && (
<div>
<LocationTypeahead
suggestedCount={3}
onSuggestionClick={({ value: location }) => {
setSelectedLocation(location);
setStep(step + 1);
}}
isLabelHidden={true}
placeholder="Other location"
// eslint-disable-next-line @typescript-eslint/no-empty-function
onQueryChange={() => {}}
onSelect={({ value: location }) => {
setSelectedLocation(location);
}}
/>
</div>
)}
{step === 2 && (
<div>
<RoleTypeahead
isLabelHidden={true}
suggestedCount={3}
onSuggestionClick={({ value: role }) => {
setSelectedRole(role);
setStep(step + 1);
}}
placeholder="Other role"
// eslint-disable-next-line @typescript-eslint/no-empty-function
onQueryChange={() => {}}
onSelect={({ value: role }) => {
setSelectedRole(role);
}}
/>
</div>
)}
{step === 3 && (
<MonthYearPicker
yearLabel={''}
monthLabel={''}
value={{
month: ((selectedDate?.getMonth() ?? 0) + 1) as Month,
year: selectedDate?.getFullYear() as number,
}}
onChange={(value) => {
setSelectedDate(
startOfMonth(new Date(value.year, value.month - 1)),
);
}} }}
/> />
</div> )}
<div> {step < 3 && (
<LocationTypeahead <Button
isLabelHidden={true} label="Next"
// eslint-disable-next-line @typescript-eslint/no-empty-function variant="primary"
onQueryChange={() => {}} onClick={() => {
onSelect={({ value: location }) => { setStep(step + 1);
setSelectedLocation(location);
}} }}
/> />
</div> )}
<p className="font-md text-xl text-slate-600">for</p> {step === 3 && (
<div> <Button
<RoleTypeahead label="Submit"
isLabelHidden={true} variant="primary"
// eslint-disable-next-line @typescript-eslint/no-empty-function onClick={() => {
onQueryChange={() => {}} if (
onSelect={({ value: role }) => { selectedCompany &&
setSelectedRole(role); selectedLocation &&
selectedRole &&
selectedDate
) {
onSubmit({
company: selectedCompany,
location: selectedLocation,
role: selectedRole,
seenAt: selectedDate,
});
}
}} }}
/> />
</div> )}
<p className="font-md text-xl text-slate-600">for</p>
<MonthYearPicker
value={{
month: ((selectedDate?.getMonth() ?? 0) + 1) as Month,
year: selectedDate?.getFullYear() as number,
}}
onChange={(value) => {
setSelectedDate(
startOfMonth(new Date(value.year, value.month - 1)),
);
}}
/>
<Button
label="Submit"
variant="primary"
onClick={() => {
if (
selectedCompany &&
selectedLocation &&
selectedRole &&
selectedDate
) {
onSubmit({
company: selectedCompany,
location: selectedLocation,
role: selectedRole,
seenAt: selectedDate,
});
}
}}
/>
</div> </div>
</div> </div>
); );

@ -0,0 +1,41 @@
import { useMemo, useState } from 'react';
import { trpc } from '~/utils/trpc';
import type { ExpandedTypeaheadProps } from './ExpandedTypeahead';
import ExpandedTypeahead from './ExpandedTypeahead';
export type CompanyTypeaheadProps = Omit<
ExpandedTypeaheadProps,
'label' | 'onQueryChange' | 'options'
>;
export default function CompanyTypeahead(props: CompanyTypeaheadProps) {
const [query, setQuery] = useState('');
const { data: companies } = trpc.useQuery([
'companies.list',
{
name: query,
},
]);
const companyOptions = useMemo(() => {
return (
companies?.map(({ id, name }) => ({
id,
label: name,
value: id,
})) ?? []
);
}, [companies]);
return (
<ExpandedTypeahead
{...(props as ExpandedTypeaheadProps)}
label="Company"
options={companyOptions}
onQueryChange={setQuery}
/>
);
}

@ -0,0 +1,37 @@
import type { ComponentProps } from 'react';
import { Button, Typeahead } from '@tih/ui';
type RequireAllOrNone<T> = T | { [K in keyof T]?: never };
type TypeaheadProps = ComponentProps<typeof Typeahead>;
type TypeaheadOption = TypeaheadProps['options'][number];
export type ExpandedTypeaheadProps = RequireAllOrNone<{
onSuggestionClick: (option: TypeaheadOption) => void;
suggestedCount: number;
}> &
TypeaheadProps;
export default function ExpandedTypeahead({
suggestedCount = 0,
onSuggestionClick,
...typeaheadProps
}: ExpandedTypeaheadProps) {
const suggestions = typeaheadProps.options.slice(0, suggestedCount);
return (
<div className="flex flex-wrap gap-x-2">
{suggestions.map((suggestion) => (
<Button
key={suggestion.id}
label={suggestion.label}
variant="tertiary"
onClick={() => {
onSuggestionClick?.(suggestion);
}}
/>
))}
<Typeahead {...typeaheadProps} />
</div>
);
}

@ -1,12 +1,19 @@
import type { ComponentProps } from 'react';
import { Typeahead } from '@tih/ui';
import { LOCATIONS } from '~/utils/questions/constants'; import { LOCATIONS } from '~/utils/questions/constants';
type TypeaheadProps = ComponentProps<typeof Typeahead>; import type { ExpandedTypeaheadProps } from './ExpandedTypeahead';
import ExpandedTypeahead from './ExpandedTypeahead';
export type LocationTypeaheadProps = Omit<TypeaheadProps, 'label' | 'options'>; export type LocationTypeaheadProps = Omit<
ExpandedTypeaheadProps,
'label' | 'options'
>;
export default function LocationTypeahead(props: LocationTypeaheadProps) { export default function LocationTypeahead(props: LocationTypeaheadProps) {
return <Typeahead label="Location" options={LOCATIONS} {...props} />; return (
<ExpandedTypeahead
{...(props as ExpandedTypeaheadProps)}
label="Location"
options={LOCATIONS}
/>
);
} }

@ -1,12 +1,19 @@
import type { ComponentProps } from 'react';
import { Typeahead } from '@tih/ui';
import { ROLES } from '~/utils/questions/constants'; import { ROLES } from '~/utils/questions/constants';
type TypeaheadProps = ComponentProps<typeof Typeahead>; import type { ExpandedTypeaheadProps } from './ExpandedTypeahead';
import ExpandedTypeahead from './ExpandedTypeahead';
export type RoleTypeaheadProps = Omit<TypeaheadProps, 'label' | 'options'>; export type RoleTypeaheadProps = Omit<
ExpandedTypeaheadProps,
'label' | 'options'
>;
export default function RoleTypeahead(props: RoleTypeaheadProps) { export default function RoleTypeahead(props: RoleTypeaheadProps) {
return <Typeahead label="Role" options={ROLES} {...props} />; return (
<ExpandedTypeahead
{...(props as ExpandedTypeaheadProps)}
label="Role"
options={ROLES}
/>
);
} }

Loading…
Cancel
Save