Skip to content

@msflib/react-lottery

Standalone lottery module built on TanStack Query.

Install

pnpm add @msflib/react-lottery

Peer deps

  • react >= 18
  • @tanstack/react-query >= 5

Usage

Wrap your app with the module Provider inside your QueryClientProvider.

In Next.js App Router, Provider must be in a client component ("use client").

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { LotteryProvider, useLottery } from '@msflib/react-lottery';

const queryClient = new QueryClient();

<QueryClientProvider client={queryClient}>
  <LotteryProvider options={{ requireAuth: true, isAuthenticated: true }}>
    <LotteryList />
  </LotteryProvider>
</QueryClientProvider>;

function LotteryList() {
  const { lotteries, myEntries, loading, createEntry } = useLottery();

  if (loading.lotteries) return <p>Loading…</p>;

  return (
    <ul>
      {lotteries.map((lottery) => (
        <li key={lottery.id}>
          {lottery.name} — prize: {lottery.prize}
          <button
            disabled={loading.createEntry}
            onClick={() => createEntry(lottery.id, { number: 7 })}
          >
            Enter with number 7
          </button>
        </li>
      ))}
    </ul>
  );
}

The provider auto-fetches both lotteries and the current user's myEntries (across all lotteries) on mount, each paginated via options.query (offset/limit, default { offset: 0, limit: 100 }).

Admin lottery access

Alongside the student-facing /lottery routes, the backend exposes /admin/lottery routes for instructor/admin surfaces — create/update/delete a lottery, and inspect entries/winners across the whole workspace or for one lottery. These are off by default; opt in with enableAdminAccess, the same way @msflib/react-tasks gates its promotion/demotion flows:

<LotteryProvider options={{ enableAdminAccess: true }}>
  {children}
</LotteryProvider>

When enabled, the provider automatically loads GET /admin/lottery/entries and GET /admin/lottery/winners (both paginated via options.query) and exposes:

const {
  adminEntries,
  adminWinners,
  createLottery,
  updateLottery,
  deleteLottery,
  listAllEntries,
  listAllWinners,
  listLotteryEntries,
  listLotteryWinners,
} = useLottery();
  • adminEntries/adminWinners — the current workspace-wide admin listings ([] until loaded, and always [] when enableAdminAccess is off).
  • createLottery(data, options?) / updateLottery(id, data, options?) / deleteLottery(id, options?) — manage lotteries. These are always exposed on context regardless of enableAdminAccess (matching promoteTask/demoteTask in react-tasks) — only the automatic fetch is gated.
  • listAllEntries(params?, options?) / listAllWinners(params?, options?) — on-demand fetch with different pagination.
  • listLotteryEntries(lotteryId, params?, options?) / listLotteryWinners(lotteryId, params?, options?) — entries/winners scoped to one lottery.

Entries

  • myEntries — the current user's entries across all lotteries (GET /lottery/entries).
  • getUserEntry(lotteryId, options?) — fetch the current user's entry for one specific lottery (GET /lottery/{id}/entry).
  • createEntry(lotteryId, data, options?) — submit a number for a lottery (POST /lottery/{id}/entries).

Fetching a single lottery

Use getLottery from context for an on-demand fetch, or the standalone useGetLottery hook for a declarative, cache-backed read:

import { useGetLottery } from '@msflib/react-lottery';

function LotteryDetail({ lotteryId }: { lotteryId: number }) {
  const { data: lottery, isPending } = useGetLottery(lotteryId);

  if (isPending) return <p>Loading…</p>;
  return <h2>{lottery?.name}</h2>;
}

useGetLottery is disabled (no request fired) while lotteryId is undefined.

Background task

runBackgroundTask() triggers the lottery draw/processing job (POST /background-tasks/lottery) — a global, non-workspace-scoped endpoint (it has no workspace_slug in its path), mirroring runWorker in @msflib/react-documents.

Options

Key Type Default Description
isWorkspaceScoped boolean true If true, lotteries are isolated per active workspace.
requireAuth boolean false If true, lotteries are only fetched once isAuthenticated is true.
isAuthenticated boolean false The authentication status of the user (e.g. from useAuth().status).
enableAdminAccess boolean false If true, also loads the workspace-wide admin entries/winners on mount.
query object — { offset?, limit? } used for every list fetched on mount (lotteries, myEntries, adminEntries, adminWinners).

Endpoints

Configure paths through configureApplication({ endpoints: { lottery } }).

Key Default path Methods / routes
lottery /lottery list (GET, supports offset/limit); detail (GET on /{id}); /entries (GET, all my entries); /{id}/entry (GET, my entry for one lottery); /{id}/entries (POST, submit a number)
adminLottery /admin/lottery create (POST); update/delete on /{id} (PUT/DELETE); /entries and /winners (GET, workspace-wide); /{id}/entries and /{id}/winners (GET, per-lottery)
backgroundTask /background-tasks/lottery POST — not workspace-scoped

Notes: - All lottery/adminLottery endpoints are workspace-scoped (they send the workspace header/prefix). - runBackgroundTask is not workspace-scoped, matching the backend API contract (the route has no workspace_slug segment). - deleteLottery resolves with the deleted Lottery, matching the backend response.