Commit Graph
1809 Commits
Author SHA1 Message Date
Rotimi Best 1cb752d229 chore(gh): add demo section to PR template (#918) 2026-08-16 21:36:05 +01:00
Rotimi Best ddba364bd2 perf(ci): parallelize Railway preview deploys (#909) 2026-08-15 23:31:41 +01:00
Richard Kenneth b314d5995a fix: translation issue in cohort goals widgett (#879) 2026-08-15 19:05:04 +01:00
Victor Jack ad5c8db5a9 feat(dashboard): moved course completion deadline to course settings page (#842) 2026-08-15 18:55:51 +01:00
Richard Kenneth b267db6319 fix: missing import in cohort newsfeed (#880) 2026-08-15 17:01:47 +01:00
Rotimi Best d3bfd58bb0 fix(dashboard): make Explore page course cards fully clickable to open the preview modal (#899) 2026-08-15 17:00:47 +01:00
Rotimi Best c2f6b6d18b docs: YouTube embed transcripts PRD (#900) 2026-08-15 16:56:40 +01:00
Rotimi Best 020f96f2a5 feat(onboarding): remove sell and teach goal options (#908)
Drop sell-online and teach-online from the "What brings you here" list so onboarding only offers employee training, customer courses, and expanding from another platform.
2026-08-15 13:33:19 +01:00
5b63c784b4 fix(course): use distinct shield-check icon for Compliance nav (#906)
* fix(course): use shield-check icon for compliance nav

Certificates and Compliance both used CertificateIcon in the course
sidebar; point Compliance at Lucide's shield-check instead.

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

* fix(ui): add ShieldCheckIcon moving icon for compliance nav

Replace the Lucide shield-check import with a moving-icons component
matching sibling hover-animation patterns, and use it for Compliance.

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

* fix(course): use moving BotIcon for AI tutor nav

Switch the course sidebar AI Assistant item from Lucide bot to the
shared moving-icons BotIcon so hover animation matches other nav items.

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

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-08-15 12:12:19 +01:00
Victor Jack d48d85df2f fix(api): allow inviting students to compliance courses without a certificate deadline (#840)
* Allow teachers invite students

* Greptile comment fix

* coderabbit comment fix
2026-08-15 10:30:45 +01:00
Victor Jack ecd456829c Coderabbit fix 2026-08-14 14:14:18 +01:00
Richard Kenneth 96bade402a fix: sequential progression bug in course with ordered contents (#889)
* fix: sequential progression bug in course with ordered contents

* test(utils): add course progression tests and unify navigation ordering

Reuse flattenNavigableItems in getOrderedNavigableContent so next/previous
navigation follows the same section-aware order as progression locks.
Add vitest coverage for grouped multi-section flattening and sequential
access rules.

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

* fix: address PR review comments for course progression

Simplify grouped flatten ordering, only block progression for lessons and
exercises explicitly, integrate flattenNavigableItems in the sequential access
test, and include @cio/core watch in api:dev:full.

---------
2026-08-14 13:50:59 +01:00
Abdulmuizz Hamzatgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>Rotimi Best
d3a0e01f2b fix: flakiness in railway deployment logs (#896)
* fix: handle flakiness in railway deployment logs by verifying actual deployment status

* fix: improve handling of railway deployment log-streaming errors to ensure accurate deployment status

* fix: enhance deployment status verification to address log-streaming glitches in railway

* fix: allow running of railway workflow for forked version

* Apply suggestions from code review

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Rotimi Best <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-08-14 13:48:58 +01:00
Victor Jack ae39b62369 Greptile comment 2026-08-14 13:35:06 +01:00
Cursor Agentandbest 78f22ef62f docs: add YouTube embed transcripts PRD
Specify cache-first caption fetch for lesson YouTube embeds so AI chat and the existing transcript UI can reuse stored text. Cross-link related research and study-from-source PRDs and add the draft to the tracker.

Co-authored-by: best <[email protected]>
2026-08-14 11:37:20 +00:00
Victor Jack b59b645d65 made the course card clickable everywhere on the body 2026-08-14 12:08:07 +01:00
Victor Jack 0be63fe558 changed the checked value from status to isPublished (#882) 2026-08-14 11:02:16 +01:00
32b8c8748c feat(prototype): slide embed picker PRD and variant explorations (#832)
Add a PRD and three prototype directions (Gallery, Command Bar, Guided) behind the prototype picker harness for the lesson Slide embed flow. Covers the top 10 embeddable slide platforms with per-platform detection, link/embed-code normalization, live preview, and How-to-embed docs links.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-08-14 11:00:06 +01:00
Richard Kennethandrichardkenneth d8d57f1118 docs: update broken documentation links in ONBOARDING.md (#881)
Fixes #870 by updating outdated documentation URLs across Part 1, Part 2, and Part 14 in ONBOARDING.md to their correct paths.

Co-authored-by: richardkenneth <[email protected]>
2026-08-14 10:59:12 +01:00
Rotimi Best a05b081dde fix(ui): render cover images in minimal landing template (#839)
Show course.logo on minimal course cards and hero.image below CTAs so
the default org landing page matches other templates.
2026-08-14 10:58:31 +01:00
Rotimi BestandCursor Agent 41d810d4f7 feat(ui): restyle unsaved-changes bar as a compact inverted card (#893)
* feat(ui): restyle unsaved-changes bar as a compact inverted card

Show Page.SettingsActions only when the form is dirty, as a centered
dark card with a secondary Discard button and a primary Save button.

Co-authored-by: best <[email protected]>

* fix(ui): announce settings actions from a persistent live region

Keep role=status mounted outside the dirty-state card so screen readers
still hear the status when the bar appears, without live-region wrapping
the Discard/Save buttons.

Co-authored-by: best <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-08-14 10:53:28 +01:00
Richard Kennethandrichardkenneth 7ba551aa43 fix(dashboard): hide admin callout banner and actions on cohort courses page for students (#895)
* fix(dashboard): hide admin callout banner and actions on cohort courses page for students

* fix(dashboard): update empty state description for student users

---------

Co-authored-by: richardkenneth <[email protected]>
2026-08-14 10:52:13 +01:00
Rotimi Best a93acd0f6d feat(db): add script to create an org with its admin and plan
Adds `db:create-org` which provisions an organization, adds the given
email as a verified admin, and attaches an active plan (BASIC by
default). Creates the user + profile first when the email is unknown,
derives the site name from the org name unless overridden, and supports
a --dry-run preview mode.
2026-08-14 01:06:06 +01:00
Rotimi Best 56c944a74e chore(website): enable Cloudflare Workers observability and logs (#892)
Turn on Workers Logs for the marketing site Worker, including invocation
logs, matching the dashboard and docs Wrangler configs.
2026-08-13 14:19:21 +01:00
Victor JackandRotimi Best 176949d8c8 feat: add back button on login page to return to landing page (#808)
* Back button in Login page

Modified the back button to have two options, href option and onlick

The onClick bceomes usable when there's no href prop

and the onClick takes you directly to the previous page

While the href takes you to the url you indicated

* added a showBack condition to show the button

* modified the onClick function

* Removed the back button, made the Image act as the back button

* Changed the OAuth place holder

* Update back-button.svelte

* remove f

---------

Co-authored-by: Rotimi Best <[email protected]>
2026-08-12 07:55:19 +01:00
Richard Kennethandrichardkenneth f0695e9d18 fix(dashboard): Certain menus inside of modals are hidden (#875)
* fix: update z-index utility class for Select component across tag category and tutor selection views

* refactor: standardize z-index management using ui:z-menu-elevated, ui:z-app-bar, ui:z-app-bar-elevated and ui:z-modal classes across UI components

---------

Co-authored-by: richardkenneth <[email protected]>
2026-08-12 07:09:54 +01:00
Victor JackandRotimi Best 6537613000 fix: preserve landing page section content when toggled off (#838)
* preserve section content when toggled off

* Update types.ts

* Remove comment on LandingPageLinks enabled property

Removed comment about the default visibility of the section.

---------

Co-authored-by: Rotimi Best <[email protected]>
2026-08-11 14:31:58 +01:00
Abdulmuizz Hamzat 0dc32b5210 refactor: selfhosting docs (#798)f
* Remove the run-docker-full-stack.sh script to streamline the deployment process and eliminate unnecessary complexity.

* fix: update documentation to clarify script behavior for building and pulling images

* update docs

* fix: update script URL to use classroomio.com for downloads

* fix: humanize selfhosting docs and minor amendment

* fix: attend to comment

* fix: humanize docs

* update docs

* fix: adjusted the Restarting ClassroomIO... function

* fix: update commands and documentation for environment variable handling and upgrades

* chore: added space for rebuild
2026-08-11 13:33:20 +01:00
Victor Jack e67c314c46 fix(api): return standard error shape for unhandled public API 500s#809 2026-08-11 13:27:41 +01:00
Cursor AgentandRotimi Best 9adc38bd8a chore(ci): harden supply-chain controls before reopening PRs
Isolate npm publish from PR CI, enforce frozen lockfiles and a 24h
minimumReleaseAge, and document safe pull_request_target usage so fork
PRs cannot reach publish credentials.

Co-authored-by: Rotimi Best <[email protected]>
2026-08-10 21:00:29 +01:00
Cursor AgentandRotimi Best bbf5eb24de fix(website): address CodeRabbit copy review comments
Normalize power users spelling on customer education, and align pricing
hero copy with Basic plan student and AI credit limits.

Co-authored-by: Rotimi Best <[email protected]>
2026-08-08 23:00:19 +01:00
Cursor AgentandRotimi Best 8dddc5ecaf fix(website): rewrite pricing page hero copy
Replace the vague "Certified training for every team" headline with
clearer plan-selection copy that matches the academy positioning.

Co-authored-by: Rotimi Best <[email protected]>
2026-08-08 23:00:19 +01:00
Cursor AgentandRotimi Best a2d1441658 fix(website): keep academy hero accent phrase on one line
Force "customers actually love." onto its own line under "Launch an
academy" so the blue accent does not wrap with an orphaned last word.

Co-authored-by: Rotimi Best <[email protected]>
2026-08-08 23:00:19 +01:00
Cursor AgentandRotimi Best 19b88774db fix(website): escape quotes in compliance renewals copy
Co-authored-by: Rotimi Best <[email protected]>
2026-08-08 23:00:19 +01:00
Cursor AgentandRotimi Best 52225f60fc fix(website): humanize marketing copy and drop em dashes
Update the customer education hero to "Launch an academy customers
actually love", rewrite landing and solutions marketing copy with the
humanizer skill, and remove em/en dashes from those pages while leaving
blog and legal content untouched.

Co-authored-by: Rotimi Best <[email protected]>
2026-08-08 23:00:19 +01:00
Rotimi Best 2a82abfa21 docs(prd): add learning paths PRD with end-to-end HTML prototypes
Full spec for ordered, sequentially unlocked, bundle-priced learning
paths, plus 16 clickable prototypes (public org site, learner LMS, and
teacher workspace) built on the packages/ui tokens and the minimal
org-landing theme. The prototypes are the UX source of truth; start at
prototypes/learning-paths/index.html. Also adds the write-prd skill
codifying the clarify -> write -> prototype workflow.
2026-08-06 07:35:10 +01:00
Victor JackandRotimi Best a83e172e6d fix: show clear error when creating or updating organization site name to an existing one (#793)
* fixed error of not showing the org name API response error

* Fixed the error also for organization settings page

* modified

* Keep field-specific mapping in OrgApi

---------

Co-authored-by: Rotimi Best <[email protected]>
2026-08-06 06:24:07 +01:00
Rotimi BestandRotimi Best 8ad0141ab8 fix(ui): align LandingButton sizing and hero/pricing CTA consistency (#833)
* fix(ui): restore theme pill radius on course landing CTAs

LandingButton hardcoded rounded-md, so hero Enroll and pricing CTAs
lost the per-theme pill radius after CoursePrimaryAction switched to
LandingButton. Use --landing-radius-pill and cn() so theme overrides
still win.

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

* fix(ui): preserve landing theme button radii

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

* fix(ui): restore hero CTA Button sizing with disabled parity

Revert CoursePrimaryAction to the shared Button so hero enroll matches
View curriculum sizing (h-10 text-sm). When enrollment is closed, apply
landing-button CSS vars with forced opacity so theme color overrides do
not keep the hero CTA looking active while pricing stays muted.

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

* fix(ui): align LandingButton sizing and use for hero CTA

Match LandingButton sm/md/lg heights to the shared Button scale (h-8/h-9/h-10,
text-sm) so hero enroll can use landing-theme tokens without oversized CTAs.

CoursePrimaryAction returns to LandingButton for enabled color parity with
pricing; disabled overrides are preserved. Terminal hero drops hardcoded hex
colors in favor of --landing-button-* tokens.

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

---------
2026-08-05 17:06:47 +01:00
Rotimi Best 24743f9986 chore(db): add enterprise upgrade SQL script 2026-08-05 14:45:33 +01:00
Victor JackandRotimi Best 60056a6236 fix: validation of allowing teachers create compliance courses without setting deadline (#778)
* Fixed Validation of allowing teachers create compliance courses without setting deadline

* greptile highlightCertificateSettings decraration suggestion

* greptile multiple error bar showing fix suggestion

* organization race condition

* Delete screenshots/Screenshot 2026-07-22 at 11.52.00 AM.png

* deleted unwanted files

* code review fixes

* Changed AttentionHightlight component to work with just one parameter

created fixed Route Enums, Paths and Sections for

* Kebab-case naming fix

* Changes for code comments

---------

Co-authored-by: Rotimi Best <[email protected]>
2026-08-05 13:28:32 +01:00
Abdulmuizz Hamzat 05936e788d chore: update-open-api-spec (#813)
* chore: update-open-api-spec

* fix: address comment
2026-08-05 08:37:41 +01:00
6ba746c80b fix(dashboard): ensure @cio/ui output.css exists before build (#835)
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-08-05 07:07:19 +01:00
9b9dccc07a feat(dashboard): add last updated at course sort option (#823)
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-08-04 18:20:25 +01:00
Rotimi Best 753f9b3d7b fix(ui): prevent layerchart circular dependency in thumbs submissions 2026-08-04 16:56:09 +01:00
ee4d4e031d fix(course): coerce metadata.discount to number before save (#830)
* fix(course): coerce metadata.discount to number before save

Normalize string discount values from the landing page pricing form and
coerce legacy string metadata on ZCourseUpdate validation so course
settings saves do not fail with a Zod type error.

Fixes classroomio/classroomio#828

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

* refactor(utils): add shared toFiniteNumber for metadata discount coercion

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

* fix(utils): only coerce string discount values in metadata preprocess

Reject broad Number() coercion on booleans and arrays. String and finite
number discounts are normalized; invalid types are left for Zod to reject.

Addresses Greptile review on #830

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

* refactor(dashboard): use writable $derived in pricing form again

Keep toFiniteNumber coercion in setter effect; derived fields stay in
sync when course loads or updates (Svelte 5 writable derived).

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

* fix(utils): reject invalid metadata.discount instead of clearing it

Only coerce numeric strings in preprocess; leave invalid strings,
non-finite numbers, and other types for Zod to reject.

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

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-08-04 15:24:24 +01:00
0c295580f1 chore(skills): import emilkowalski design skills (#829)
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-08-04 14:28:13 +01:00
Rotimi Best 7e8bd3d718 fix(ui): let Page.SettingsActions sticky bar escape its wrapper (#825)
The bar was nested in an extra shrink-0 div together with the dock
sentinel. A sticky element is constrained to its containing block, and
that wrapper is only as tall as the bar plus a 1px sentinel, so the bar
had roughly 1px of travel. It could never float over the page content and
only ever appeared where it naturally sits, at the end of the page.

Render the bar and the sentinel as siblings so the containing block is
Page.Root and the bar has the full page to travel. Measured in headless
Chrome at scrollY=600 in a 713px viewport: wrapped bar bottom was 2484
(off screen), sibling bar bottom is 713, pinned to the viewport.

Also raise the bar from z-10 to z-50. The editor root is relative z-50,
so it painted over the bar on any settings page with an editor, such as
the course welcome email. Matching 50 rather than exceeding it wins the
tie on DOM order while keeping body-portaled dialogs and sheets above the
bar.

ref is now bound to the sticky element rather than the removed wrapper.
No dashboard caller binds ref today.
2026-08-03 02:28:19 +01:00
Rotimi BestandRotimi Best dc55f936a1 fix(dashboard): persist notification preferences after save (#824)
* fix(dashboard): persist notification preferences after save

Personal notification toggles were reset when a stale GET response
arrived after save. Org toggles also triggered a spurious org save
because change detection compared against stale currentOrg settings,
and the UI reset because currentOrg was not updated after org save.

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

* refactor(dashboard): send only changed settings key and tidy load guard

The org update endpoint deep-merges settings server-side, so spreading
local settings into the payload risked writing stale values over
unrelated keys. Also drop a redundant load-version bump and let only
the latest in-flight preferences load clear the loading flag.

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

* refactor(dashboard): hydrate org notification defaults on org id transition

The sync effect read orgHasChanges and savedOrgToggles while writing them
through applyOrgToggleState, causing an extra effect pass after hydration.
/account returns settings together with the org id and role, so gating
hydration on org id transitions is sufficient and also discards unsaved
edits when switching orgs instead of saving them against the wrong org.

Addresses CodeRabbit review on PR #824.

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

---------
2026-08-03 02:18:26 +01:00
Abdulmuizz Hamzat 8af0675895 fix: minio bucket seed race (#820)
* fix(workflows): retry MinIO bucket creation to survive post-boot format window

A freshly duplicated cio-minio's HTTP listener accepts connections
before it finishes formatting its storage backend (confirmed via its
own boot logs: "Formatting 1st pool, 1 set(s), 1 drives per set."),
so a bucket-creation call landing in that window can transiently fail
with a misleading "specified bucket does not exist" response even
though auth and connectivity (mc alias set) already succeeded. Retry
bucket creation up to 10 times with a 5s backoff instead of failing
the whole run on what's usually just a not-ready-yet response.

* fix(workflows): truncate env name slug to fit Railway's length limit

railway environment new failed with "Error in name - Invalid input"
for a 48-char name (pr-cursor-notes-workspace-feedback-6d09-0e6aa7c9)
derived from a long auto-generated branch name. Railway docs cite a
32-char limit for service names; environment names hit the same wall.

The collision-resistant hash suffix (added for the earlier CodeRabbit
finding) is what actually guarantees uniqueness -- the slug is only
there for readability -- so cap the slug at 18 chars instead of
dropping the hash. pr-<slug><=18>-<hash8> stays at or under 30 chars.

* fix(workflows): retry mc anonymous set for the same boot-race window

The mb retry loop worked as intended in the latest test run (attempt
1 failed on all three buckets during cio-minio's post-boot format
window, attempt 2 succeeded), but the very next call -- anonymous set
download on cio/media -- hit the identical "specified bucket does not
exist" error right after mb had just succeeded. Same underlying race,
one command later. Wrap it in the same retry pattern.

* update workflow

* fix: silen error on db:seed

* update workflow

* update workflow

* update workflow
2026-08-03 02:05:10 +01:00
Rotimi Best e79d920aeb fix(newsfeed): tighten card spacing and reposition pinned icon (#818)
* fix(newsfeed): tighten card spacing and reposition pinned icon

- Move pinned icon to top-left corner with a tilted rotation
- Remove the extra pin row that added excess top padding
- Reduce spacing between post content, reactions, and comments
- Remove card box shadow

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

* fix(newsfeed): place pinned icon on card corner instead of avatar

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

* fix(newsfeed): stop dropdown menus locking body scroll

bits-ui defaults DropdownMenu.Content's preventScroll to true, so every
open ran a body scroll lock: a forced full-document style recalc and
reflow, a horizontal shift by the scrollbar width, and pointer-events:
none on the body. On the newsfeed page — a long list of cards each with
a scrollable comment thread — that made the comment ellipsis menu feel
sluggish to open. Menus don't need scroll locking; only modal layers do.

Also tighten card spacing, use the primary color for the pinned icon,
give the card menu trigger a pointer cursor, and switch the comment
save and submit buttons to the loading and secondary variants.

* refactor(newsfeed): store reply targets as data, not markup

Replies used to prepend a presentational <blockquote> — Tailwind classes,
hardcoded English and a copied snippet of the parent — onto the comment
body, and that whole string was persisted to course_newsfeed_comment.content.
Editing a reply therefore showed raw markup in the textarea, the quoted
snippet froze at write time, the label could never be localized, and two of
its ui: utilities never compiled because they appeared only in dashboard code.

Add a replyToCommentId column, distinct from parentId (the thread root), so
a reply can name the comment it answers. The service verifies the target
belongs to the same feed and the same thread; the paginated query joins it
to return replyToAuthorFullname, so the label is rendered from live data by
the new CommentTree.ReplyingTo component using the existing replying_to key.

Rows written the old way still display their stored quote, and
stripLegacyReplyQuote keeps that markup out of the edit textarea.

* refactor(newsfeed): drop redundant comments on reply target plumbing

* fix(newsfeed): address review feedback on pinned icon and quote stripping

- The pin icon carried an unprefixed `text-muted-foreground` plus
  `ui:stroke-primary`, which is emitted nowhere because it appears only in
  dashboard code, so the outline fell back to the muted colour. Use
  `ui:text-primary` and let the stroke inherit currentColor.
- Give the pinned state an accessible name: the icon is `aria-hidden`, and
  header.svelte no longer carries an indicator, so screen readers had nothing.
- Filter feeds on `isPinned` alone. `is_pinned` is only the database column
  name; drizzle maps it to `isPinned` and the API never returns the snake_case
  form, so the `as any` fallback was dead and let the page and card disagree.
- `ui:transition-color` is not a Tailwind utility and emitted nothing; the
  plural `ui:transition-colors` is what applies the transition.
- Strip a legacy reply quote only when the blockquote carries the `reply-quote`
  class the old code always wrote, so a blockquote the author typed is kept.

* docs(agents): forbid presentational markup in persisted content columns

---------
2026-08-02 23:23:18 +01:00