From c76607acfed3ddbfafc4002cf9eaa8b51d243769 Mon Sep 17 00:00:00 2001 From: Zhang Ziqing <69516975+ziqing26@users.noreply.github.com> Date: Thu, 3 Nov 2022 10:59:09 +0800 Subject: [PATCH] [offers][feat] add default filters and more income columns (#495) * [offers][feat] add yoe query param and display all by default * [offers][feat] add base bonus stocks to table and default homepage * [offers][style] style loading spinner --- .../src/components/offers/table/OffersRow.tsx | 39 +++++-- .../components/offers/table/OffersTable.tsx | 102 ++++++++++++++---- .../src/components/offers/table/types.ts | 16 ++- apps/portal/src/pages/offers/index.tsx | 26 ++--- .../pages/offers/profile/[offerProfileId].tsx | 2 +- .../offers/submit/result/[offerProfileId].tsx | 2 +- 6 files changed, 142 insertions(+), 45 deletions(-) diff --git a/apps/portal/src/components/offers/table/OffersRow.tsx b/apps/portal/src/components/offers/table/OffersRow.tsx index a5f51208..77c7ab44 100644 --- a/apps/portal/src/components/offers/table/OffersRow.tsx +++ b/apps/portal/src/components/offers/table/OffersRow.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx'; import Link from 'next/link'; +import { JobType } from '@prisma/client'; import type { JobTitleType } from '~/components/shared/JobTitles'; import { getLabelForJobTitleType } from '~/components/shared/JobTitles'; @@ -9,25 +10,47 @@ import { formatDate } from '~/utils/offers/time'; import type { DashboardOffer } from '~/types/offers'; -export type OfferTableRowProps = Readonly<{ row: DashboardOffer }>; +export type OfferTableRowProps = Readonly<{ + jobType: JobType; + row: DashboardOffer; +}>; export default function OfferTableRow({ - row: { company, id, income, monthYearReceived, profileId, title, totalYoe }, + jobType, + row: { + baseSalary, + bonus, + company, + id, + income, + monthYearReceived, + profileId, + stocks, + title, + totalYoe, + }, }: OfferTableRowProps) { return ( - + {company.name} - + {getLabelForJobTitleType(title as JobTitleType)} - {totalYoe} - {convertMoneyToString(income)} - {formatDate(monthYearReceived)} + {totalYoe} + {convertMoneyToString(income)} + {jobType === JobType.FULLTIME && ( + + {`${baseSalary && convertMoneyToString(baseSalary)} / ${ + bonus && convertMoneyToString(bonus) + } / ${stocks && convertMoneyToString(stocks)}`} + + )} + {formatDate(monthYearReceived)} ; export default function OffersTable({ - cityFilter, + countryFilter, companyFilter, jobTitleFilter, }: OffersTableProps) { const [currency, setCurrency] = useState(Currency.SGD.toString()); // TODO: Detect location - const [selectedYoe, setSelectedYoe] = useState(YOE_CATEGORY.ENTRY); + const [selectedYoe, setSelectedYoe] = useState(''); + const [jobType, setJobType] = useState(JobType.FULLTIME); const [pagination, setPagination] = useState({ currentPage: 0, numOfItems: 0, @@ -43,6 +47,10 @@ export default function OffersTable({ OfferTableFilterOptions[0].value, ); const { event: gaEvent } = useGoogleAnalytics(); + const router = useRouter(); + const { yoeCategory = '' } = router.query; + const [isLoading, setIsLoading] = useState(true); + useEffect(() => { setPagination({ currentPage: 0, @@ -50,20 +58,26 @@ export default function OffersTable({ numOfPages: 0, totalItems: 0, }); - }, [selectedYoe, currency]); - const offersQuery = trpc.useQuery( + setIsLoading(true); + }, [selectedYoe, currency, countryFilter, companyFilter, jobTitleFilter]); + + useEffect(() => { + setSelectedYoe(yoeCategory as YOE_CATEGORY); + event?.preventDefault(); + }, [yoeCategory]); + + trpc.useQuery( [ 'offers.list', { companyId: companyFilter, - // Location: 'Singapore, Singapore', // TODO: Geolocation - countryId: cityFilter, + countryId: countryFilter, currency, limit: NUMBER_OF_OFFERS_IN_PAGE, offset: pagination.currentPage, sortBy: OfferTableSortBy[selectedFilter] ?? '-monthYearReceived', title: jobTitleFilter, - yoeCategory: selectedYoe, + yoeCategory: YOE_CATEGORY_PARAM[yoeCategory as string] ?? undefined, }, ], { @@ -73,6 +87,8 @@ export default function OffersTable({ onSuccess: (response: GetOffersResponse) => { setOffers(response.data); setPagination(response.paging); + setJobType(response.jobType); + setIsLoading(false); }, }, ); @@ -80,14 +96,43 @@ export default function OffersTable({ function renderFilters() { return (
- + itemValue === selectedYoe, + )[0].label + } + size="inherit"> {OfferTableYoeOptions.map(({ label: itemLabel, value }) => ( { - setSelectedYoe(value); + if (value === '') { + router.replace( + { + pathname: router.pathname, + query: undefined, + }, + undefined, + // Do not refresh the page + { shallow: true }, + ); + } else { + const params = new URLSearchParams({ + ['yoeCategory']: value, + }); + router.replace( + { + pathname: location.pathname, + search: params.toString(), + }, + undefined, + { shallow: true }, + ); + } gaEvent({ action: `offers.table_filter_yoe_category_${value}`, category: 'engagement', @@ -98,7 +143,7 @@ export default function OffersTable({ ))}
-
+
Display offers in @@ -134,7 +179,7 @@ export default function OffersTable({ } function renderHeader() { - const columns = [ + let columns = [ 'Company', 'Title', 'YOE', @@ -142,6 +187,18 @@ export default function OffersTable({ 'Date Offered', 'Actions', ]; + if (jobType === JobType.FULLTIME) { + columns = [ + 'Company', + 'Title', + 'YOE', + 'Annual TC', + 'Annual Base / Bonus / Stocks', + 'Date Offered', + 'Actions', + ]; + } + return ( @@ -149,7 +206,7 @@ export default function OffersTable({ {renderFilters()} - {offersQuery.isLoading ? ( -
+ {isLoading ? ( +
) : ( -
- +
+
{renderHeader()} {offers.map((offer) => ( - + ))}
+ {!offers || + (offers.length === 0 && ( +
+
No data yet🥺
+
+ Please try another set of filters. +
+
+ ))}
)} = { + entry: 1, + intern: 0, + mid: 2, + senior: 3, +}; + export const OfferTableYoeOptions = [ + { label: 'All Full Time YOE', value: '' }, { label: 'Fresh Grad (0-2 YOE)', value: YOE_CATEGORY.ENTRY, diff --git a/apps/portal/src/pages/offers/index.tsx b/apps/portal/src/pages/offers/index.tsx index f1581e01..9eaeb955 100644 --- a/apps/portal/src/pages/offers/index.tsx +++ b/apps/portal/src/pages/offers/index.tsx @@ -5,18 +5,16 @@ import { Banner } from '@tih/ui'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; import OffersTable from '~/components/offers/table/OffersTable'; -import CitiesTypeahead from '~/components/shared/CitiesTypeahead'; import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead'; import Container from '~/components/shared/Container'; +import CountriesTypeahead from '~/components/shared/CountriesTypeahead'; import type { JobTitleType } from '~/components/shared/JobTitles'; import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead'; export default function OffersHomePage() { - const [jobTitleFilter, setJobTitleFilter] = useState( - 'software-engineer', - ); + const [jobTitleFilter, setJobTitleFilter] = useState(''); const [companyFilter, setCompanyFilter] = useState(''); - const [cityFilter, setCityFilter] = useState(''); + const [countryFilter, setCountryFilter] = useState(''); const { event: gaEvent } = useGoogleAnalytics(); return ( @@ -28,21 +26,23 @@ export default function OffersHomePage() { . ⭐ -
+
- { if (option) { - setCityFilter(option.value); + setCountryFilter(option.value); gaEvent({ - action: `offers.table_filter_city_${option.value}`, + action: `offers.table_filter_country_${option.value}`, category: 'engagement', - label: 'Filter by city', + label: 'Filter by country', }); + } else { + setCountryFilter(''); } }} /> @@ -64,7 +64,7 @@ export default function OffersHomePage() {
{ if (option) { @@ -102,8 +102,8 @@ export default function OffersHomePage() {
diff --git a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx index 4ea13123..092cf8b9 100644 --- a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx +++ b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx @@ -195,7 +195,7 @@ export default function OfferProfile() { )} {getProfileQuery.isLoading && (
-
+
Loading...
diff --git a/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx b/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx index d1c9dd09..35c72796 100644 --- a/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx +++ b/apps/portal/src/pages/offers/submit/result/[offerProfileId].tsx @@ -71,7 +71,7 @@ export default function OffersSubmissionResult() { <> {getAnalysis.isLoading && (
-
+
Loading...