feat(tender-list): add GSAP for animations and enhance table functionality
- Integrated GSAP for improved animations in the tender list components, enhancing user experience during data loading and transitions. - Refactored TendersTable and TenderListFilters to utilize new animation features, providing smoother interactions. - Updated TableBody and TableSkeleton components to support ref forwarding, improving flexibility in rendering and animations. - Enhanced useTenderListPresenter to manage loading states and pagination more effectively, ensuring better data handling and user feedback.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ExclamationIcon, EyeIcon } from "@/assets/icons";
|
||||
import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
|
||||
import Status from "@/components/ui/Status";
|
||||
import {
|
||||
@@ -10,32 +11,20 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import useTenderListPresenter from "./useTenderListPresenter";
|
||||
|
||||
import { ExclamationIcon, EyeIcon } from "@/assets/icons";
|
||||
|
||||
import IsVisible from "@/components/ui/IsVisible";
|
||||
import ListHeader from "@/components/ui/ListHeader";
|
||||
import ListWrapper from "@/components/ui/ListWrapper";
|
||||
import Modal from "@/components/ui/modal";
|
||||
import Pagination from "@/components/ui/pagination";
|
||||
import TableSkeleton from "@/components/ui/TableSkeleton";
|
||||
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getPaginatedRowNumber, unixToDate } from "@/utils/shared";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import TenderListFilters from "./TenderListFilters";
|
||||
import useTenderListPresenter from "./useTenderListPresenter";
|
||||
|
||||
const TendersTable = () => {
|
||||
const {
|
||||
allTenders,
|
||||
isPending,
|
||||
router,
|
||||
pathName,
|
||||
setParams,
|
||||
params,
|
||||
columns,
|
||||
isFilterModalOpen,
|
||||
setIsFilterModalOpen,
|
||||
filterRegister,
|
||||
handleFilterSubmit,
|
||||
search,
|
||||
@@ -44,149 +33,130 @@ const TendersTable = () => {
|
||||
control,
|
||||
isMutating,
|
||||
shouldShowPagination,
|
||||
clearAllFilters,
|
||||
tableSectionRef,
|
||||
skeletonTbodyRef,
|
||||
dataTbodyRef,
|
||||
tendersList,
|
||||
isPending,
|
||||
navigateToTenderDetails,
|
||||
navigateToTenderFeedback,
|
||||
handlePaginationChange,
|
||||
getRowNumber,
|
||||
formatDateTimeCell,
|
||||
pagination,
|
||||
} = useTenderListPresenter();
|
||||
|
||||
return (
|
||||
<ListWrapper>
|
||||
<ListHeader
|
||||
onFilter={() => setIsFilterModalOpen(true)}
|
||||
hasFilter={false}
|
||||
createButtonText="Create Tender"
|
||||
hasCreate={false}
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-none uppercase [&>th]:text-start">
|
||||
{columns.map((column) => (
|
||||
<TableHead key={column} colSpan={100}>
|
||||
{column}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TenderListFilters
|
||||
setValue={setFilterValue}
|
||||
filterRegister={filterRegister}
|
||||
control={control}
|
||||
handleFilterSubmit={handleFilterSubmit}
|
||||
isMutating={isMutating as number}
|
||||
search={search}
|
||||
watch={watch}
|
||||
onClearAll={clearAllFilters}
|
||||
/>
|
||||
<div ref={tableSectionRef}>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-none uppercase [&>th]:text-start">
|
||||
{columns.map((column) => (
|
||||
<TableHead key={column} colSpan={100}>
|
||||
{column}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
{isPending && <TableSkeleton column={columns.length} />}
|
||||
<TableBody>
|
||||
<EmptyListWrapper
|
||||
list={allTenders?.data.tenders ?? []}
|
||||
columns={columns}
|
||||
emptyMessage="No tenders were found"
|
||||
isLoading={isPending}
|
||||
>
|
||||
{(allTenders?.data.tenders ?? []).map((item, index) => (
|
||||
<TableRow
|
||||
key={item.id}
|
||||
className="odd:bg-gray-2 dark:odd:bg-gray-7"
|
||||
{isPending ? (
|
||||
<TableSkeleton ref={skeletonTbodyRef} column={columns.length} />
|
||||
) : (
|
||||
<TableBody ref={dataTbodyRef}>
|
||||
<EmptyListWrapper
|
||||
list={tendersList}
|
||||
columns={columns}
|
||||
emptyMessage="No tenders were found"
|
||||
isLoading={false}
|
||||
>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{getPaginatedRowNumber({
|
||||
index,
|
||||
page: allTenders?.meta?.page,
|
||||
limit: allTenders?.meta?.limit,
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{item.title}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{item.country_code}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{unixToDate({
|
||||
unix: item.publication_date,
|
||||
hasTime: true,
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{unixToDate({
|
||||
unix: item.submission_deadline,
|
||||
hasTime: true,
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{unixToDate({ unix: item.tender_deadline, hasTime: true })}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{unixToDate({
|
||||
unix: item?.created_at ?? 0,
|
||||
hasTime: true,
|
||||
})}
|
||||
</TableCell>
|
||||
{tendersList.map((item, index) => (
|
||||
<TableRow
|
||||
key={item.id}
|
||||
className="odd:bg-gray-2 dark:odd:bg-gray-7"
|
||||
>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{getRowNumber(index)}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{item.title}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{item.country_code}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{formatDateTimeCell(item.publication_date)}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{formatDateTimeCell(item.submission_deadline)}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{formatDateTimeCell(item.tender_deadline)}
|
||||
</TableCell>
|
||||
<TableCell className="text-start" colSpan={100}>
|
||||
{formatDateTimeCell(item?.created_at ?? 0)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className={cn(`text capitalize`)} colSpan={100}>
|
||||
<Status status={item.status}>{item.status}</Status>
|
||||
</TableCell>
|
||||
<TableCell className="capitalize" colSpan={100}>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
data-tooltip-id="details"
|
||||
data-tooltip-content="Details"
|
||||
data-tooltip-place="top"
|
||||
onClick={() => {
|
||||
const queryString = params?.lang
|
||||
? `?lang=${encodeURIComponent(params.lang)}`
|
||||
: "";
|
||||
router.push(`${pathName}/${item.id}${queryString}`);
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
{..._TooltipDefaultParams({
|
||||
id: "details",
|
||||
})}
|
||||
/>
|
||||
<EyeIcon />
|
||||
</button>
|
||||
<button
|
||||
data-tooltip-id="feedback"
|
||||
data-tooltip-content="Feedback"
|
||||
data-tooltip-place="top"
|
||||
className="text-gray-40 rounded-full bg-gray-dark bg-opacity-5 hover:text-green-dark dark:bg-gray dark:bg-opacity-5"
|
||||
onClick={() => {
|
||||
const queryString = params?.lang
|
||||
? `?lang=${encodeURIComponent(params.lang)}`
|
||||
: "";
|
||||
router.push(
|
||||
`${pathName}/feedback/${item.id}${queryString}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Tooltip {..._TooltipDefaultParams({ id: "feedback" })} />
|
||||
<ExclamationIcon />
|
||||
</button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</EmptyListWrapper>
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Modal
|
||||
isOpen={isFilterModalOpen}
|
||||
onClose={() => setIsFilterModalOpen(false)}
|
||||
classNames="w-full xl:w-1/2"
|
||||
showButtons={false}
|
||||
>
|
||||
<TenderListFilters
|
||||
setValue={setFilterValue}
|
||||
filterRegister={filterRegister}
|
||||
control={control}
|
||||
handleFilterSubmit={handleFilterSubmit}
|
||||
isMutating={isMutating as number}
|
||||
search={search}
|
||||
setIsFilterModalOpen={setIsFilterModalOpen}
|
||||
watch={watch}
|
||||
/>
|
||||
</Modal>
|
||||
<TableCell className={cn(`text capitalize`)} colSpan={100}>
|
||||
<Status status={item.status}>{item.status}</Status>
|
||||
</TableCell>
|
||||
<TableCell className="capitalize" colSpan={100}>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
data-tooltip-id="details"
|
||||
data-tooltip-content="Details"
|
||||
data-tooltip-place="top"
|
||||
onClick={() => navigateToTenderDetails(item.id)}
|
||||
>
|
||||
<Tooltip
|
||||
{..._TooltipDefaultParams({
|
||||
id: "details",
|
||||
})}
|
||||
/>
|
||||
<EyeIcon />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-tooltip-id="feedback"
|
||||
data-tooltip-content="Feedback"
|
||||
data-tooltip-place="top"
|
||||
className="text-gray-40 rounded-full bg-gray-dark bg-opacity-5 hover:text-green-dark dark:bg-gray dark:bg-opacity-5"
|
||||
onClick={() => navigateToTenderFeedback(item.id)}
|
||||
>
|
||||
<Tooltip {..._TooltipDefaultParams({ id: "feedback" })} />
|
||||
<ExclamationIcon />
|
||||
</button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</EmptyListWrapper>
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
<IsVisible condition={shouldShowPagination}>
|
||||
<Pagination
|
||||
currentPage={allTenders?.meta?.page ? allTenders?.meta?.page - 1 : 0}
|
||||
totalPages={allTenders?.meta?.pages ?? 1}
|
||||
onPageChange={(e) => {
|
||||
setParams((currentParams) => ({
|
||||
...currentParams,
|
||||
offset: e.selected * (allTenders?.meta?.limit ?? 10),
|
||||
limit: allTenders?.meta?.limit ?? 10,
|
||||
}));
|
||||
}}
|
||||
currentPage={pagination.currentPage}
|
||||
totalPages={pagination.totalPages}
|
||||
onPageChange={handlePaginationChange}
|
||||
/>
|
||||
</IsVisible>
|
||||
</ListWrapper>
|
||||
|
||||
Reference in New Issue
Block a user