Files
classroomio/apps/docs
Rotimi BestandRotimi Best 2ebb544a81 fix(dashboard): cache org stats in Redis with versioned invalidation (#883)
* fix(dashboard): cache org stats in Redis with versioned invalidation

Replace the never-expiring process-local cache in the org dashboard page
load with API-side Redis caching keyed per org. A version stamp prevents
slow reads from writing pre-purge data back into the cache.

Invalidate on course create/update/delete, student enroll/remove, certificate
claim, and progress reset. Remove the dashboard SSR module cache so every
page load reads through the API cache layer.

Fixes #852

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(dashboard): address PR review on stats cache invalidation

- Validate bust via ZDashStats instead of raw query access
- Use import type for OrganisationAnalytics
- Resolve orgId before course delete so invalidation is reliable
- Invalidate immediately after persisted writes, before fallible side effects
- Invalidate on member role changes and program course access enrollment

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(dashboard): complete stats cache invalidation coverage

Wire org stats cache purge on lesson completion (manual toggle and
video-watch auto-complete) so topCourses completion percentages stay
fresh. Harden bulk member adds with sequential inserts and a finally
invalidation boundary, and only purge org invite enrollments when new
group memberships were actually inserted.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(core): make lesson completion stats purge best-effort

Wrap org stats cache invalidation in try/catch so a lookup or Redis
failure cannot turn a successful lesson completion into a 500.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(dashboard): keep org stats cache through lesson completion

Lesson complete is a high-frequency event and usually does not change
the rounded dashboard percentages. Rely on a 10 minute TTL instead of
purging the snapshot on every completion.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* docs: explain how admin dashboard numbers are calculated

Add a user-facing guide covering headline counts, top courses,
certificates, login activity, and when the 10 minute snapshot refreshes.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

---------
2026-08-16 22:15:34 +01:00
..

docs

The ClassroomIO documentation site, built with Blume (Astro + Vite). Served at classroomio.com/docs.

Requirements

Node >= 22.12 (see .nvmrc). The rest of the monorepo is still on Node 20, so switch versions before working here:

nvm use

Develop

pnpm dev      # fetches the OpenAPI spec, then starts the dev server
pnpm build    # builds to dist/, then packages to build/ for the worker
pnpm preview  # builds, then serves through the worker on :4173
pnpm validate # checks internal links and heading anchors

Use pnpm preview before deploying anything that touches routing. pnpm dev serves pages straight from Blume and never sees Cloudflare's asset server, so it cannot show you the _redirects rules, the 404 page, or the trailing-slash handling — only preview runs the site the way production does.

How it's served

The site is fully static. classroomio.com/docs/* is proxied by the marketing site (apps/website/src/hooks.server.ts) to the cio-docs worker, which serves the build as static assets — there is no server-side rendering.

Two things follow from being mounted at /docs:

  • Links are authored root-relative (/course-enrollment). Blume prepends the base at build time.
  • Images must be written with the base (/docs/certificates-rules.webp). Blume deliberately does not rebase images, so an unprefixed path would 404.

scripts/package-assets.mjs moves dist/ under build/docs/ to match the served path and writes the _redirects file (/docs → /docs/home, and the legacy /docs/how-to-guides/* → /docs/*).

API reference

/docs/api is the Scalar API reference, rendered at build time (the old /docs/api/reference path 301s here). scripts/fetch-openapi.mjs pulls the spec from R2 and injects the bearer auth scheme via @cio/utils/openapi/public-api. Because it is static, a new spec reaches the site only on a rebuild — .github/workflows/upload-openapi-spec.yml triggers one automatically.