Skip to content

feat(marketplace): destination tab picker and completion feedback - #8487

Open
LWS49 wants to merge 11 commits into
lws49/feat-marketplace-pr5-previewfrom
lws49/feat-marketplace-pr5b-misc
Open

feat(marketplace): destination tab picker and completion feedback#8487
LWS49 wants to merge 11 commits into
lws49/feat-marketplace-pr5-previewfrom
lws49/feat-marketplace-pr5b-misc

Conversation

@LWS49

@LWS49 LWS49 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

This rounds out the preview/duplicate flow from the parent PR. The duplicate confirmation dialog now lets the user actively pick the destination category/tab via a new DestinationTabPicker, instead of just displaying whichever tab they happened to arrive from. Completion feedback is upgraded to report once the duplication job actually finishes, with a link to where the assessment landed, instead of a fire-and-forget "started" toast. The listing preview page is badged so it's never mistaken for the real assessment it mirrors, and a bare /marketplace/listings now redirects to the marketplace index instead of 404ing.

Design decisions

  • DuplicateConfirmation now takes the full destinationTabs list and an initialDestinationTabId, letting the user change the destination tab instead of being locked into the one they arrived from; the initial selection still defaults to the from_tab tab when it names a real tab, falling back to the course's first tab otherwise.
  • The completion toast now fires from pollJob's completion callback and links to the duplicated assessment (redirectUrl), since the previous "Duplicating assessment started." toast fired on job submission and gave no way to find the result afterward.
  • The selected tab is only re-seeded when the dialog reopens ([open] as the sole effect dependency), not on every parent re-render, so an in-progress tab choice isn't reset out from under the user while the dialog stays open.

Regression prevention

Tests cover: DestinationTabPicker's category grouping (including tabs arriving non-contiguously) and its selection wiring; the tab-selection reset-on-reopen vs. preserve-while-open behaviour; the completion and failure toast copy and the redirect link; the dialog locking/unlocking around an in-flight job so it can't be double-submitted or abandoned mid-job; the destinationTabs now returned from the listing show endpoint; the /marketplace/listings redirect; and the listing preview page's "Preview" badge.

Manual testing covered picking a different destination tab before confirming, the completion toast and its link after a duplication job finishes, a failed duplication leaving the dialog open for retry, and the bare /marketplace/listings redirect.

@LWS49
LWS49 force-pushed the lws49/feat-marketplace-pr5-preview branch 2 times, most recently from a31b07a to 85ab1fc Compare July 17, 2026 07:14
@LWS49
LWS49 force-pushed the lws49/feat-marketplace-pr5b-misc branch from 2fce896 to a3e493d Compare July 17, 2026 07:27
@LWS49 LWS49 changed the title Lws49/feat marketplace pr5b misc feat(marketplace): destination tab picker and completion feedback Jul 17, 2026
LWS49 added 8 commits July 17, 2026 16:03
- add Listing and Adoption models under Course::Assessment::Marketplace
  namespace, with course_assessment_marketplace_ prefix on both tables
- Listing tracks published state and publisher, with a uniqueness
  constraint per assessment and an adoption_count helper
- Adoption links a listing to a destination course and duplicated
  assessment, one adoption per duplicated assessment
- wire has_one :marketplace_listing onto Course::Assessment
- add AssessmentMarketplaceAbilityComponent: admins can publish listings,
  course managers/owners can access, duplicate, and preview published
  listings
- add publish/remove listing endpoints (admin-gated create/destroy)
- expose canPublishToMarketplace + listing state on assessment show DTO
- add Publish/Remove to Marketplace button on the assessment header
- warn in the delete Prompt when a listed assessment is removed
- add MarketplaceAPI client, translations, and controller/FE specs
- add cross-instance listings index (published only, live counts)
- add browse page with title search, adoptions/newest sort, row select
- add sidebar admin entry + /courses/:id/marketplace route
- add "Import Assessments" button on assessments index (from_tab)
- add FE api/operations/types, controller + component specs
- add DuplicationJob: copies listings into a course tab, writes adoption
- add bulk duplicate endpoint enqueuing the job for selected listings
- add DuplicateConfirmation modal with row + bulk triggers, job polling
- add MarketplaceAPI.duplicate and duplicateListings poll operation
- serialize and assert live distinct-course adoption count in index
Add the read-only backend for the marketplace browse flow:

