mirror of https://git.sr.ht/~ashkeel/strimertul
156 lines
4.6 KiB
TypeScript
156 lines
4.6 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { RouteComponentProps } from '@reach/router';
|
|
import { useModule } from '../../../lib/react-utils';
|
|
import { modules } from '../../../store/api/reducer';
|
|
import PageList from '../../components/PageList';
|
|
|
|
interface SortingOrder {
|
|
key: 'user' | 'when';
|
|
order: 'asc' | 'desc';
|
|
}
|
|
|
|
export default function LoyaltyRedeemQueuePage(
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
props: RouteComponentProps<unknown>,
|
|
): React.ReactElement {
|
|
const [redemptions, setRedeemQueue] = useModule(modules.loyaltyRedeemQueue);
|
|
|
|
const [sorting, setSorting] = useState<SortingOrder>({
|
|
key: 'when',
|
|
order: 'desc',
|
|
});
|
|
|
|
const [entriesPerPage, setEntriesPerPage] = useState(15);
|
|
const [page, setPage] = useState(0);
|
|
const [usernameFilter, setUsernameFilter] = useState('');
|
|
|
|
const changeSort = (key: 'user' | 'when') => {
|
|
if (sorting.key === key) {
|
|
// Same key, swap sorting order
|
|
setSorting({
|
|
...sorting,
|
|
order: sorting.order === 'asc' ? 'desc' : 'asc',
|
|
});
|
|
} else {
|
|
// Different key, change to sort that key
|
|
setSorting({ ...sorting, key, order: 'asc' });
|
|
}
|
|
};
|
|
|
|
const filtered =
|
|
redemptions?.filter(({ user }) => user.includes(usernameFilter)) ?? [];
|
|
|
|
const sortedEntries = filtered;
|
|
switch (sorting.key) {
|
|
case 'user':
|
|
if (sorting.order === 'asc') {
|
|
sortedEntries.sort((a, b) => (a.user > b.user ? 1 : -1));
|
|
} else {
|
|
sortedEntries.sort((a, b) => (a.user < b.user ? 1 : -1));
|
|
}
|
|
break;
|
|
case 'when':
|
|
if (sorting.order === 'asc') {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
sortedEntries.sort(
|
|
(a, b) =>
|
|
Date.parse(a.when.toString()) - Date.parse(b.when.toString()),
|
|
);
|
|
} else {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
sortedEntries.sort(
|
|
(a, b) =>
|
|
Date.parse(b.when.toString()) - Date.parse(a.when.toString()),
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
// unreacheable
|
|
}
|
|
|
|
const paged = sortedEntries.slice(
|
|
page * entriesPerPage,
|
|
(page + 1) * entriesPerPage,
|
|
);
|
|
const totalPages = Math.floor(sortedEntries.length / entriesPerPage);
|
|
|
|
return (
|
|
<>
|
|
<h1 className="title is-4">Redemption queue</h1>
|
|
{redemptions ? (
|
|
<>
|
|
<div className="field">
|
|
<input
|
|
className="input is-small"
|
|
type="text"
|
|
placeholder="Search by username"
|
|
value={usernameFilter}
|
|
onChange={(ev) =>
|
|
setUsernameFilter(ev.target.value.toLowerCase())
|
|
}
|
|
/>
|
|
</div>
|
|
<PageList
|
|
current={page + 1}
|
|
min={1}
|
|
max={totalPages + 1}
|
|
onPageChange={(p) => setPage(p - 1)}
|
|
/>
|
|
<table className="table is-striped is-fullwidth">
|
|
<thead>
|
|
<tr>
|
|
<th style={{ width: '20%' }}>
|
|
<span className="sortable" onClick={() => changeSort('when')}>
|
|
Date
|
|
{sorting.key === 'when' ? (
|
|
<span className="sort-icon">
|
|
{sorting.order === 'asc' ? '▴' : '▾'}
|
|
</span>
|
|
) : null}
|
|
</span>
|
|
</th>
|
|
<th>
|
|
<span className="sortable" onClick={() => changeSort('user')}>
|
|
Username
|
|
{sorting.key === 'user' ? (
|
|
<span className="sort-icon">
|
|
{sorting.order === 'asc' ? '▴' : '▾'}
|
|
</span>
|
|
) : null}
|
|
</span>
|
|
</th>
|
|
<th>Reward name</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
{paged.map((redemption) => (
|
|
<tr
|
|
key={`${redemption.when}-${redemption.user}-${redemption.reward.id}`}
|
|
>
|
|
<td>{new Date(redemption.when).toLocaleString()}</td>
|
|
<td>{redemption.user}</td>
|
|
<td>{redemption.reward.name}</td>
|
|
<td></td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
<PageList
|
|
current={page + 1}
|
|
min={1}
|
|
max={totalPages + 1}
|
|
onPageChange={(p) => setPage(p - 1)}
|
|
/>
|
|
</>
|
|
) : (
|
|
<p>
|
|
Redemption queue is not available (loyalty disabled or no one has
|
|
redeemed anything yet)
|
|
</p>
|
|
)}
|
|
</>
|
|
);
|
|
}
|