CredentialWorks is a static Next.js course storefront and credential-verification demo for Northern SafeWorks Training, a fictional Canadian training brand. It combines a searchable catalogue, industry pages, MDX articles, a public demo dashboard, and build-time signed sample credentials in one interview-ready project.
Requirements: Node.js 20 or newer and npm.
cp .env.example .env.local
npm ci
npm run devOpen http://localhost:3000. The defaults in .env.example are sufficient for local JSON data. On PowerShell, use Copy-Item .env.example .env.local instead of cp if needed. npm ci also runs the prepare script, which generates the gitignored src/data/credentials.json and public/qr/*.png from credentials.source.json; npm run build regenerates both with the configured salt and site URL.
Sanity is optional. To use CMS course data, configure NEXT_PUBLIC_SANITY_PROJECT_ID, NEXT_PUBLIC_SANITY_DATASET, and, only for a private dataset, SANITY_API_READ_TOKEN. Then follow studio/README.md to run the standalone Studio. If Sanity is not configured or cannot return valid courses, the storefront uses the checked-in JSON data.
| Command | Purpose |
|---|---|
npm run dev |
Start the Next.js development server. |
npm run check |
Run Biome formatting and lint checks. |
npm run typecheck |
Run TypeScript without emitting files. |
npm test |
Run the Vitest suite in jsdom. |
npm run build |
Sign demo credentials, generate QR codes, and export the static site to out/. |
The app uses Next.js App Router with output: "export". There are no runtime API routes, server actions, middleware, ISR, or runtime secrets. Local and optional Sanity data are parsed with Zod at build time. Credential signing and QR generation run in prebuild, so generated src/data/credentials.json and public/qr/ artifacts are deliberately ignored by Git.
The optional Sanity Studio is a separate application in studio/. The public storefront reads Sanity at build time; publishing CMS content therefore requires a new storefront build.
| Variable | Required | Purpose |
|---|---|---|
NEXT_PUBLIC_SITE_URL |
Yes for production builds | Absolute origin used by metadata, sitemap, robots, and generated QR links. |
NEXT_PUBLIC_CREDENTIAL_SALT |
Yes | Public demo salt used for build-time hashes and browser verification. |
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME |
No | Enables the optional Cloudinary image loader. |
NEXT_PUBLIC_SANITY_PROJECT_ID |
No | Enables optional Sanity course data. |
NEXT_PUBLIC_SANITY_DATASET |
With Sanity | Dataset name; defaults to production. |
SANITY_API_READ_TOKEN |
Private Sanity only | Server-only build token; never expose it with a NEXT_PUBLIC_ prefix. |
Never commit .env.local or pass private secrets as Docker build arguments.
NSW-2025-0001— validNSW-2024-0002— expiredNSW-2025-0003— revokedNSW-2025-0004— tamperedNSW-2026-0005— valid and expiring soon
The outcome precedence is not_found → tampered → revoked → expired → valid. Unit tests document the security-relevant tamper-first behavior.
Demo hashes are signed at build time with a public salt to demonstrate the verification UX; they do not provide production-grade authenticity because the signing material is public. Production signing would use a server-held private key with server-side verification. The dashboard would also sit behind session authentication and authorization rather than being a public static page.
Build and serve the static export with Caddy:
docker build -t credentialworks --build-arg NEXT_PUBLIC_SITE_URL=http://localhost:8080 .
docker run --rm -p 8080:80 credentialworksVisit http://localhost:8080, a deep course URL, and a nonexistent URL. Caddy serves the branded static 404 and applies immutable caching to hashed Next.js assets plus one-day caching to generated QR images.
| Technology or practice | Where it is used |
|---|---|
| Next.js, React, TypeScript | App Router pages and typed components under src/app and src/components. |
| Responsive, accessible UI | Semantic page structure, keyboard-visible controls, responsive layouts, verifier live region, and reduced-motion styles. |
| Content management | Optional standalone Sanity Studio with typed schemas and build-time storefront fallback. |
| Data validation | Zod schemas validate local JSON and CMS responses before rendering. |
| Testing | Vitest covers verification precedence, dashboard metrics, and the React Testing Library catalogue flow. |
| CI/CD readiness | GitHub Actions runs format/lint, types, tests, and the full static build for pushes and pull requests. |
| Containerization | Multi-stage Docker build serves the exported site from Caddy with explicit cache headers. |
| SEO and performance | Static generation, route metadata, sitemap, robots, structured data, and optional optimized image delivery. |
| Analytics | Typed catalogue, verification, CTA, and course-view events feed window.dataLayer. |
Lighthouse must be recorded in a real browser against the final served build; scores are intentionally not invented in this repository.
| Page | Performance | Accessibility | Best Practices | SEO | First-load JS |
|---|---|---|---|---|---|
| Home | Pending final audit | Pending final audit | Pending final audit | Pending final audit | Pending build measurement |
| Catalogue | Pending final audit | Pending final audit | Pending final audit | Pending final audit | Pending build measurement |
| Course detail | Pending final audit | Pending final audit | Pending final audit | Pending final audit | Pending build measurement |
Before publishing, run Lighthouse on those three routes from the production-like Caddy or static server, replace the pending cells with measured results, and add the resulting screenshots. Targets are at least 95 in each category and less than 110 kB of first-load JavaScript on marketing pages.
Before publishing:
- Run
npm run check,npm run typecheck,npm test, andnpm run build. - Serve
out/and check every route,sitemap.xml,robots.txt, deep links, and the branded 404. - Build and exercise the Docker image and inspect response cache headers.
- Complete keyboard-only and screen-reader spot checks; confirm one
h1per page, logical heading order, visible focus, filter and accordion operation, and announced verification outcomes. - Run Lighthouse, record the results above, and retain screenshots as release evidence.
Northern SafeWorks Training is invented for this portfolio project. No WKT copy, logos, trademarks, proprietary content, or customer data are used.