Skip to content

feat(admin/plugins): install/update/uninstall UI + verbatim capability review — closes #253 - #385

Merged
tayebmokni merged 1 commit into
mainfrom
feat/253-plugin-admin-ui
May 18, 2026
Merged

feat(admin/plugins): install/update/uninstall UI + verbatim capability review — closes #253#385
tayebmokni merged 1 commit into
mainfrom
feat/253-plugin-admin-ui

Conversation

@tayebmokni

Copy link
Copy Markdown
Contributor

Summary

  • Builds the admin-side plugin management screens at apps/admin/src/app/plugins/: a list view, per-plugin detail view, and the install flow centred on a verbatim capability review that lists every requested permission in plain English with explicit operator consent before the install fires.
  • Adds server actions wrapping /api/v1/plugins/{install,activate,deactivate,:name} with friendly "endpoint not yet deployed" fallbacks for the in-flight REST work (issues fix(ci): vet/test workspace modules only, not transitive deps #340 / feat(auth): login flow + 2FA — closes #124 #341).
  • Mirrors packages/go/plugins/capabilities/registry.go::builtinCapabilityDefs into a TS lookup so the consent UI renders meaningful descriptions ("Send transactional email on behalf of this site") before the host receives the install request. Sensitive caps are visually flagged; unknown ids surface as "Unrecognised" so out-of-band manifests can’t be silently consented to.

File layout

apps/admin/src/app/plugins/
  page.tsx                          RSC list view, defensive fetch
  PluginListClient.tsx              search / filter / row actions / uninstall modal
  actions.ts                        server actions over /api/v1/plugins/*
  types.ts                          TS projection of lifecycle.Plugin
  capability-registry.ts            TS mirror of host cap registry
  [name]/
    page.tsx                        RSC detail fetch
    PluginDetailView.tsx            manifest summary + caps + deps + last-error
  install/
    page.tsx                        install entry
    InstallForm.tsx                 upload / paste flow + capability review
  components/
    CapabilityReview.tsx            the verbatim consent screen
    PluginStatusBadge.tsx           shared lifecycle-state pill

Sidebar gains a /plugins link alongside Users and Settings.

Verification

  • pnpm install --frozen-lockfile=false — clean
  • pnpm --filter @gonext/admin test87 / 87 passing (33 new tests across 6 new test files)
  • pnpm --filter @gonext/admin typecheck — clean
  • pnpm --filter @gonext/admin lint — no errors

Test plan

  • List page renders one row per plugin from the fetched data
  • Empty / filter-empty / fetch-error states render without crashing
  • Detail view renders capabilities as human-readable text in declaration order
  • Dependency status renders satisfied / unsatisfied chips, propagates reason
  • errored plugins surface the last-error block as a role="alert"
  • Install page: file upload (manifest.json) triggers the manifest preview
  • Install page: pasting JSON triggers the manifest preview
  • Capability review: every cap from the manifest appears in declaration order
  • Capability review: sensitive caps get the "Sensitive" tag; unknown ids get "Unrecognised"
  • Capability review: duplicate caps render twice (verbatim, no dedup)
  • Install button is disabled until the consent checkbox is ticked
  • Uninstall opens a confirmation modal; Cancel closes without firing the action
  • Uninstall confirm calls the server action with the right slug
  • Active plugins disable the Uninstall button until deactivated (with title= hint)
  • Errored plugins can be uninstalled (after confirmation)
  • Sidebar test updated for the new /plugins entry

🤖 Generated with Claude Code

@tayebmokni
tayebmokni enabled auto-merge (squash) May 18, 2026 19:31
…closes #253

Wires up the user-facing plugins screen on the admin Next.js app:

  - apps/admin/src/app/plugins/page.tsx — list view (RSC) over
    GET /api/v1/plugins, defensive fetch with empty/error fallback.
  - apps/admin/src/app/plugins/PluginListClient.tsx — interactive table
    with search, status-filter chips, per-row Activate / Deactivate /
    Uninstall actions, and a confirmation modal for the destructive path.
  - apps/admin/src/app/plugins/[name]/page.tsx + PluginDetailView.tsx —
    detail view: manifest summary, declared capabilities with operator-
    voice descriptions, dependency status with satisfied chips, install
    timestamps, last-error block when state=errored.
  - apps/admin/src/app/plugins/install/page.tsx + InstallForm.tsx —
    upload form (bundle, manifest.json, JSON paste) with the verbatim
    CapabilityReview centerpiece. Install action requires an explicit
    consent checkbox.
  - apps/admin/src/app/plugins/actions.ts — server actions wrapping
    /api/v1/plugins/{install,activate,deactivate,delete} with friendly
    "endpoint not deployed yet" fallback for issues #340 / #341.
  - apps/admin/src/app/plugins/components/CapabilityReview.tsx — the
    review screen itself; renders every cap in manifest order, flags
    sensitive ones, surfaces unknown ids as "Unrecognised".
  - apps/admin/src/app/plugins/components/PluginStatusBadge.tsx —
    state pill shared by list + detail.
  - apps/admin/src/app/plugins/capability-registry.ts — TS mirror of
    packages/go/plugins/capabilities/registry.go::builtinCapabilityDefs,
    extended with operator-voice phrasing for the consent UI.

Adds 33 Vitest tests covering list rendering, status filtering, search,
the uninstall confirmation flow (cancel + confirm), the disabled-while-
active uninstall path, the detail capability rendering, the dependency
satisfied/unsatisfied chips, the install file-upload + paste flows, the
consent-gated submit, and the capability registry mirror.

Sidebar gets a /plugins entry alongside Users and Settings; Sidebar.test
is updated to match.

Signed-off-by: Mohamed Tayeb Mokni <tayeb.mokni@gmail.com>
@tayebmokni
tayebmokni force-pushed the feat/253-plugin-admin-ui branch from 4f1cb91 to 52c12d3 Compare May 18, 2026 20:12
@tayebmokni
tayebmokni merged commit 49c0c3a into main May 18, 2026
8 checks passed
@tayebmokni
tayebmokni deleted the feat/253-plugin-admin-ui branch May 18, 2026 20:14
tayebmokni pushed a commit that referenced this pull request May 23, 2026
Adds the public-facing browse/install surface that sits next to the
existing plugins manager (PR #385). The catalogue is fed by the
marketplace data model from PR #396; the install path reuses the
plugin lifecycle.Install runtime so consent semantics stay identical
across manual and catalogue flows.

Backend (apps/api/internal/admin/marketplace):
  - GET    /api/v1/admin/marketplace/listings        (category/q/sort)
  - GET    /api/v1/admin/marketplace/listings/{slug}
  - GET    /api/v1/admin/marketplace/listings/{slug}/versions
  - GET    /api/v1/admin/marketplace/listings/{slug}/ratings
  - POST   /api/v1/admin/marketplace/listings/{slug}/ratings  (1..5 stars)
  - POST   /api/v1/admin/marketplace/listings/{slug}/install
  - Read endpoints require an authenticated principal; install +
    rating POST gated on plugins.install (CapInstallPlugins).
  - Install dispatches to lifecycle.Install with the resolved
    version's wasm bytes via a BundleFetcher abstraction; success
    and failure both append a plugin_install_events row.

Admin UI (apps/admin/src/app/marketplace):
  - page.tsx + MarketplaceClient — grid w/ search, category chips,
    and sort-by recent/stars/popular.
  - [slug]/page.tsx + ListingDetailView — description, version
    history, compat matrix, rating aggregate + submission form.
  - [slug]/install/page.tsx + InstallConfirm — reuses the
    CapabilityReview component from #385 (no duplication) and the
    consent checkbox before firing the install action.
  - components/RatingStars (read-only + interactive variants),
    components/MarketplaceCard.

Tests:
  - Backend: filter/sort, slug detail/404, install dispatch + event
    capture, capability gate, rating range checks.
  - Frontend: grid render, URL push for search/sort/category, capability
    review reuse + consent gating, install success path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Mohamed Tayeb Mokni <tayeb.mokni@gmail.com>
tayebmokni added a commit that referenced this pull request May 23, 2026
## Summary
- Adds `apps/api/internal/admin/marketplace` (handler + tests) exposing
browse, detail, versions, ratings, and install endpoints over the data
model from PR #396. Install dispatches to `lifecycle.Manager.Install`
via a bundle-fetcher seam and records `plugin_install_events` rows.
- Adds `apps/admin/src/app/marketplace/*` (Next.js App Router):
catalogue grid with category chips + search + sort, listing detail with
version history / compat matrix / ratings form, and an install
confirmation screen that **reuses the `CapabilityReview` component from
PR #385** rather than duplicating consent UX.
- Sidebar gets a new `Marketplace` entry next to `Plugins`.

## Test plan
- [x] `cd apps/api && go vet ./internal/admin/marketplace/...`
- [x] `cd apps/api && go test -race -count=1
./internal/admin/marketplace/...` (all green; covers list filters, slug
404, install dispatch + event capture, capability gate on install +
rating, rating range checks)
- [x] `pnpm --filter @gonext/admin typecheck`
- [x] `pnpm --filter @gonext/admin test` (37 files / 255 tests pass —
new tests: `MarketplaceClient.test.tsx`, `RatingStars.test.tsx`,
`InstallConfirm.test.tsx` asserts the shared `CapabilityReview` is
reused)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Mohamed Tayeb Mokni <tayeb.mokni@gmail.com>
Co-authored-by: Mohamed Tayeb Mokni <tayeb.mokni@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
tayebmokni added a commit that referenced this pull request May 26, 2026
…brand (#437)

## Summary

Restyles every screen in the GoNext plugin marketplace admin (PR #420)
and installed-plugins admin (PR #385) to the **"Living systems"**
brand foundation that landed via PR #432. Tokens from
`src/styles/tokens.css` are honoured throughout — no hex literals, no
legacy `--color-accent`. Each page lands the brand's signature
italic-serif accent inside a heavy Archivo headline.

- **`/marketplace` — catalogue.** Headline reads `Marketplace
catalogue.`
  with the italic-accent on the noun, an emerald eyebrow, and a Geist
  lead. Filter chips toggle to `--emerald-soft` when active; sort chips
  swap to a solid `--ink` fill. MarketplaceCard sits on `--paper-2`
  with a paper-3 thumb glyph well, Archivo display title, monospace
  slug, lavender-soft capability chip, and emerald rating stars. Cards
  lift to `--sh-md` on hover via a 2px transform — the marketplace
  moodboard's hover gesture.
- **`/marketplace/[slug]` — listing detail.** Headline pairs the
  Archivo title with an italic-serif accent on the author name.
  Version history is a paper-2 table with mono row IDs + SHA digests;
  the compat matrix lives on `--paper-3` sunken surface to read as
  reference data. Ratings render as a horizontal review carousel of
  cream cards with emerald stars.
- **`/marketplace/[slug]/install` — confirmation.** Headline:
  `Install <name>.` with the listing name in italic-serif. The meta
  strip uses the brand's card chrome with mono version label and
  uppercase eyebrow labels. Install CTA is the emerald brand button.
  Success panel uses the italic-accent pattern with an emerald
  left-border.
- **Shared `CapabilityReview` (also rendered by `/plugins/install`).**
  Each capability row is its own paper-2 card with a 22px circular
  marker. Sensitive caps swap to `--lavender-soft` background + solid
  lavender marker; once the consent box is ticked every non-unknown
  marker flips to an **emerald checkmark** — the row visibly confirms
  it has been reviewed.
- **`/plugins` — installed plugins list.** Headline:
  `Installed plugins.` with the italic-accent on `plugins`. Status
  filter chips and `PluginStatusBadge` share the brand's semantic
  palette: emerald for active (alive), paper-3 / fg-muted for inactive
  (quietly present), lavender-soft for installed, warning-soft for
  pending uninstall, danger-soft for errored. Badge gains a leading
  `currentColor` dot so the row is legible without colour.

## Test plan

- [x] `pnpm --filter @gonext/admin lint` — clean (only pre-existing
  `<img>` warnings in unrelated `media/` files)
- [x] `pnpm --filter @gonext/admin typecheck` — clean
- [x] `pnpm --filter @gonext/admin test:ci` — **349 tests pass**,
  including new snapshot tests for `MarketplaceCard` and every
  `PluginStatusBadge` state
- [x] `pnpm --filter @gonext/admin build` — succeeds, marketplace +
  plugins routes ship under their existing bundle budgets
- [x] Snapshot pins lock the brand contract — future drift will fail
  the snapshot and demand a deliberate update

## Tokens audit

Every colour, type ramp, radius, shadow, motion, and spacing in the
diff resolves to a `var(--*)` from `tokens.css`. The few raw values
that remain are intentional: the modal backdrop scrim is
`rgba(14, 26, 20, 0.55)` (the ink ramp tinted at 55%, no token covers
backdrop overlays) and the icon-well letter sizes are pixel-precise
display measurements.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Mohamed Tayeb Mokni <tayeb.mokni@gmail.com>
Co-authored-by: Mohamed Tayeb Mokni <tayeb.mokni@gmail.com>
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.

2 participants