[offers][fix] improve table UX

pull/535/head
Yangshun Tay 2 years ago
parent b0c7006a2d
commit a225b9ba93

@ -61,7 +61,7 @@ function ProfileJewel() {
<Menu.Button className="focus:ring-primary-500 flex rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-offset-2"> <Menu.Button className="focus:ring-primary-500 flex rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-offset-2">
<span className="sr-only">Open user menu</span> <span className="sr-only">Open user menu</span>
{session?.user?.image == null ? ( {session?.user?.image == null ? (
<span>Render some icon</span> <span>TODO: Render some icon</span>
) : ( ) : (
<img <img
alt={session?.user?.email ?? session?.user?.name ?? ''} alt={session?.user?.email ?? session?.user?.name ?? ''}

@ -35,7 +35,7 @@ export default function OfferTableRow({
<tr key={id} className="divide-x divide-slate-200 border-b bg-white"> <tr key={id} className="divide-x divide-slate-200 border-b bg-white">
<td className="space-y-0.5 py-4 px-4" scope="row"> <td className="space-y-0.5 py-4 px-4" scope="row">
<div className="font-medium">{company.name}</div> <div className="font-medium">{company.name}</div>
<div className="text-xs text-slate-400"> <div className="text-xs text-slate-500">
{location.cityName} ({location.countryCode}) {location.cityName} ({location.countryCode})
</div> </div>
</td> </td>

@ -1,6 +1,6 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import { JobType } from '@prisma/client'; import { JobType } from '@prisma/client';
import { DropdownMenu, Spinner, useToast } from '@tih/ui'; import { DropdownMenu, Spinner, useToast } from '@tih/ui';
@ -23,13 +23,15 @@ import OffersRow from './OffersRow';
import type { DashboardOffer, GetOffersResponse, Paging } from '~/types/offers'; import type { DashboardOffer, GetOffersResponse, Paging } from '~/types/offers';
const NUMBER_OF_OFFERS_IN_PAGE = 10; const NUMBER_OF_OFFERS_PER_PAGE = 20;
export type OffersTableProps = Readonly<{ export type OffersTableProps = Readonly<{
companyFilter: string; companyFilter: string;
companyName?: string; companyName?: string;
countryFilter: string; countryFilter: string;
jobTitleFilter: string; jobTitleFilter: string;
}>; }>;
export default function OffersTable({ export default function OffersTable({
countryFilter, countryFilter,
companyName, companyName,
@ -101,15 +103,16 @@ export default function OffersTable({
pathname, pathname,
]); ]);
const topRef = useRef<HTMLDivElement>(null);
const { showToast } = useToast(); const { showToast } = useToast();
trpc.useQuery( const { isLoading: isResultsLoading } = trpc.useQuery(
[ [
'offers.list', 'offers.list',
{ {
companyId: companyFilter, companyId: companyFilter,
countryId: countryFilter, countryId: countryFilter,
currency, currency,
limit: NUMBER_OF_OFFERS_IN_PAGE, limit: NUMBER_OF_OFFERS_PER_PAGE,
offset: pagination.currentPage, offset: pagination.currentPage,
sortBy: selectedSortBy ?? '-monthYearReceived', sortBy: selectedSortBy ?? '-monthYearReceived',
title: jobTitleFilter, title: jobTitleFilter,
@ -257,17 +260,27 @@ export default function OffersTable({
}; };
return ( return (
<div className="relative w-full border border-slate-200"> <div className="relative w-full divide-y divide-slate-200 border border-slate-200 bg-white">
{renderFilters()} <div ref={topRef}>{renderFilters()}</div>
<OffersTablePagination
endNumber={
pagination.currentPage * NUMBER_OF_OFFERS_PER_PAGE + offers.length
}
handlePageChange={handlePageChange}
isInitialFetch={isLoading}
isLoading={isResultsLoading}
pagination={pagination}
startNumber={pagination.currentPage * NUMBER_OF_OFFERS_PER_PAGE + 1}
/>
{isLoading ? ( {isLoading ? (
<div className="col-span-10 py-32"> <div className="col-span-10 py-32">
<Spinner display="block" size="lg" /> <Spinner display="block" size="lg" />
</div> </div>
) : ( ) : (
<div className="overflow-x-auto 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 text-xs text-slate-700 sm:text-sm md:text-base"> <table className="w-full divide-y divide-slate-200 text-left text-xs text-slate-700 sm:text-sm">
{renderHeader()} {renderHeader()}
<tbody> <tbody className="divide-y divide-slate-200">
{offers.map((offer) => ( {offers.map((offer) => (
<OffersRow key={offer.id} jobType={jobType} row={offer} /> <OffersRow key={offer.id} jobType={jobType} row={offer} />
))} ))}
@ -283,11 +296,18 @@ export default function OffersTable({
)} )}
<OffersTablePagination <OffersTablePagination
endNumber={ endNumber={
pagination.currentPage * NUMBER_OF_OFFERS_IN_PAGE + offers.length pagination.currentPage * NUMBER_OF_OFFERS_PER_PAGE + offers.length
} }
handlePageChange={handlePageChange} handlePageChange={(number) => {
topRef?.current?.scrollIntoView({
block: 'start',
});
handlePageChange(number);
}}
isInitialFetch={isLoading}
isLoading={isResultsLoading}
pagination={pagination} pagination={pagination}
startNumber={pagination.currentPage * NUMBER_OF_OFFERS_IN_PAGE + 1} startNumber={pagination.currentPage * NUMBER_OF_OFFERS_PER_PAGE + 1}
/> />
</div> </div>
); );

@ -1,29 +1,38 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Pagination } from '@tih/ui'; import { Pagination, Spinner } from '@tih/ui';
import type { Paging } from '~/types/offers'; import type { Paging } from '~/types/offers';
type OffersTablePaginationProps = Readonly<{ type OffersTablePaginationProps = Readonly<{
endNumber: number; endNumber: number;
handlePageChange: (page: number) => void; handlePageChange: (page: number) => void;
isInitialFetch?: boolean;
isLoading?: boolean;
pagination: Paging; pagination: Paging;
startNumber: number; startNumber: number;
}>; }>;
export default function OffersTablePagination({ export default function OffersTablePagination({
isInitialFetch,
isLoading,
endNumber, endNumber,
pagination, pagination,
startNumber, startNumber,
handlePageChange, handlePageChange,
}: OffersTablePaginationProps) { }: OffersTablePaginationProps) {
const [screenWidth, setScreenWidth] = useState(0); const [screenWidth, setScreenWidth] = useState(0);
useEffect(() => { useEffect(() => {
setScreenWidth(window.innerWidth); setScreenWidth(window.innerWidth);
}, []); }, []);
return ( return (
<nav aria-label="Table navigation" className="p-4"> <nav aria-label="Offers Pagination" className="py-3 px-4">
<div className="flex grid grid-cols-1 items-center md:grid-cols-2"> <div className="grid grid-cols-1 items-center gap-2 md:grid-cols-2">
<div className="mb-2 text-sm font-normal text-slate-500 md:mb-0"> <div>
{!isInitialFetch && (
<div className="flex items-center space-x-2">
<div className="text-sm text-slate-500">
Showing Showing
<span className="font-semibold text-slate-900"> <span className="font-semibold text-slate-900">
{` ${endNumber > 0 ? startNumber : 0} - ${endNumber} `} {` ${endNumber > 0 ? startNumber : 0} - ${endNumber} `}
@ -31,7 +40,12 @@ export default function OffersTablePagination({
{`of `} {`of `}
<span className="font-semibold text-slate-900"> <span className="font-semibold text-slate-900">
{pagination.totalItems} {pagination.totalItems}
</span> </span>{' '}
results
</div>
{isLoading && <Spinner size="xs" />}
</div>
)}
</div> </div>
<div className="flex md:justify-end"> <div className="flex md:justify-end">
<Pagination <Pagination

Loading…
Cancel
Save