feat(LOY-501): add sorting to Reward Night Table * feat(LOY-501): add sorting using Tanstack Table Approved-by: Chuma Mcphoy (We Ahead)
167 lines
4.9 KiB
TypeScript
167 lines
4.9 KiB
TypeScript
"use client"
|
|
|
|
import {
|
|
createColumnHelper,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
getSortedRowModel,
|
|
type SortingState,
|
|
useReactTable,
|
|
} from "@tanstack/react-table"
|
|
import { cx } from "class-variance-authority"
|
|
import { useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
import Table from "@scandic-hotels/design-system/Table"
|
|
import { TextLink } from "@scandic-hotels/design-system/TextLink"
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
|
|
import { OfferPrice } from "./OfferPrice"
|
|
import {
|
|
formatPoints,
|
|
hasActiveCampaign,
|
|
type HotelData,
|
|
nameToSort,
|
|
} from "./util"
|
|
|
|
import styles from "./rewardNights.module.css"
|
|
|
|
interface RewardNightsTableProps {
|
|
hotelData: HotelData[]
|
|
}
|
|
export function RewardNightsTable({ hotelData }: RewardNightsTableProps) {
|
|
const intl = useIntl()
|
|
|
|
const [sorting, setSorting] = useState<SortingState>([
|
|
{ id: "destination", desc: false },
|
|
])
|
|
|
|
const columnHelper = createColumnHelper<HotelData>()
|
|
const columns = [
|
|
columnHelper.accessor("name", {
|
|
header: intl.formatMessage({
|
|
id: "rewardNights.table.hotel",
|
|
defaultMessage: "Hotel",
|
|
}),
|
|
sortingFn: (a, b) =>
|
|
nameToSort(a.original.name).localeCompare(nameToSort(b.original.name)),
|
|
cell: ({ row }) => (
|
|
<TextLink href={row.original.url}>{row.original.name}</TextLink>
|
|
),
|
|
}),
|
|
|
|
columnHelper.accessor((row) => `${row.city}, ${row.country}`, {
|
|
id: "destination",
|
|
header: intl.formatMessage({
|
|
id: "rewardNights.table.destination",
|
|
defaultMessage: "Destination",
|
|
}),
|
|
sortingFn: (a, b) => a.original.city.localeCompare(b.original.city),
|
|
cell: ({ row }) => {
|
|
const hotel = row.original
|
|
const hasCampaign = hasActiveCampaign(hotel.rewardNight.campaign)
|
|
|
|
return (
|
|
<>
|
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
|
{hotel.city}, {hotel.country}
|
|
{hasCampaign ? (
|
|
<OfferPrice {...hotel.rewardNight.campaign} />
|
|
) : null}
|
|
</>
|
|
)
|
|
},
|
|
}),
|
|
|
|
columnHelper.accessor((row) => row.rewardNight.points, {
|
|
id: "points",
|
|
header: intl.formatMessage({
|
|
id: "common.points",
|
|
defaultMessage: "Points",
|
|
}),
|
|
sortingFn: (a, b) =>
|
|
a.original.rewardNight.points - b.original.rewardNight.points,
|
|
cell: ({ row }) => {
|
|
const hotel = row.original
|
|
const hasCampaign = hasActiveCampaign(hotel.rewardNight.campaign)
|
|
|
|
return (
|
|
<div className={cx({ [styles.grid]: hasCampaign })}>
|
|
{formatPoints(hotel.rewardNight.points)}
|
|
|
|
{hasCampaign ? (
|
|
<Typography
|
|
variant="Body/Paragraph/mdBold"
|
|
className={styles.highlightedText}
|
|
>
|
|
<span>{formatPoints(hotel.rewardNight.campaign.points)}</span>
|
|
</Typography>
|
|
) : null}
|
|
</div>
|
|
)
|
|
},
|
|
}),
|
|
]
|
|
|
|
const table = useReactTable({
|
|
data: hotelData,
|
|
columns,
|
|
state: { sorting },
|
|
onSortingChange: setSorting,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
enableSortingRemoval: false,
|
|
})
|
|
|
|
return (
|
|
<Table intent="striped" variant="content" style={{ textWrap: "balance" }}>
|
|
<Table.THead>
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
<Table.TR key={headerGroup.id}>
|
|
{headerGroup.headers.map((header) => (
|
|
<Table.TH
|
|
key={header.id}
|
|
onClick={header.column.getToggleSortingHandler()}
|
|
style={{
|
|
...(header.column.getIsSorted() && {
|
|
color: "var(--Text-Interactive-Secondary)",
|
|
}),
|
|
cursor: "pointer",
|
|
whiteSpace: "nowrap",
|
|
minWidth: "max-content",
|
|
}}
|
|
>
|
|
{flexRender(
|
|
header.column.columnDef.header,
|
|
header.getContext()
|
|
)}
|
|
<MaterialIcon
|
|
icon="keyboard_arrow_up"
|
|
color="CurrentColor"
|
|
className={cx(styles.icon, {
|
|
[styles.isTransformed]:
|
|
header.column.getIsSorted() === "desc",
|
|
})}
|
|
/>
|
|
</Table.TH>
|
|
))}
|
|
</Table.TR>
|
|
))}
|
|
</Table.THead>
|
|
|
|
<Table.TBody>
|
|
{table.getRowModel().rows.map((row) => (
|
|
<Table.TR key={row.id}>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<Table.TD key={cell.id} style={{ alignContent: "flex-start" }}>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</Table.TD>
|
|
))}
|
|
</Table.TR>
|
|
))}
|
|
</Table.TBody>
|
|
</Table>
|
|
)
|
|
}
|