- listings#show serializes a curated, read-only view of a published
  assessment (config + per-question summaries) for the listing preview.
- questions#show serializes a single question's detail, dispatching to
  per-type detail partials (multiple/text/voice/forum/programming/
  rubric/scribing) so each renderer gets exactly the data it needs.
- The listings index gains destination tabs plus preview/duplicate URLs
  so the browse table can link into the flow and target a tab.

Type labels are serialized human-readable (question_type_readable) to
match the real assessment show page, while the demodulized discriminator
is kept for frontend renderer dispatch. The base controller pulls in
AssessmentsHelper so the preview views can reuse display_graded_test_types,
and the sidebar component now uses the :marketplace (storefront) icon.
…port

Extract the assessment/tab/question tree from AssessmentsListing into a
reusable DuplicationAssessmentTree component so both the duplication page
and the marketplace duplicate dialog render an identical tree. The old
DuplicateItemsConfirmation listing is rewired onto it.

Also add the shared table primitives the marketplace index needs:

- renderEmpty flows through TableTemplate -> Body -> MuiTable so a table
  can render a custom empty state when it has no rows.
- hideSelectAll drops the select-all header checkbox while keeping the
  per-row checkboxes.
- Register the storefront icon in COURSE_COMPONENT_ICONS.
Build the read-only browse experience on top of the preview endpoints:

- Marketplace index: single-toolbar table with pagination, empty states,
  hidden select-all, and links into the listing preview / duplicate flow.
- Listing preview page: read-only assessment config, per-question cards
  (type chip, staff-only notes, expandable options) and a Duplicate
  Assessment action.
- Question detail preview: header chip plus a renderer dispatcher with a
  renderer per question type (multiple/text/voice/forum/programming/
  rubric/scribing).
- Duplicate dialog now shows the destination course and the shared
  assessment tree.

Includes the api client, operations, types, translations and locale
strings backing the above.
Thread the origin assessment tab (from_tab) through the whole browse flow (index -> listing -> question preview and back) via withFromTab helpers,
so a duplication always imports into the tab the user started from no matter how they navigate. Add the route data handles that build the
marketplace / listing / question breadcrumbs, preserving from_tab on the crumb links.
@LWS49
LWS49 force-pushed the lws49/feat-marketplace-pr5-preview branch from 85ab1fc to a7086e3 Compare July 17, 2026 08:11
@LWS49
LWS49 force-pushed the lws49/feat-marketplace-pr5b-misc branch from a3e493d to 3bc206d Compare July 17, 2026 08:12
LWS49 added 3 commits July 17, 2026 16:13
Replace the static destination summary with an in-dialog tab picker and
report duplication results honestly:

- Add DestinationTabPicker, a radio tree grouping the current course's
  tabs by category, so the duplicator chooses the destination tab inside
  the dialog instead of it being fixed by the launching `from_tab`.
  The selection seeds from `from_tab` (falling back to the first tab)
  and re-seeds on each reopen, but a parent re-render never resets a
  choice mid-decision.
- Serve `destinationTabs` from the listing show endpoint too, so the
  picker is available when duplicating from the listing detail page,
  not just the marketplace index.
- Restyle the dialog: vertically stacked tabs with larger category/tab
  text, a dense TypeBadge variant, an explicit "Duplicating" heading,
  the ⊘ "arrives unpublished" hint, and explicit cancel/primary colors.
- Report a *completed* duplication (the toast fires from pollJob's
  completion callback, not on submit) and link to where the copy landed
  via the job's redirectUrl; reword the failure copy to plain language.
  Widen the shared toast Toaster type to ReactNode so the toast can
  carry that link (type-only change, no runtime effect).
`marketplace/listings` with no listing id matched no route and 404'd.
Add a redirect so it lands on the same page as `marketplace/`.
Add a "Preview" chip beside the title on the read-only listing detail
page, so it is never mistaken for the real assessment it mirrors.
@LWS49
LWS49 force-pushed the lws49/feat-marketplace-pr5b-misc branch from 3bc206d to 909976b Compare July 17, 2026 08:14
@LWS49
LWS49 marked this pull request as ready for review July 17, 2026 08:14
@LWS49
LWS49 force-pushed the lws49/feat-marketplace-pr5-preview branch 2 times, most recently from e04d41c to 5751e1b Compare July 29, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant