diff --git a/apps/portal/src/components/questions/QuestionAggregateBadge.tsx b/apps/portal/src/components/questions/QuestionAggregateBadge.tsx new file mode 100644 index 00000000..57ab330b --- /dev/null +++ b/apps/portal/src/components/questions/QuestionAggregateBadge.tsx @@ -0,0 +1,39 @@ +import type { ComponentProps } from 'react'; +import { useMemo } from 'react'; +import { Badge } from '@tih/ui'; + +type BadgeProps = ComponentProps; + +export type QuestionAggregateBadgeProps = Omit & { + statistics: Record; +}; + +export default function QuestionAggregateBadge({ + statistics, + ...badgeProps +}: QuestionAggregateBadgeProps) { + const mostCommonStatistic = useMemo( + () => + Object.entries(statistics).reduce( + (mostCommon, [key, value]) => { + if (value > mostCommon.value) { + return { key, value }; + } + return mostCommon; + }, + { key: '', value: 0 }, + ), + [statistics], + ); + + const additionalStatisticCount = Object.keys(statistics).length - 1; + + const label = useMemo(() => { + if (additionalStatisticCount === 0) { + return mostCommonStatistic.key; + } + return `${mostCommonStatistic.key} (+${additionalStatisticCount})`; + }, [mostCommonStatistic, additionalStatisticCount]); + + return ; +} diff --git a/apps/portal/src/components/questions/card/SimilarQuestionCard.tsx b/apps/portal/src/components/questions/card/SimilarQuestionCard.tsx deleted file mode 100644 index 063bdf91..00000000 --- a/apps/portal/src/components/questions/card/SimilarQuestionCard.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { QuestionCardProps } from './QuestionCard'; -import QuestionCard from './QuestionCard'; - -export type SimilarQuestionCardProps = Omit< - QuestionCardProps & { - showActionButton: true; - showUserStatistics: false; - showVoteButtons: false; - }, - | 'actionButtonLabel' - | 'answerCount' - | 'onActionButtonClick' - | 'showActionButton' - | 'showUserStatistics' - | 'showVoteButtons' - | 'upvoteCount' -> & { - onSimilarQuestionClick: () => void; -}; - -export default function SimilarQuestionCard(props: SimilarQuestionCardProps) { - const { onSimilarQuestionClick, ...rest } = props; - return ( - - ); -} diff --git a/apps/portal/src/components/questions/card/QuestionCard.tsx b/apps/portal/src/components/questions/card/question/BaseQuestionCard.tsx similarity index 71% rename from apps/portal/src/components/questions/card/QuestionCard.tsx rename to apps/portal/src/components/questions/card/question/BaseQuestionCard.tsx index effe2190..3c54d58b 100644 --- a/apps/portal/src/components/questions/card/QuestionCard.tsx +++ b/apps/portal/src/components/questions/card/question/BaseQuestionCard.tsx @@ -7,14 +7,15 @@ import { TrashIcon, } from '@heroicons/react/24/outline'; import type { QuestionsQuestionType } from '@prisma/client'; -import { Badge, Button } from '@tih/ui'; +import { Button } from '@tih/ui'; import { useQuestionVote } from '~/utils/questions/useVote'; -import type { CreateQuestionEncounterData } from '../forms/CreateQuestionEncounterForm'; -import CreateQuestionEncounterForm from '../forms/CreateQuestionEncounterForm'; -import QuestionTypeBadge from '../QuestionTypeBadge'; -import VotingButtons from '../VotingButtons'; +import type { CreateQuestionEncounterData } from '../../forms/CreateQuestionEncounterForm'; +import CreateQuestionEncounterForm from '../../forms/CreateQuestionEncounterForm'; +import QuestionAggregateBadge from '../../QuestionAggregateBadge'; +import QuestionTypeBadge from '../../QuestionTypeBadge'; +import VotingButtons from '../../VotingButtons'; type UpvoteProps = | { @@ -60,35 +61,44 @@ type ActionButtonProps = type ReceivedStatisticsProps = | { - onReceivedSubmit: (data: CreateQuestionEncounterData) => void; receivedCount: number; showReceivedStatistics: true; } | { - onReceivedSubmit?: never; receivedCount?: never; showReceivedStatistics?: false; }; -export type QuestionCardProps = ActionButtonProps & +type CreateEncounterProps = + | { + onReceivedSubmit: (data: CreateQuestionEncounterData) => void; + showCreateEncounterButton: true; + } + | { + onReceivedSubmit?: never; + showCreateEncounterButton?: false; + }; + +export type BaseQuestionCardProps = ActionButtonProps & AnswerStatisticsProps & + CreateEncounterProps & DeleteProps & ReceivedStatisticsProps & UpvoteProps & { - company: string; + companies: Record; content: string; - location: string; + locations: Record; questionId: string; - role: string; + roles: Record; showHover?: boolean; timestamp: string; truncateContent?: boolean; type: QuestionsQuestionType; }; -export default function QuestionCard({ +export default function BaseQuestionCard({ questionId, - company, + companies, answerCount, content, receivedCount, @@ -96,19 +106,20 @@ export default function QuestionCard({ showVoteButtons, showAnswerStatistics, showReceivedStatistics, + showCreateEncounterButton, showActionButton, actionButtonLabel, onActionButtonClick, upvoteCount, timestamp, - role, - location, + roles, + locations, showHover, onReceivedSubmit, showDeleteButton, onDelete, truncateContent = true, -}: QuestionCardProps) { +}: BaseQuestionCardProps) { const [showReceivedForm, setShowReceivedForm] = useState(false); const { handleDownvote, handleUpvote, vote } = useQuestionVote(questionId); const hoverClass = showHover ? 'hover:bg-slate-50' : ''; @@ -125,11 +136,11 @@ export default function QuestionCard({
- -

- {timestamp} · {location} · {role} -

+ + + +

{timestamp}

{showActionButton && (
- )} + )} +
+ )} {showReceivedForm && ( { diff --git a/apps/portal/src/components/questions/card/FullQuestionCard.tsx b/apps/portal/src/components/questions/card/question/FullQuestionCard.tsx similarity index 60% rename from apps/portal/src/components/questions/card/FullQuestionCard.tsx rename to apps/portal/src/components/questions/card/question/FullQuestionCard.tsx index 57245e1e..c99b4409 100644 --- a/apps/portal/src/components/questions/card/FullQuestionCard.tsx +++ b/apps/portal/src/components/questions/card/question/FullQuestionCard.tsx @@ -1,29 +1,33 @@ -import type { QuestionCardProps } from './QuestionCard'; -import QuestionCard from './QuestionCard'; +import type { BaseQuestionCardProps } from './BaseQuestionCard'; +import BaseQuestionCard from './BaseQuestionCard'; export type QuestionOverviewCardProps = Omit< - QuestionCardProps & { + BaseQuestionCardProps & { showActionButton: false; showAnswerStatistics: false; + showCreateEncounterButton: true; showDeleteButton: false; - showReceivedStatistics: true; + showReceivedStatistics: false; showVoteButtons: true; }, | 'actionButtonLabel' | 'onActionButtonClick' | 'showActionButton' | 'showAnswerStatistics' + | 'showCreateEncounterButton' + | 'showDeleteButton' | 'showReceivedStatistics' | 'showVoteButtons' >; export default function FullQuestionCard(props: QuestionOverviewCardProps) { return ( - diff --git a/apps/portal/src/components/questions/card/QuestionListCard.tsx b/apps/portal/src/components/questions/card/question/QuestionListCard.tsx similarity index 75% rename from apps/portal/src/components/questions/card/QuestionListCard.tsx rename to apps/portal/src/components/questions/card/question/QuestionListCard.tsx index d340a2ea..b7b74cfa 100644 --- a/apps/portal/src/components/questions/card/QuestionListCard.tsx +++ b/apps/portal/src/components/questions/card/question/QuestionListCard.tsx @@ -1,10 +1,10 @@ import withHref from '~/utils/questions/withHref'; -import type { QuestionCardProps } from './QuestionCard'; -import QuestionCard from './QuestionCard'; +import type { BaseQuestionCardProps } from './BaseQuestionCard'; +import BaseQuestionCard from './BaseQuestionCard'; export type QuestionListCardProps = Omit< - QuestionCardProps & { + BaseQuestionCardProps & { showActionButton: false; showAnswerStatistics: false; showDeleteButton: true; @@ -20,7 +20,8 @@ export type QuestionListCardProps = Omit< function QuestionListCardWithoutHref(props: QuestionListCardProps) { return ( - & { + onSimilarQuestionClick: () => void; +}; + +export default function SimilarQuestionCard(props: SimilarQuestionCardProps) { + const { onSimilarQuestionClick, ...rest } = props; + return ( + + ); +} diff --git a/apps/portal/src/components/questions/forms/CreateQuestionEncounterForm.tsx b/apps/portal/src/components/questions/forms/CreateQuestionEncounterForm.tsx index bd4dd4d3..4524c428 100644 --- a/apps/portal/src/components/questions/forms/CreateQuestionEncounterForm.tsx +++ b/apps/portal/src/components/questions/forms/CreateQuestionEncounterForm.tsx @@ -5,9 +5,9 @@ import { Button } from '@tih/ui'; import type { Month } from '~/components/shared/MonthYearPicker'; import MonthYearPicker from '~/components/shared/MonthYearPicker'; +import CompanyTypeahead from '../typeahead/CompanyTypeahead'; import LocationTypeahead from '../typeahead/LocationTypeahead'; import RoleTypeahead from '../typeahead/RoleTypeahead'; -import CompanyTypeahead from '../typeahead/CompanyTypeahead'; export type CreateQuestionEncounterData = { company: string; @@ -40,15 +40,15 @@ export default function CreateQuestionEncounterForm({ {step === 0 && (
{ + onSelect={({ value: company }) => { setSelectedCompany(company); - setStep(step + 1); }} - isLabelHidden={true} - onSelect={({ value: company }) => { + onSuggestionClick={({ value: company }) => { setSelectedCompany(company); + setStep(step + 1); }} />
@@ -56,18 +56,18 @@ export default function CreateQuestionEncounterForm({ {step === 1 && (
{ - setSelectedLocation(location); - setStep(step + 1); - }} isLabelHidden={true} placeholder="Other location" + suggestedCount={3} // eslint-disable-next-line @typescript-eslint/no-empty-function onQueryChange={() => {}} onSelect={({ value: location }) => { setSelectedLocation(location); }} + onSuggestionClick={({ value: location }) => { + setSelectedLocation(location); + setStep(step + 1); + }} />
)} @@ -75,28 +75,28 @@ export default function CreateQuestionEncounterForm({
{ - setSelectedRole(role); - setStep(step + 1); - }} placeholder="Other role" + suggestedCount={3} // eslint-disable-next-line @typescript-eslint/no-empty-function onQueryChange={() => {}} onSelect={({ value: role }) => { setSelectedRole(role); }} + onSuggestionClick={({ value: role }) => { + setSelectedRole(role); + setStep(step + 1); + }} />
)} {step === 3 && ( { setSelectedDate( startOfMonth(new Date(value.year, value.month - 1)), @@ -106,6 +106,11 @@ export default function CreateQuestionEncounterForm({ )} {step < 3 && (