feat(marketplace): system-admin listing and version management - #8526
Open
LWS49 wants to merge 2 commits into
Open
Conversation
LWS49
force-pushed
the
lws49/feat-marketplace-pr7c-versioning-admin
branch
from
July 29, 2026 13:19
15ec00a to
62905b8
Compare
LWS49
force-pushed
the
lws49/feat-marketplace-pr7b-versioning-course-surfaces
branch
from
July 29, 2026 13:32
d959743 to
8fe805e
Compare
LWS49
force-pushed
the
lws49/feat-marketplace-pr7c-versioning-admin
branch
from
July 29, 2026 13:32
62905b8 to
341976b
Compare
LWS49
force-pushed
the
lws49/feat-marketplace-pr7b-versioning-course-surfaces
branch
from
July 30, 2026 02:10
8fe805e to
c3cedff
Compare
LWS49
force-pushed
the
lws49/feat-marketplace-pr7c-versioning-admin
branch
from
July 30, 2026 02:16
341976b to
1988955
Compare
LWS49
force-pushed
the
lws49/feat-marketplace-pr7b-versioning-course-surfaces
branch
from
July 30, 2026 02:51
c3cedff to
8ff2acd
Compare
LWS49
force-pushed
the
lws49/feat-marketplace-pr7c-versioning-admin
branch
from
July 30, 2026 02:51
1988955 to
6d215d1
Compare
LWS49
force-pushed
the
lws49/feat-marketplace-pr7b-versioning-course-surfaces
branch
from
July 30, 2026 02:58
8ff2acd to
621c34d
Compare
Every listing across instances, with its version history, adoption list and source provenance, plus the actions only an admin has: unlist, re-list, rebuild the authoring copy, and permanently delete a listing that is off the marketplace. The whole read path runs tenant-free — listings span instances while their snapshots live in the preview container — and links to a source assessment are absolute, since a course id only resolves on its own instance's host.
The listings table, its per-listing page, and the two maintenance buttons. Actions sit in one fixed order so a given action always occupies the same slot, and delete is present on every row — disabled until the listing is unlisted — so its tooltip can state the rule rather than leaving the admin to infer it from a missing icon.
LWS49
force-pushed
the
lws49/feat-marketplace-pr7c-versioning-admin
branch
from
July 30, 2026 03:35
6d215d1 to
aef1eb3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Gives system admins somewhere to manage marketplace listings. One table covers every listing across every instance, showing the version currently being served, how many courses adopted it, where it came from, and whether its source still exists. A per-listing page adds the full version history and adoption list.
It also adds the maintenance actions only an admin has: take a listing off the marketplace or put it back, rebuild the authoring copy of a listing that lost its source, and permanently delete a listing that is off the marketplace. Before this there was no way to reach an orphaned listing at all, because the only unlist action hung off the authoring assessment that an orphaned listing no longer has.
Split into two commits along the backend/frontend seam so the API and the UI can each be reviewed on their own.
Design decisions
The admin unlist/re-list duplicates the course-side action rather than reusing it. The course-side one resolves the listing through its authoring assessment, so the listings an admin most often needs to pull are exactly the ones that path cannot reach. Re-listing also does not route through the publish service, because that needs the authoring assessment too and because an unlist/re-list round trip must not mint a version nobody published.
publishedis the only writable attribute on a listing. Everything else is provenance or derived state, so there is nothing else an admin could legitimately edit here.Delete appears on every row, disabled rather than absent while the listing is still published, so its tooltip can state the rule - unlist first, keeping the reversible step ahead of the irreversible one. A missing icon read as "this table cannot delete" and left nowhere to learn why. Adoption count gates nothing: deleting an adopted listing is allowed, and the confirmation dialog is where that consequence is surfaced.
The column naming distinguishes the assessment a listing was first published from ("Original assessment") from the one it is published from now, which differ after a rebuild. The Actions link owns the live one; the column owns the historical one, which is what makes a struck-through cell literally true rather than a claim that the listing is broken.
Links to a source assessment are absolute and carry the source instance's own host and port. A course id only resolves on its own instance's host, so a relative path would 404 for every listing published from another instance, and naming the port explicitly is required because a controller always supplies the port the request reached Rails on, which differs from the public one behind a proxy.
The admin payload carries no source-date range. A "Taught" column was the only consumer and was dropped during the admin-table UX round, so the fields were captured, serialized and typed while being rendered nowhere.
Regression prevention
Covers: the index and show payloads including version history, adoption list, provenance and derived state; authorization sitting behind
:manage, :allrather than an ability on the listing, since CanCan's:managewildcard would otherwise let a course manager through; unlist and re-list, and that re-listing does not create a version; delete accepted only for a listing off the marketplace and rejected for a published one; restore-authoring offered only for an orphaned listing that still has a snapshot to copy from; cross-instance link construction, including the regression where the request's port silently replaced the instance's; and the admin course list gaining its marketplace column.Frontend suites cover the table's derived cells and empty states, which actions each listing state offers, the disabled-delete tooltip, both maintenance buttons including their confirmation flows, and the per-listing page's version and adoption rendering.
Manually verified: the listings table shows served version, adoption count, provenance and the Original assessment column; unlisting enables delete and re-listing works; deleting an unlisted or orphaned listing purges it; rebuilding an orphaned listing's source produces a copy in the container; and the source assessment link opens on the correct host for a cross-instance listing.
New surface, so nothing existing changes for admins beyond the added nav entry.