|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
import clsx from 'clsx';
|
|
|
|
|
import { useRouter } from 'next/router';
|
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
|
import { JobType } from '@prisma/client';
|
|
|
|
|
import { DropdownMenu, Spinner } from '@tih/ui';
|
|
|
|
|
|
|
|
|
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
|
|
|
@ -23,17 +24,18 @@ import type { DashboardOffer, GetOffersResponse, Paging } from '~/types/offers';
|
|
|
|
|
|
|
|
|
|
const NUMBER_OF_OFFERS_IN_PAGE = 10;
|
|
|
|
|
export type OffersTableProps = Readonly<{
|
|
|
|
|
cityFilter: string;
|
|
|
|
|
companyFilter: string;
|
|
|
|
|
countryFilter: string;
|
|
|
|
|
jobTitleFilter: string;
|
|
|
|
|
}>;
|
|
|
|
|
export default function OffersTable({
|
|
|
|
|
cityFilter,
|
|
|
|
|
countryFilter,
|
|
|
|
|
companyFilter,
|
|
|
|
|
jobTitleFilter,
|
|
|
|
|
}: OffersTableProps) {
|
|
|
|
|
const [currency, setCurrency] = useState(Currency.SGD.toString()); // TODO: Detect location
|
|
|
|
|
const [selectedYoe, setSelectedYoe] = useState('');
|
|
|
|
|
const [jobType, setJobType] = useState<JobType>(JobType.FULLTIME);
|
|
|
|
|
const [pagination, setPagination] = useState<Paging>({
|
|
|
|
|
currentPage: 0,
|
|
|
|
|
numOfItems: 0,
|
|
|
|
@ -47,6 +49,7 @@ export default function OffersTable({
|
|
|
|
|
const { event: gaEvent } = useGoogleAnalytics();
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const { yoeCategory = '' } = router.query;
|
|
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setPagination({
|
|
|
|
@ -55,19 +58,20 @@ export default function OffersTable({
|
|
|
|
|
numOfPages: 0,
|
|
|
|
|
totalItems: 0,
|
|
|
|
|
});
|
|
|
|
|
}, [selectedYoe, currency]);
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
}, [selectedYoe, currency, countryFilter, companyFilter, jobTitleFilter]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setSelectedYoe(yoeCategory as YOE_CATEGORY);
|
|
|
|
|
event?.preventDefault();
|
|
|
|
|
}, [yoeCategory]);
|
|
|
|
|
|
|
|
|
|
const offersQuery = trpc.useQuery(
|
|
|
|
|
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,
|
|
|
|
@ -83,6 +87,8 @@ export default function OffersTable({
|
|
|
|
|
onSuccess: (response: GetOffersResponse) => {
|
|
|
|
|
setOffers(response.data);
|
|
|
|
|
setPagination(response.paging);
|
|
|
|
|
setJobType(response.jobType);
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
@ -118,10 +124,14 @@ export default function OffersTable({
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
['yoeCategory']: value,
|
|
|
|
|
});
|
|
|
|
|
router.replace({
|
|
|
|
|
router.replace(
|
|
|
|
|
{
|
|
|
|
|
pathname: location.pathname,
|
|
|
|
|
search: params.toString(),
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
undefined,
|
|
|
|
|
{ shallow: true },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
gaEvent({
|
|
|
|
|
action: `offers.table_filter_yoe_category_${value}`,
|
|
|
|
@ -169,7 +179,7 @@ export default function OffersTable({
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderHeader() {
|
|
|
|
|
const columns = [
|
|
|
|
|
let columns = [
|
|
|
|
|
'Company',
|
|
|
|
|
'Title',
|
|
|
|
|
'YOE',
|
|
|
|
@ -177,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 (
|
|
|
|
|
<thead className="text-slate-700">
|
|
|
|
|
<tr className="divide-x divide-slate-200">
|
|
|
|
@ -184,7 +206,7 @@ export default function OffersTable({
|
|
|
|
|
<th
|
|
|
|
|
key={header}
|
|
|
|
|
className={clsx(
|
|
|
|
|
'bg-slate-100 py-3 px-6',
|
|
|
|
|
'bg-slate-100 py-3 px-4',
|
|
|
|
|
// Make last column sticky.
|
|
|
|
|
index === columns.length - 1 &&
|
|
|
|
|
'sticky right-0 drop-shadow md:drop-shadow-none',
|
|
|
|
@ -207,20 +229,29 @@ export default function OffersTable({
|
|
|
|
|
return (
|
|
|
|
|
<div className="relative w-full border border-slate-200">
|
|
|
|
|
{renderFilters()}
|
|
|
|
|
{offersQuery.isLoading ? (
|
|
|
|
|
<div className="col-span-10 pt-4">
|
|
|
|
|
{isLoading ? (
|
|
|
|
|
<div className="col-span-10 py-32">
|
|
|
|
|
<Spinner display="block" size="lg" />
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="overflow-x-auto">
|
|
|
|
|
<table className="w-full divide-y divide-slate-200 border-y border-slate-200 text-left text-slate-600">
|
|
|
|
|
<div className="overflow-x-auto text-slate-600">
|
|
|
|
|
<table className="w-full divide-y divide-slate-200 border-y border-slate-200 text-left">
|
|
|
|
|
{renderHeader()}
|
|
|
|
|
<tbody>
|
|
|
|
|
{offers.map((offer) => (
|
|
|
|
|
<OffersRow key={offer.id} row={offer} />
|
|
|
|
|
<OffersRow key={offer.id} jobType={jobType} row={offer} />
|
|
|
|
|
))}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
{!offers ||
|
|
|
|
|
(offers.length === 0 && (
|
|
|
|
|
<div className="py-16 text-lg">
|
|
|
|
|
<div className="flex justify-center">No data yet🥺</div>
|
|
|
|
|
<div className="flex justify-center">
|
|
|
|
|
Please try another set of filters.
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
<OffersTablePagination
|
|
|
|
|