Skip to content

feat(tools): Angular Tools portlet (Beta) with dialogs, drag-drop and mocked backend - #37481

Draft
AP2300 wants to merge 7 commits into
mainfrom
issue-37354-tools-portlet-modernization
Draft

feat(tools): Angular Tools portlet (Beta) with dialogs, drag-drop and mocked backend#37481
AP2300 wants to merge 7 commits into
mainfrom
issue-37354-tools-portlet-modernization

Conversation

@AP2300

@AP2300 AP2300 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Builds the Tools Beta portlet under Settings — a two-column master/detail where an admin shapes the backend navigation by managing sections, choosing tools in each one via checkbox, ordering both by drag, and creating custom content tools. Ships as a route (/#/c/tools-beta) with MenuGuardService gating, opt-in through Add Portlet.

Everything on the frontend is done and rendering against mocked service methods that mirror the endpoint contract pinned in #37353. When those endpoints land, the swap is a service body edit — no changes to the store, dialogs, drag-and-drop, confirmations, or models.

Draft because the backend wiring for the real write path (endpoints, PortletID.TOOLS gate) is in-flight on #37353 and open questions remain in the spike #37352. Reachable via Add Portlet as of this PR.

Commits

  1. feat(tools): register tools-beta backend portletportlet.xml entry + Language.properties title + SerializationHelperTest assertion, mirroring users-beta / roles-beta.
  2. feat(tools): scaffold Angular Tools portlet with dialogs, drag-drop and PrimeNG UIlibs/portlets/dot-tools/ (shell → page → store), section dialog, tool dialog, CDK drag with custom preview/placeholder templates, p-confirmDialog with count-driven copy, DotToolsService with WAITING FOR BACKEND stubs.
  3. i18n(tools): move every user-facing string to Language.properties — ~50 tools.* keys wired through DotMessagePipe and DotMessageService.
  4. feat(tools): loading, empty, error and no-selection states + aria labelsshowLoading, showError, showEmptySections computeds on the store; four distinct left-panel states plus right-panel "select a section"; aria labels on every icon-only button and drag handle.
  5. test(tools): store + dialog specs — 35 tests covering computeds, mutations, forms — store 96%, dialogs 93–100%.

Backend blockers (not in this PR)

Every mock in DotToolsService maps 1:1 to an endpoint in #37353. Swap point is the service body only.

FE method (mocked) Endpoint per #37353
getSections GET /v1/layouts
getCatalog GET /v1/portlet
createSection / updateSection / deleteSection / reorderSections POST / PUT / DELETE / PUT _reorder under /v1/layouts
setSectionTools PUT /v1/layouts/{id}/portlets
createCustomTool / updateCustomTool existing POST / PUT /v1/portlet/custom
deleteCustomTool pending path decision — see comment on #37352

Two additive requests posted on #37353:

  • isCustom: boolean on GET /v1/portlet rows (already consumed by the client to gate the row menu on the Available Tools panel).
  • GET /v1/portlet/custom/{portletId} for tool-edit prefill (component already accepts a prefill; today it falls back to tool.title / tool.id from the catalog).

Test plan

  • Build a fresh dotCMS image and confirm "Tools (Beta)" appears in Add Portlet.
  • Add the portlet to a layout, navigate to /#/c/tools-beta, confirm the two-column layout renders.
  • Left panel: create, rename, delete a section; drag to reorder; verify per-row overflow menu; verify empty state after deleting all sections.
  • Right panel: check/uncheck a tool → appears/disappears under the selected section on the left; drag a tool inside a section; use the row-menu Edit/Delete on a custom tool (menu should not appear on first-party rows).
  • Dialogs: submit an empty section dialog → warning + red name outline; submit valid → dialog closes and section appears; same for the tool dialog with auto-slug id behavior.
  • Delete confirmations show the count-driven copy: "The section and its N tool assignments…" and "The tool will be deleted and removed from N sections…".
  • Load more: with the mocked catalog (40 rows), the "Load 40 more" button appears at row 16 and pulls the remainder on click.
  • i18n: every visible string is a tools.* key — swap Language.properties to a translated version and confirm nothing hardcodes.

Related

Refs #37354

🤖 Generated with Claude Code

AP2300 and others added 5 commits September 9, 2026 15:26
Adds the three pieces the beta pattern needs so the Angular Tools portlet
becomes reachable through Add Portlet: portlet.xml entry, Language.properties
title, and the SerializationHelperTest assertion. Mirrors the users-beta and
roles-beta registrations.

No PortletID enum entry yet — that lands with #37353 when the write endpoints
gate on requiredPortlet("tools").

Refs #37354

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nd PrimeNG UI

Adds the Beta Tools portlet under Settings — a two-column master/detail where
an admin shapes the backend navigation by managing sections, choosing tools in
each one via checkbox, ordering both by drag, and creating custom content
tools.

Structure follows the dot-roles / dot-users convention (shell → page → store)
and the reference conventions in libs/portlets/CLAUDE.md.

Included:
  * Sections panel with CDK drag reorder, hover-revealed overflow menu, and
    inline nested tool list on the selected section
  * Available Tools panel with search, p-checkbox rows, load-more pagination
    (15 initial, +40 per click), and per-row overflow menu on custom tools
  * Section dialog: name + curated Material Symbols icon picker
  * Tool dialog: name + auto-slug id + base/content-type multi-select against
    real DotContentTypeService + List/Card view mode toggle
  * Drag polish via *cdkDragPreview / *cdkDragPlaceholder templates plus
    arbitrary-variant Tailwind on drop lists (no scss, no ::ng-deep)
  * Form submission: submit is always enabled, invalid submit lights up field
    outlines and shows a warning icon + text opposite the submit button
  * p-confirmDialog with count-driven delete copy for section and catalog-tool
    scope
  * DotToolsService with WAITING FOR BACKEND stubs mirroring the endpoint
    contract in #37353; swap point is the service body only
  * DotToolsStore signal store owning all client-side derivations (selection,
    filter, pagination, delete-impact counts, optimistic reorder patches)

Route registered at /c/tools-beta with MenuGuardService gating, matching
users-beta and roles-beta.

Refs #37354

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracts every hardcoded English string in the Tools portlet — labels, form
placeholders, error copy, delete-confirmation bodies, tooltip text — to
tools.* keys in Language.properties and wires them through DotMessagePipe
(templates) and DotMessageService.get() (dialog headers, menu items, confirm
copy).

Two-key pluralization for the delete-confirm counts ("1 tool assignment" vs
"N tool assignments") keeps grammar right without a plural library.

Also pulls the catalog pagination constants into the shared constants file so
the store's slice count, the load-more button label and the tests upcoming
next stay in agreement.

Refs #37354

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds view-state helpers on the store (showLoading, showError,
showEmptySections) so the page template stays a plain reader and never
inlines status checks. The left panel now renders four distinct states
depending on the store: spinner while first-loading, error card with retry
when the load failed, empty-state with a "New Section" call to action when
the store settled with no sections, and the normal drag-sortable list
otherwise. The right panel gets a "Select a section" empty state whenever no
section is chosen and the store is not still loading or errored.

Also fills in the accessibility gaps flagged by the portlets CLAUDE.md:
aria-labels on the section/tool overflow menu triggers, the "remove from
section" button and both drag handles. Icon-only p-buttons use PrimeNG's
[ariaLabel] input; the drag-handle spans use [attr.aria-label].

All new strings are i18n keys under tools.* — no hardcoded English added.

Refs #37354

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tions, forms

DotToolsStore: 23 tests, 96% coverage. Every computed (filter, pagination,
selectedSectionToolIds, toolSectionCount, showLoading / showError /
showEmptySections), every mutation (create/update/delete for sections and
custom tools, toggle/remove/reorder for tools inside a section), and the
error paths that revert the store to `loaded` on a mutation failure.

DotToolsSectionDialogComponent: 5 tests covering create and edit modes —
initial state, submit warning + field error on invalid submit, close with
form value on valid submit, prefill from incoming section.

DotToolsToolDialogComponent: 7 tests across three describe blocks — create
mode (content-type loading, auto-slug id, submit flow), edit mode with
explicit prefill (all fields), edit mode without prefill (catalog fallback
for name and id, matching the interim UX until the GET /v1/portlet/custom/
endpoint lands per #37353).

Refs #37354

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AP2300
AP2300 force-pushed the issue-37354-tools-portlet-modernization branch from acdbacc to 726c37c Compare September 9, 2026 19:30
@github-actions github-actions Bot added Area : Backend PR changes Java/Maven backend code Area : Frontend PR changes Angular/TypeScript frontend code labels Sep 9, 2026
AP2300 and others added 2 commits September 10, 2026 10:11
Previous placeholder / preview were indigo-branded with dashed borders and
shadow-xl, which felt heavier than the rest of the portlet and off-key
against the design's minimal slate palette. Three moves:

  * Placeholder is now a faded ghost of the row itself (same layout, same
    content, opacity 40%, slate-100 tint) so the eye tracks where the item
    will land instead of a highlighted callout.
  * Preview mirrors the row's own styling with a soft lift — slate-200
    border, slate text, shadow-lg — no indigo tint, no rotation, no
    scale-up. "The same row, lifted" instead of a distinct card.
  * Drop list gets a subtle slate-100 tint via the .cdk-drop-list-dragging
    class while a drag is in progress, so the active target reads without
    being noisy. Uses a transition-colors so the tint fades in/out cleanly.

Same slate treatment applied to the nested tool list. All Tailwind, no scss.

Refs #37354

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewires the DnD interaction so it matches the design's intent instead of
CDK's default sort-live-during-drag behavior:

  * cdkDropListSortingDisabled on both drop lists. The original row stays
    visible in place (rendered by the placeholder template at opacity 40)
    and siblings do not shift while dragging.
  * A 2px indigo drop-line renders in the gap the pointer is closest to,
    driven by $sectionDropIndex / $toolDropIndex signals updated on every
    cdkDragMoved. The trailing gap (after the last row) is supported too.
  * The preview is a translucent copy of the row (opacity 70, subtle
    shadow) — no borders, no branding, "the same row, lifted".
  * onSectionDragEnded / onToolDragEnded use the tracked drop index rather
    than CdkDragDrop.currentIndex (which is unreliable when sorting is
    disabled), normalizing the gap index for insertion via a small helper
    that accounts for the source being removed first.

Row and placeholder both carry a data-row attribute so the pointer-position
computation always sees N slots regardless of which one is the source
(placeholder replaces the source li at drag start, keeping its slot).

Refs #37354

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area : Backend PR changes Java/Maven backend code Area : Frontend PR changes Angular/TypeScript frontend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant