Commit Graph
65 Commits
Author SHA1 Message Date
Rotimi Best 08ea608f94 feat(media): delete videos from the media library
Add an admin flow to permanently delete a media asset and clean up
everything it leaves behind.

- Delete dialog with a destructive, irreversible confirmation; deletion
  is blocked while the video is still attached and shows the in-use
  lessons so the admin can detach first.
- Usage list resolves lesson/course context for each attachment, links
  to the lesson editor via an anchor (right-click "open in new tab"),
  and exposes a refresh button to re-check usage after detaching
  elsewhere.
- Storage cleanup runs as a background maintenance job: the producer
  captures the asset's object keys/prefixes up front and an idempotent
  worker sweeps HLS segments, thumbnails, audio, transcript, and the
  raw upload from object storage.

Also harden usage accounting so stale attachments cannot trap an asset:

- asset_usages has no FK to its polymorphic target, so prune a lesson's
  usage rows whenever the lesson is hard-deleted (single + bulk paths).
- Usage resolution drops orphaned lesson usages (lesson gone or course
  soft-deleted) so they neither appear in the list nor block deletion.

Document the icon-only button convention (variant="secondary") in
AGENTS.md.
2026-06-28 12:36:13 +01:00
a695c97eda feat(course): reset student course progress (#671)
* feat(course): add reset student course progress action

Give course admins and tutors a destructive reset-progress flow on the
student people detail page. The backend clears learner progress for one
(courseId, memberId) pair in a single transaction while keeping the
groupmember enrolled, and logs an audit summary. The dashboard shows a
confirmation dialog with impact counts from analytics before deleting.

Co-authored-by: Rotimi Best <[email protected]>

* fix(i18n): add localized reset progress strings for all locales

Co-authored-by: Rotimi Best <[email protected]>

* fix(course): scope reset progress to course data only

Stop deleting org-level community questions and answers during course
progress reset. Fix course newsfeed deletion order so comments on a
student's posts are removed before the posts themselves. Restrict reset
to student members, move analytics loading to the person detail layout,
and rename impact copy to course newsfeed only.

Co-authored-by: Rotimi Best <[email protected]>

* fix(course): address Greptile review on reset progress

Make progressImpact opt-in via includeProgressImpact query param so
analytics requests avoid extra DB work unless the reset dialog needs it.
Count only student-authored course newsfeed posts and comments in the
impact summary to avoid double-counting replies on the student's posts.

Co-authored-by: Rotimi Best <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-06-27 10:14:52 +01:00
Abdulmuizz HamzatandRotimi Best 6d594b4296 feat: harden self-hosting + add pull-based deploy, Coolify/Railway (#666)
* feat: enhance self-hosting support and CI/CD workflows

- Updated .env.example to clarify auth secret generation and self-hosting mode.
- Added CI workflow for build and lint checks on PRs and main branch pushes.
- Improved docker-publish workflow to trigger on relevant file changes and added multi-arch support.
- Enhanced upload-openapi-spec workflow with caching for dependencies.
- Removed obsolete Dockerfile for API service and added jobs worker to Docker setup.
- Updated self-host documentation to reflect new jobs worker requirement.
- Modified Dockerfiles for API and Dashboard to optimize dependency fetching and installation.
- Enhanced docker-compose.yaml with health checks and jobs worker configuration.
- Updated docker-push.sh to support multi-architecture builds and improved logging.
- Revised documentation for publishing images and self-hosting to include new features and requirements.

* feat(self-host): safe versioned releases + pull-based image pipeline

Builds on the pull-based images compose (docker-compose.images.yaml) to make
pulling pre-built images safe for self-hosters:

- docker-publish: stable-releases + edge tag policy. Git tag vX.Y.Z publishes
  X.Y.Z/X.Y/X/latest; a push to main publishes only :edge (latest never moves
  on a routine merge).
- Pre-publish smoke-test gate: each image is booted against ephemeral
  postgres+redis and healthchecked before any push (docker/smoke-test.sh).
- CIO_VERSION pinning in .env.example; run-docker-full-stack.sh --images flag
  to pull instead of build.
- Docs: SELF_HOST pull-from-images + versioning/upgrade/rollback sections,
  PUBLISHING_IMAGES tag policy + pnpm release flow, self-hosting.mdx quickstart.

* feat: enhance self-hosting support and CI/CD workflows

- Updated .env.example to clarify auth secret generation and self-hosting mode.
- Added CI workflow for build and lint checks on PRs and main branch pushes.
- Improved docker-publish workflow to trigger on relevant file changes and added multi-arch support.
- Enhanced upload-openapi-spec workflow with caching for dependencies.
- Removed obsolete Dockerfile for API service and added jobs worker to Docker setup.
- Updated self-host documentation to reflect new jobs worker requirement.
- Modified Dockerfiles for API and Dashboard to optimize dependency fetching and installation.
- Enhanced docker-compose.yaml with health checks and jobs worker configuration.
- Updated docker-push.sh to support multi-architecture builds and improved logging.
- Revised documentation for publishing images and self-hosting to include new features and requirements.

* ci: run build-only on PRs, defer lint until repo-wide format

* docs: point Railway template link at the new template URL

* fix: address Greptile review + dashboard CI build env

- docker-push.sh: only also-tag :latest for real semver releases, so
  VERSION=edge/main/dev can't clobber the stable latest pointer (P1).
- run-docker-full-stack.sh: stop deriving OBJECT_STORAGE_PUBLIC_ENDPOINT from
  the dashboard origin (broke presigned URLs for videos/documents buckets);
  derive only the public media base URL and document the S3-API route.
- ci.yml: provide PUBLIC_IS_SELFHOSTED at build time (dashboard reads it from
  SvelteKit's strict $env/static/public); bump cache actions v3 -> v4.
- upload-openapi-spec.yml: bump cache actions v3 -> v4.
- smoke-test.sh: dashboard gate now fails on 5xx instead of accepting any response.

* ci: scope PR build gate to the api + dashboard closures

Building every package surfaced pre-existing rot in apps that aren't part of
this PR (storybook's missing @lucide/svelte import, course-app prettier debt).
The gate's purpose is to verify the two Docker-image apps; the rest are built on
PRs by their own Cloudflare Workers builds. Filter to @cio/api... and
@cio/dashboard... so the gate covers exactly what it should.

* fix(docker-push): make latest_arg always return 0

Use an `if` instead of `is_release_version && printf` so the helper returns 0
even when it emits no tag. A non-zero return inside the $(latest_arg ...) build
argument is fragile under set -e (and would abort with inherit_errexit), which
would break the edge/main builds the policy explicitly supports.

* fix(docker): copy patches/ into jobs image so pnpm install resolves patchedDependencies

The jobs Dockerfile ran `pnpm install` without copying patches/, so pnpm
failed hashing package.json#pnpm.patchedDependencies (layerchart patch),
breaking the self-host full-stack build. api/dashboard already copy patches/.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(docker): build jobs worker deps in topological order

The jobs Dockerfile built workspace packages with a hand-written list that
omitted transitive deps (e.g. @cio/question-types, consumed by @cio/utils),
so `tsc` failed with TS2307 during the self-host build. Use the recursive
`pnpm -r --filter @cio/jobs-worker... build` like the api/dashboard images,
which builds the full dependency closure in the correct order.

* refactor(docker): move jobs Dockerfile to docker/Dockerfile.jobs and align dep install

Move apps/jobs/Dockerfile -> docker/Dockerfile.jobs so all three image recipes
live together in docker/, and update every reference (docker-compose.yaml,
docker-push.sh, docker-publish.yml, railway.jobs.json, self-hosting.mdx).

Also align its dependency install with the api/dashboard images: replace the
hand-maintained list of workspace package.json copies (which silently missed
transitive deps like @cio/core / @cio/ai-assistant, leaving their node_modules
uninstalled and breaking the build with TS2307) with the proven pnpm-fetch
pattern: fetch the full lockfile into a cached store, COPY . ., then an offline
filtered install of @cio/jobs-worker... This installs the complete dependency
closure regardless of how the workspace graph grows.

* refactor(docker): move docker-compose.yaml to repo root for Coolify compatibility

Coolify's Docker Compose build pack forces Base Directory to the repo root and
resolves build contexts from there, so the compose's `context: ..` (needed when
the file lived in docker/) climbed above the repo and failed with
`lstat /artifacts/docker: no such file or directory`.

Move docker/docker-compose.yaml -> ./docker-compose.yaml and change the build
contexts from `..` to `.` so the context is the repo root with no upward path.
This works identically for run-docker-full-stack.sh, manual `docker compose up`,
and Coolify. Dockerfiles stay referenced as docker/Dockerfile.*. Update the
references in run-docker-full-stack.sh and the docs accordingly. The images
compose (docker-compose.images.yaml) is unaffected (no build context) and stays
in docker/.

* Add self-hosting documentation for Coolify and Railway

- Created new documentation for deploying ClassroomIO on Coolify using a pull-based Docker Compose stack.
- Added comprehensive guide for self-hosting ClassroomIO with Docker, covering both pre-built images and building from source.
- Introduced a new meta.json file to organize self-hosting documentation pages.
- Developed a Railway deployment guide with environment variable configurations for API, dashboard, and jobs worker.
- Updated self-hosting blog post to link directly to the new Railway guide.
- Removed obsolete Railway configuration files (railway.api.json, railway.dashboard.json, railway.jobs.json).
- Enhanced run-docker-full-stack.sh script to improve MinIO credential generation and service readiness checks.
- Updated docker-compose files to enforce strong secret requirements for authentication.
- Adjusted self-hosting references in the Docker documentation to include new guides.

* fix(docker): point references at root-level docker-compose.images.yaml

The pull-based compose file moved to the repo root (alongside docker-compose.yaml).
Update IMAGES_COMPOSE_FILE in run-docker-full-stack.sh, the file's own header
example, and the docs (docker.mdx curl URL, coolify.mdx path + blob link,
SELF_HOST.md relative link + curl URL).

* fix(self-host): address review round 2 — drop unused AUTH_BEARER_TOKEN, pull-by-default, doc fixes

- Remove AUTH_BEARER_TOKEN everywhere (unused in app code; PRIVATE_SERVER_KEY is the real
  dashboard<->api key). Simplifies run-docker-full-stack.sh secret generation.
- run-docker-full-stack.sh now defaults to PULLING pre-built images; add --build for source.
- docs: recommend MinIO, 'a way' (not 'recommended way') to self-host, remove Direct API Access
  section, trim Common Commands, SMTP vars per-line, fix PRIVATE_SERVER_KEY wording.

* feat(db): versioned migrations for self-host with auto-baseline

- Regenerate a single clean baseline migration; db:setup now runs drizzle-kit migrate (not push).
- Add db:generate / db:migrate / db:baseline scripts.
- baselineMigrationsIfNeeded(): adopt an existing (push/supabase) schema into drizzle's journal so
  migrate won't try to recreate existing objects ("type/table already exists"). No-ops on fresh
  and already-tracked databases; runs automatically in db:setup before migrate.

* ci(openapi): drop stale ci.yml reference in turbo-cache comment

ci.yml was removed earlier in this review; the openapi workflow's turbo cache still
warms from its own prior runs via restore-keys, so this is a comment-only fix.

* fix(db): make baseline squash-safe for migrate-tracked databases

baselineMigrationsIfNeeded returned early whenever drizzle.__drizzle_migrations
had any rows. After a migration squash, an instance tracked under the old
journal (pre-squash hashes) would then let `drizzle-kit migrate` re-run the
consolidated baseline — whose journal `when` is newer than every old created_at
— and crash on `CREATE TYPE/TABLE "..." already exists` at startup.

Now, when history exists but the consolidated baseline hash is absent, insert
that hash (marking it already applied) so migrate skips it. Only the baseline
entry (idx 0) is reconciled, so genuinely pending future migrations still run.

Verified end-to-end against Postgres: reproduces the original crash, confirms
the fix no-ops migrate on a pre-squash DB, and confirms a later 0001 migration
still applies. Fresh-DB and already-adopted paths unchanged.

* fix(docker): add explicit id to pnpm-store cache mounts

Railway's Metal builder rejects BuildKit cache mounts that omit an
explicit id (it does not default id to target like standard Docker/
BuildKit do), failing the build at parse time with:

  dockerfile invalid: flag '--mount=type=cache,target=/pnpm/store'
  is missing an id argument

Add id=pnpm-store to all six cache mounts across the api, dashboard,
and jobs images. Harmless on Docker Desktop / CI / GitHub Actions, and
required for the Railway self-host build path.

* fix(docker): drop BuildKit cache mounts for Railway compatibility

Railway's builder rejects cache-mount ids not prefixed with its
per-service cacheKey, which can't be hardcoded in a Dockerfile shared
across CI, local Docker, and three Railway services. Remove the
--mount=type=cache flag (keep --store-dir /pnpm/store); the store is
baked into the fetch layer and reused by the offline install, so
behaviour is unchanged. Supersedes 9c4633f2c.

* fix(docker): force LF for shell scripts and Dockerfiles via .gitattributes

cio-api crash-looped on Railway with 'exec /app/entrypoint.sh: No such
file or directory' because the entrypoint shipped with CRLF endings
(Windows checkout, core.autocrlf=true) — the '#!/bin/sh\r' shebang is
unresolvable in the container. Add .gitattributes pinning *.sh and
Dockerfile* to LF so container-executed files are never CRLF regardless
of contributor OS.

* fix(db): serialize db-setup with an advisory lock to prevent baseline double-insert

Concurrent db:setup runs (e.g. multiple API replicas booting together) could both
pass the baseline 'already present?' check and insert duplicate rows into
drizzle.__drizzle_migrations, which has no unique constraint on hash. Wrap baseline
+ migrate in a pg_advisory_lock held on a dedicated connection so concurrent
startups serialize: the loser blocks, then sees the baseline applied and no-ops.

* docs(railway): update template deploy URL to classroomio-1

---------
Co-authored-by: Rotimi Best <[email protected]>
2026-06-23 10:17:09 +01:00
f2d7afc590 fix(analytics): show per-student last seen from login and session activity (#674)
* fix(analytics): show per-student last seen from login and session activity

Return ISO last-seen timestamps from course analytics instead of
pre-formatted strings so the dashboard can format them per student.
Batch-fetch last seen for all students and include session activity
alongside login events for more accurate, distinct values.

Co-authored-by: Rotimi Best <[email protected]>

* fix(analytics): destructure first row from getLastLogin query results

Promise.all returns an array per Drizzle select; nested destructuring
is required so loggedInAt and updatedAt are read from row objects.

Co-authored-by: Rotimi Best <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-06-23 10:09:47 +01:00
Rotimi Best 75bd0f4b67 fix(db): add groupmember to GROUP BY in getEnrolledCourses
The query selected groupmember.certificate_earned_at as a non-aggregated
column but only grouped by course.id and course_completion_record.id,
causing Postgres error 42803. Add groupmember.id (its PK) to the GROUP BY
so the certificate column is functionally determined.
2026-06-22 07:12:26 +01:00
Rotimi Best 81294a47f9 feat: live class sessions, CE workflow, and docs (#683)
Adds live-class session scheduling on lessons (join link, date/time,
timezone) with automatic 24h and 1h reminder emails plus a calendar
invite (.ics), a time-change notification with an updated invite, a
teacher-authored welcome email and session invite on enrollment, and a
"Notify students" quiz nudge.

LMS gets an upcoming live-sessions card and Join buttons, plus a
reusable live-session card component (3 states) in @cio/ui with a
Storybook story. The sidebar content tree now reflects sequential
progression locking like the content list, and the course-completion
confetti regression is fixed.

Org-based emails no longer reference ClassroomIO by name. Docs add
live-class, welcome-email, and course-progression guides, expand the
certificates guide (design, signatures, corrected labels), move
plan-required callouts to the top, flatten how-to-guides to the docs
root with redirects, and add screenshots. Includes an add-docs-image
skill.
2026-06-22 05:43:48 +01:00
Rotimi BestandCursor ab735505e5 fix(db): align course metadata types with validation schema
Make reward.description and instructor.coursesNo optional in the course
metadata JSON type so API builds match Zod validation.

Co-authored-by: Cursor <[email protected]>
2026-06-16 20:36:01 +01:00
Abdulmuizz Hamzat 5e2dcf71f4 docs: add dev setup guide and make package scripts cross-platform (Windows) (#664)
* chore: update README with environment setup instructions and demo account credentials

fix: replace rm -rf with rimraf in build scripts for cross-platform compatibility

fix: add shell option to pnpm command in db-setup script for Windows compatibility

chore: update package.json files to use rimraf instead of rm -rf for consistency

chore: add rimraf dependency to pnpm-lock.yaml

feat: add postinstall script to install lefthook git hooks in a cross-platform manner

* docs(qa): add QA test plan, flow checklists, and bug-report workflow

Add a repo-native QA execution layer on top of FEATURE_AUDIT.md (the
feature map). Test cases live in the repo; bugs live in GitHub Issues, so
there is no duplicate tracker to keep in sync.

QA system:
- FEATURE_AUDIT.md: the feature map - every feature, its user flow, and
  edge cases with file:line citations.
- qa/README.md: the workflow, severity/difficulty rubrics, label setup,
  and a static feature -> flow coverage map proving all 42 audited
  features have a test plan.
- qa/flows/01..13: per-flow happy-path checklists covering signup &
  onboarding, course authoring, people/invites/roles, student ->
  certificate, AI assistant & tutor, programs/attendance, community,
  org public site, media & import, org settings, public API/MCP,
  enterprise SSO/licensing, and billing & AI credits.
- .github/ISSUE_TEMPLATE/qa-bug.yml: a QA Bug template requiring
  severity, difficulty, area, repro steps, and a screen recording.

Dev tooling:
- package.json: add db:studio and docker:build scripts.
- packages/db: add db:studio (drizzle-kit studio); fix db:reset on
  Windows by spawning the pnpm.cmd shim with shell:true (Node's
  CVE-2024-27980 fix blocks spawning .cmd/.bat without a shell).

* chore: update QA bug template and README for clarity and improved workflow

* docs: enhance DEV_SETUP_NOTES and README for clarity on prerequisites and email setup
chore: update package.json scripts for cross-platform compatibility
feat: add cross-platform Prettier check script for pre-commit hook

* Remove QA documentation and flow files for ClassroomIO

- Deleted the entire QA README.md file which contained the QA process, bug filing guidelines, and feature coverage map.
- Removed all flow files (01 to 13) that detailed the QA testing processes for various features including admin signup, course authoring, people management, student learning, AI assistant, programs, community, org public site, media management, org settings, public API, enterprise SSO, billing, and AI credits.

* fix: update postinstall script
2026-06-15 17:40:06 +01:00
Rotimi Best 52d9bf18c3 feat: add sequential progression and video watch lesson completion
Gate student content by completion order, track per-video watch progress
with seek enforcement, and auto-complete lessons at the configured
threshold. Show ring progress in the lesson header, keep watch progress
on rewatch, and sync progression locks client-side so the next item
unlocks after completion.
2026-06-11 14:48:34 +01:00
Rotimi Best c8154409f5 feat: improve LMS course discovery and enrollment UX
- Add paginated recommended courses to LMS dashboard (top 3, list
    view)
    and explore page (12 per page); exclude courses blocking new
    students
  - Polish course preview modal: remove padding, render requirements as
    HTML, round banner image, secondary close button
  - Redirect to course start page after successful enrollment instead of
    /lms;
    keep spinner active until navigation completes
  - Auto-enable "allow new students" when a course is published
  - Move Explore after My Learning in LMS sidebar nav
  - Move Certificates after Content in course sidebar
  - Add hiddenColumns prop to CourseListRow; show "Learn more" button
    instead of actions menu in LMS explore context
2026-06-11 14:22:18 +01:00
Rotimi Best 02ae9b81b8 fix: get the type of settings 2026-06-10 09:08:57 +01:00
Rotimi Best ab25b8e5d7 chore: improve enrollment documentation 2026-06-10 02:56:00 +01:00
Rotimi Best d5b07d356e feat: HLS streaming, transcripts, agent course generation & landing-page course previews (#663)
New features:
- HLS video encoding/streaming (hls routes, hls-encoder) and a media
  transcripts pipeline (transcripts route + media-transcript queries)
- AI assistant chat vs. run model: agent-course-generation worker/queue,
  run-monitor pane, chat-changes card, content ask-ai bar
- Per-theme course-page previews across org landing-page themes (bold,
  classic, corporate, editorial, etc.) with a shared course token/component set
- View-as-student mode and login-link / link-invite invite flows

Refactor:
- Extract packages/core consolidating config (env, storage, dashboard-url),
  services and utils previously duplicated under apps/api/src/config
- Remove legacy agent service modules (chat-tools, chat-context,
  context-window, document, fetch-url, lesson-content)
- Relax Cross-Origin-Resource-Policy to allow cross-origin media embedding
- Add format script to tenant-router so turbo run format covers it
2026-06-07 18:43:05 +01:00
Rotimi Best 730bcee553 fix: send memberid on checkout 2026-05-27 20:44:56 +01:00
Rotimi Best 9082cbebf4 chore: add more agent upcoming features 2026-05-27 11:55:40 +01:00
Rotimi Best 30e5f9cd54 chore: auto compact chat 2026-05-21 19:46:17 +01:00
Rotimi Best ec92fc5083 fix: approximated not verifying domain, change request type for /agents
route
2026-05-21 14:05:37 +01:00
Rotimi Best 61cbb258ea chore: move lms to myclassroomio.com and use approximated for custom
domain
2026-05-21 11:29:47 +01:00
Rotimi Best 29b02c0011 chore: auto enroll, custom domain improvement, course pricing style improv. 2026-05-20 21:58:42 +01:00
Rotimi Best 38c0c0b4f0 feat: enhance tenant provisioning hook to support OAuth state resolution for organization enrollment 2026-05-20 17:12:00 +01:00
Rotimi Best f82e4ff4c4 debug: tenant provisioning hook 2026-05-20 16:50:07 +01:00
Rotimi Best f7570d9a21 refactor: remove request logging from createProfileHook for cleaner output 2026-05-20 16:35:32 +01:00
Rotimi Best 266f9f2b22 chore: simplify workflow for self hosters. 2026-05-20 16:33:11 +01:00
Rotimi Best da71598e4a feat: add console logging for layout data in dashboard 2026-05-20 16:01:07 +01:00
Rotimi Best cbf1218186 chore: move orgRole back to session 2026-05-20 15:58:43 +01:00
Rotimi Best fe455dd3f7 fix: users can retriger transcription 2026-05-20 14:07:45 +01:00
Rotimi Best f7d6f81901 chore: move orgRoles from session to Redis 2026-05-20 11:52:49 +01:00
Rotimi Best 94c75779bf refactor: enhance API routing to include direct handling of /api/auth/* for improved auth flow 2026-05-20 09:04:55 +01:00
Rotimi Best 71fbb2101c feat: auto enroll into org as student and oauth improvements 2026-05-20 08:32:56 +01:00
Rotimi Best bad837a4cb feat: add tenant-router Cloudflare Worker with routing for classroomio domains and update dependencies 2026-05-19 23:53:51 +01:00
Rotimi Best 274acad987 refactor: reorganize imports in auth.ts for better clarity and maintainability 2026-05-19 10:25:08 +01:00
Rotimi Best 917a71ccce feat: support custom domain in TRUSTED_ORIGIN 2026-05-19 09:59:56 +01:00
Rotimi Best 65962b49d5 chore: remove vitest configuration file and update TypeScript paths in analytics package 2026-05-19 08:00:01 +01:00
Rotimi Best 1d10b852b5 chore: update website name in wrangler config and refactor compliance seeding logic 2026-05-19 04:51:43 +01:00
Rotimi Best 0048f267e2 fix: deployment errors 2026-05-19 04:32:32 +01:00
Rotimi Best ac3a2c14a7 feat: certificates, landing pages, ai tutor, website update, compliance 2026-05-18 03:47:22 +01:00
Rotimi Best c31ad63955 chore: ai, public course and other ui improvements 2026-05-09 06:05:03 +01:00
Rotimi Best 5717dd8004 feat: widgets and ai chat 2026-04-27 11:40:38 +01:00
Rotimi Best d6e812d08b feat: enhance API and middleware functionality: add public API routes, implement program and course member checks, and improve rate limiting for invite acceptance. Update OpenAPI spec generation to focus on public API endpoints and add new middleware for automation key scopes. Include new documentation for lesson AI features and public API tests. 2026-04-12 08:58:41 +01:00
Rotimi Best 5a8f50f67b feat: certification rules, content sanitization, handle draft course updates and reset content, better redis logging 2026-03-30 08:24:55 +01:00
Rotimi Best ff2ad59377 Implement textarea validation for character limits in submission service; enhance user feedback with error messages for minimum and maximum character requirements. Update translations and UI components to support new validation features. 2026-03-29 09:50:24 +01:00
Rotimi Best 2e38f7541b Refactor avatar components across the application to use the new UserAvatar component, replacing instances of the Avatar component for improved consistency and maintainability. 2026-03-28 23:22:55 +01:00
Rotimi Best 3919ff6c2f Enhance audience management features with new components and API integrations
- Added `AssignCoursesModal`, `AudienceMemberRow`, `AudienceTable`, and related components for improved audience management UI.
- Implemented audience invite actions including resend and revoke functionality.
- Updated audience API to support importing members and assigning courses.
- Refactored audience data handling to include status and invite management.
- Improved overall user experience with pagination and selection features in the audience table.
2026-03-22 18:42:23 +01:00
Rotimi Best ef4424e267 feat: implement automation feature 2026-03-18 23:39:12 +01:00
Rotimi Best 5a68d1bd7c refactor: add BetterAuthPlugin type to loginLink and tokenExchange functions 2026-03-16 05:25:14 +01:00
Rotimi Best 492f8dc5ed feat: implement automation key management with middleware and routes for organization API keys, invite improvements 2026-03-13 18:40:07 +01:00
Rotimi Best 50252c2327 feat: add login link functionality with token generation and validation 2026-03-13 05:24:47 +01:00
Rotimi Best 1d378fa5ff add markUserAndProfileEmailVerified 2026-03-13 04:58:33 +01:00
Rotimi Best 420409722a refactor: reorganize imports in course route and update middleware for course tags endpoint 2026-03-12 22:16:51 +01:00
Rotimi Best a42ae50bca feat: add check for existing organization members to prevent duplicates during member creation 2026-03-12 05:31:46 +01:00