- Seed now resets the database and seeds the Aldenfell campaign from the campaign book: admin DM user, default weapons, locations, bestiary, pre-built encounters, and DM/shared notes - Default weapon/spell/monster library, character profile fields, locations and dice mode (new migration) - Party overview page, pagination, info tips, build preview calc - Remove stale demo-login hint; notes preserve line breaks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
51 lines
1.5 KiB
TypeScript
51 lines
1.5 KiB
TypeScript
import { useMemo, useState } from 'react';
|
|
|
|
// Small generic pager shared by every list-turned-table in the app, so page
|
|
// state isn't hand-rolled per screen. Resets to page 1 whenever the
|
|
// underlying item count shrinks below the current page (e.g. after a delete
|
|
// or a filter change).
|
|
export function usePagination<T>(items: T[] | undefined, pageSize = 10) {
|
|
const [page, setPage] = useState(1);
|
|
const all = items || [];
|
|
const pageCount = Math.max(1, Math.ceil(all.length / pageSize));
|
|
const safePage = Math.min(page, pageCount);
|
|
const pageItems = useMemo(() => all.slice((safePage - 1) * pageSize, safePage * pageSize), [all, safePage, pageSize]);
|
|
|
|
return {
|
|
page: safePage,
|
|
pageCount,
|
|
pageItems,
|
|
total: all.length,
|
|
setPage,
|
|
nextPage: () => setPage((p) => Math.min(pageCount, p + 1)),
|
|
prevPage: () => setPage((p) => Math.max(1, p - 1)),
|
|
};
|
|
}
|
|
|
|
export function Pagination({
|
|
page,
|
|
pageCount,
|
|
onPrev,
|
|
onNext,
|
|
}: {
|
|
page: number;
|
|
pageCount: number;
|
|
onPrev: () => void;
|
|
onNext: () => void;
|
|
}) {
|
|
if (pageCount <= 1) return null;
|
|
return (
|
|
<div className="pagination">
|
|
<button type="button" className="btn btn-ghost" onClick={onPrev} disabled={page <= 1}>
|
|
← Prev
|
|
</button>
|
|
<span className="pagination-status">
|
|
Page {page} of {pageCount}
|
|
</span>
|
|
<button type="button" className="btn btn-ghost" onClick={onNext} disabled={page >= pageCount}>
|
|
Next →
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|