From 975568cb77161f6635cc2e2f3e2099c40696c86f Mon Sep 17 00:00:00 2001 From: wlren Date: Thu, 20 Oct 2022 11:24:36 +0800 Subject: [PATCH] [questions][ui] lists hover delete --- .../questions/card/QuestionCard.tsx | 33 +- .../questions/card/QuestionListCard.tsx | 35 ++ .../questions/card/QuestionOverviewCard.tsx | 4 + apps/portal/src/pages/questions/lists.tsx | 352 ++---------------- 4 files changed, 102 insertions(+), 322 deletions(-) create mode 100644 apps/portal/src/components/questions/card/QuestionListCard.tsx diff --git a/apps/portal/src/components/questions/card/QuestionCard.tsx b/apps/portal/src/components/questions/card/QuestionCard.tsx index bcec3f21..63ba7a5a 100644 --- a/apps/portal/src/components/questions/card/QuestionCard.tsx +++ b/apps/portal/src/components/questions/card/QuestionCard.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { ChatBubbleBottomCenterTextIcon, EyeIcon, + TrashIcon, } from '@heroicons/react/24/outline'; import type { QuestionsQuestionType } from '@prisma/client'; import { Badge, Button } from '@tih/ui'; @@ -23,6 +24,16 @@ type UpvoteProps = upvoteCount?: never; }; +type DeleteProps = + | { + onDelete: () => void; + showDeleteButton: true; + } + | { + onDelete?: never; + showDeleteButton?: false; + }; + type AnswerStatisticsProps = | { answerCount: number; @@ -59,6 +70,7 @@ type ReceivedStatisticsProps = export type QuestionCardProps = ActionButtonProps & AnswerStatisticsProps & + DeleteProps & ReceivedStatisticsProps & UpvoteProps & { company: string; @@ -90,11 +102,12 @@ export default function QuestionCard({ location, showHover, onReceivedSubmit, + showDeleteButton, + onDelete, }: QuestionCardProps) { const [showReceivedForm, setShowReceivedForm] = useState(false); const { handleDownvote, handleUpvote, vote } = useQuestionVote(questionId); const hoverClass = showHover ? 'hover:bg-slate-50' : ''; - const cardContent = showReceivedForm ? ( { @@ -168,8 +181,24 @@ export default function QuestionCard({ return (
+ className={`group flex gap-4 rounded-md border border-slate-300 bg-white p-4 ${hoverClass}`}> {cardContent} + {showDeleteButton && ( +
+
+ )}
); } diff --git a/apps/portal/src/components/questions/card/QuestionListCard.tsx b/apps/portal/src/components/questions/card/QuestionListCard.tsx new file mode 100644 index 00000000..f10431a6 --- /dev/null +++ b/apps/portal/src/components/questions/card/QuestionListCard.tsx @@ -0,0 +1,35 @@ +import withHref from '~/utils/questions/withHref'; + +import type { QuestionCardProps } from './QuestionCard'; +import QuestionCard from './QuestionCard'; + +export type QuestionListCardProps = Omit< + QuestionCardProps & { + showActionButton: false; + showDeleteButton: true; + showUserStatistics: false; + showVoteButtons: false; + }, + | 'actionButtonLabel' + | 'onActionButtonClick' + | 'showActionButton' + | 'showDeleteButton' + | 'showUserStatistics' + | 'showVoteButtons' +>; + +function QuestionListCardWithoutHref(props: QuestionListCardProps) { + return ( + + ); +} + +const QuestionListCard = withHref(QuestionListCardWithoutHref); +export default QuestionListCard; diff --git a/apps/portal/src/components/questions/card/QuestionOverviewCard.tsx b/apps/portal/src/components/questions/card/QuestionOverviewCard.tsx index ff4708f9..77c0922d 100644 --- a/apps/portal/src/components/questions/card/QuestionOverviewCard.tsx +++ b/apps/portal/src/components/questions/card/QuestionOverviewCard.tsx @@ -7,13 +7,16 @@ export type QuestionOverviewCardProps = Omit< QuestionCardProps & { showActionButton: false; showAnswerStatistics: true; + showDeleteButton: false; showReceivedStatistics: true; showVoteButtons: true; }, | 'actionButtonLabel' | 'onActionButtonClick' + | 'onDelete' | 'showActionButton' | 'showAnswerStatistics' + | 'showDeleteButton' | 'showReceivedStatistics' | 'showVoteButtons' >; @@ -24,6 +27,7 @@ function QuestionOverviewCardWithoutHref(props: QuestionOverviewCardProps) { {...props} showActionButton={false} showAnswerStatistics={true} + showDeleteButton={false} showHover={true} showReceivedStatistics={true} showVoteButtons={true} diff --git a/apps/portal/src/pages/questions/lists.tsx b/apps/portal/src/pages/questions/lists.tsx index 93ac9725..5447807e 100644 --- a/apps/portal/src/pages/questions/lists.tsx +++ b/apps/portal/src/pages/questions/lists.tsx @@ -1,331 +1,43 @@ -import { subMonths, subYears } from 'date-fns'; -import Router, { useRouter } from 'next/router'; -import { useEffect, useMemo, useState } from 'react'; import { NoSymbolIcon } from '@heroicons/react/24/outline'; -import type { QuestionsQuestionType } from '@prisma/client'; -import { SlideOut } from '@tih/ui'; -import QuestionOverviewCard from '~/components/questions/card/QuestionOverviewCard'; -import ContributeQuestionCard from '~/components/questions/ContributeQuestionCard'; -import FilterSection from '~/components/questions/filter/FilterSection'; -import type { LandingQueryData } from '~/components/questions/LandingComponent'; -import LandingComponent from '~/components/questions/LandingComponent'; -import QuestionSearchBar from '~/components/questions/QuestionSearchBar'; +import QuestionListCard from '~/components/questions/card/QuestionListCard'; -import type { QuestionAge } from '~/utils/questions/constants'; -import { - COMPANIES, - LOCATIONS, - QUESTION_AGES, - QUESTION_TYPES, -} from '~/utils/questions/constants'; +import { SAMPLE_QUESTION } from '~/utils/questions/constants'; import createSlug from '~/utils/questions/createSlug'; -import { - useSearchFilter, - useSearchFilterSingle, -} from '~/utils/questions/useSearchFilter'; -import { trpc } from '~/utils/trpc'; -export default function QuestionsHomePage() { - const router = useRouter(); +export default function ListPage() { + const questions = [SAMPLE_QUESTION, SAMPLE_QUESTION, SAMPLE_QUESTION]; - const [selectedCompanies, setSelectedCompanies, areCompaniesInitialized] = - useSearchFilter('companies'); - const [ - selectedQuestionTypes, - setSelectedQuestionTypes, - areQuestionTypesInitialized, - ] = useSearchFilter('questionTypes', { - queryParamToValue: (param) => { - return param.toUpperCase() as QuestionsQuestionType; - }, - }); - const [ - selectedQuestionAge, - setSelectedQuestionAge, - isQuestionAgeInitialized, - ] = useSearchFilterSingle('questionAge', { - defaultValue: 'all', - }); - const [selectedLocations, setSelectedLocations, areLocationsInitialized] = - useSearchFilter('locations'); - - const today = useMemo(() => new Date(), []); - const startDate = useMemo(() => { - return selectedQuestionAge === 'last-year' - ? subYears(new Date(), 1) - : selectedQuestionAge === 'last-6-months' - ? subMonths(new Date(), 6) - : selectedQuestionAge === 'last-month' - ? subMonths(new Date(), 1) - : undefined; - }, [selectedQuestionAge]); - - const { data: questions } = trpc.useQuery( - [ - 'questions.questions.getQuestionsByFilter', - { - companies: selectedCompanies, - endDate: today, - locations: selectedLocations, - questionTypes: selectedQuestionTypes, - roles: [], - startDate, - }, - ], - { - keepPreviousData: true, - }, - ); - - const utils = trpc.useContext(); - const { mutate: createQuestion } = trpc.useMutation( - 'questions.questions.create', - { - onSuccess: () => { - utils.invalidateQueries('questions.questions.getQuestionsByFilter'); - }, - }, - ); - - const [hasLanded, setHasLanded] = useState(false); - const [loaded, setLoaded] = useState(false); - const [filterDrawerOpen, setFilterDrawerOpen] = useState(false); - - const companyFilterOptions = useMemo(() => { - return COMPANIES.map((company) => ({ - ...company, - checked: selectedCompanies.includes(company.value), - })); - }, [selectedCompanies]); - - const questionTypeFilterOptions = useMemo(() => { - return QUESTION_TYPES.map((questionType) => ({ - ...questionType, - checked: selectedQuestionTypes.includes(questionType.value), - })); - }, [selectedQuestionTypes]); - - const questionAgeFilterOptions = useMemo(() => { - return QUESTION_AGES.map((questionAge) => ({ - ...questionAge, - checked: selectedQuestionAge === questionAge.value, - })); - }, [selectedQuestionAge]); - - const locationFilterOptions = useMemo(() => { - return LOCATIONS.map((location) => ({ - ...location, - checked: selectedLocations.includes(location.value), - })); - }, [selectedLocations]); - - const handleLandingQuery = async (data: LandingQueryData) => { - const { company, location, questionType } = data; - - setSelectedCompanies([company]); - setSelectedLocations([location]); - setSelectedQuestionTypes([questionType as QuestionsQuestionType]); - setHasLanded(true); - }; - - const areFiltersInitialized = useMemo(() => { - return ( - areCompaniesInitialized && - areQuestionTypesInitialized && - isQuestionAgeInitialized && - areLocationsInitialized - ); - }, [ - areCompaniesInitialized, - areQuestionTypesInitialized, - isQuestionAgeInitialized, - areLocationsInitialized, - ]); - - const { pathname } = router; - useEffect(() => { - if (areFiltersInitialized) { - // Router.replace used instead of router.replace to avoid - // the page reloading itself since the router.replace - // callback changes on every page load - Router.replace({ - pathname, - query: { - companies: selectedCompanies, - locations: selectedLocations, - questionAge: selectedQuestionAge, - questionTypes: selectedQuestionTypes, - }, - }); - const hasFilter = - selectedCompanies.length > 0 || - selectedLocations.length > 0 || - selectedQuestionAge !== 'all' || - selectedQuestionTypes.length > 0; - if (hasFilter) { - setHasLanded(true); - } - - setLoaded(true); - } - }, [ - areFiltersInitialized, - hasLanded, - loaded, - pathname, - selectedCompanies, - selectedLocations, - selectedQuestionAge, - selectedQuestionTypes, - ]); - - if (!loaded) { - return null; - } - const filterSidebar = ( -
- { - if (checked) { - setSelectedCompanies([...selectedCompanies, optionValue]); - } else { - setSelectedCompanies( - selectedCompanies.filter((company) => company !== optionValue), - ); - } - }} - /> - { - if (checked) { - setSelectedQuestionTypes([...selectedQuestionTypes, optionValue]); - } else { - setSelectedQuestionTypes( - selectedQuestionTypes.filter( - (questionType) => questionType !== optionValue, - ), - ); - } - }} - /> - { - setSelectedQuestionAge(optionValue); - }} - /> - { - if (checked) { - setSelectedLocations([...selectedLocations, optionValue]); - } else { - setSelectedLocations( - selectedLocations.filter((location) => location !== optionValue), - ); - } - }} - /> -
- ); - - return !hasLanded ? ( - - ) : ( + return (
-
-
-
-
- { - createQuestion({ - company: data.company, - content: data.questionContent, - location: data.location, - questionType: data.questionType, - role: data.role, - seenAt: data.date, - }); - }} - /> - { - setFilterDrawerOpen(!filterDrawerOpen); - }} - onSortChange={(value) => { - // eslint-disable-next-line no-console - console.log(value); - }} - /> - {(questions ?? []).map((question) => ( - - ))} - {questions?.length === 0 && ( -
- -

Nothing found. Try changing your search filters.

-
- )} -
+
+ {(questions ?? []).map((question) => ( + { + // eslint-disable-next-line no-console + console.log('delete'); + }} + /> + ))} + {questions?.length === 0 && ( +
+ +

Your list is empty.

-
- - { - setFilterDrawerOpen(false); - }}> - {filterSidebar} - + )}
);