Trivia quiz app hosted on Dokploy
Adds a deploy job to the Forgejo workflow, gated on check passing and limited to pushes to main. Uses CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID repo secrets (Vikunja #301). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Reviewed-on: https://forgejo.theladners.com/LabNerd/trivia-app/pulls/2 Co-authored-by: Brian Ladner <git@theladners.com> Co-committed-by: Brian Ladner <git@theladners.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| context | ||
| docs | ||
| public | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| CLAUDE.md | ||
| eslint.config.js | ||
| index.html | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
| wrangler.jsonc | ||
Trivia App
Live demo: https://trivia-app.labnerd.net/
A trivia quiz app built with React and TypeScript, deployed as a static site on Cloudflare Workers. There is no backend: every request goes straight from the browser to the question source.
Features
- Four question sources behind one interface: two live APIs (Open Trivia Database, The Trivia API) and offline snapshots of both
- Filter by category, difficulty, and question type; the form adapts to what each source supports
- Paginated sessions: fetch the next 10 questions without leaving the quiz
- Light and dark themes, persisted per browser
- Opens on an offline snapshot, so the app works even when the live APIs are down
Engineering Highlights
- Provider abstraction. Each source is a plain object implementing one
Providerinterface (src/api/providers.ts,src/api/adapters/). Responses are normalized to a single question shape, and the UI adapts to each provider's declared difficulties and types. Adding a source means implementing the interface and registering it. - State and data fetching. Provider selection, session tokens, and category live in a context (
src/context/ProviderContext.tsx). AuseFetchhook owns loading, error, retry, andAbortControllercleanup, with a cancelled-flag guard against state updates after unmount. - Input validation. Route params for category, difficulty, and type are checked against the provider's allowlists before any request is made.
- Offline data pipeline.
scripts/download-trivia.mjsbuilds the offline snapshots from the live APIs. - Card-scanning pipeline.
scripts/scanner.pyextracts questions from photographed trivia cards using the Anthropic vision API, andscripts/import-cards.jsmerges and dedupes the output. See docs/scanner/ARCHITECTURE.md. The resulting datasets come from commercial card games, so they are kept local and are not part of this repository or the deployed site. - Testing and deployment. Vitest and Testing Library cover providers, context, hooks, and pages. Cloudflare Workers Builds deploys on every push to
main.
Local Development
npm install
npm run dev # dev server on port 3000
npm run build # production build to dist/
npm run preview # build + serve preview on port 3000
npm run lint # ESLint
npm run typecheck # TypeScript type check
npm run test # Vitest
To enable the local-only card game providers, generate their data with the scanner pipeline and set VITE_LOCAL_CARDS=true in .env.local.
Tech Stack
- React 19, React Router 7
- TypeScript 5 (strict mode)
- Vite 7, Vitest, Testing Library
- Axios
- Cloudflare Workers (static assets with SPA fallback)
Analytics
The deployed site loads a self-hosted Umami script for anonymous page-view counts. It sets no cookies.