Skip to content

feat(categories): migrate Categories portlet from Dojo to Angular#35146

Merged
hmoreras merged 36 commits intomainfrom
issue-34731-category-portlet
Apr 9, 2026
Merged

feat(categories): migrate Categories portlet from Dojo to Angular#35146
hmoreras merged 36 commits intomainfrom
issue-34731-category-portlet

Conversation

@hmoreras
Copy link
Copy Markdown
Member

@hmoreras hmoreras commented Mar 27, 2026

Summary

This PR migrates the Categories portlet from the legacy Dojo/Java-server implementation to a modern Angular portlet using PrimeNG and NgRx Signals. It is extracted from the broader PrimeNG migration branch (`dot-categories`) and rebased on top of `main`.

Closes #34731

What changed

Frontend — New portlet

  • New portlet at `libs/portlets/dot-categories/` replacing the old implementation in `apps/dotcms-ui/src/app/portlets/dot-categories/`
  • Service moved to `libs/data-access/src/lib/dot-categories/` and registered in the shared data-access index

Features implemented

  • TreeTable-style list with pagination, sorting, and search/filter with debounce (search disabled when list is empty with no active filter)
  • Breadcrumb navigation — click a row to drill into children (including childless categories); breadcrumbs update in-memory state
  • Empty state (full-height, centered icon + message, matching tags portlet pattern)
  • Create/Edit dialog (single component, two modes via `DynamicDialogConfig.data`) — form fields: name (required), variable (readonly, auto-generated in camelCase), key, keywords; success toast shown on create/update
  • Inline sort order editing — per-row `p-inputNumber`, saves on blur/Enter with optimistic update (reverts on error, no table reload)
  • Context menu per row (Edit, Permissions, Delete, separator, Add to Bundle) — opens at cursor position via `p-contextMenu`, visible only on hover
  • Toolbar ellipsis menu with Add-to-Bundle option
  • Bulk delete with confirmation dialog (UX-polished: no button icons, full-bleed info message)
  • CSV import via drag-and-drop upload dialog (merge/replace mode selection, flat-import info tooltip, result toast showing success/warning based on API response)
  • CSV export (with loading spinner)
  • Permissions dialog — new generic `DotPermissionsIframeDialogComponent` in `libs/ui/` that loads the legacy JSP permissions page in an iframe; wired to the context menu; also refactors the edit-content sidebar permissions to reuse this component
  • All user-facing text uses i18n keys via `DotMessagePipe` (32 keys added)

Backend wiring

  • `portlet.xml`: switched `categories` entry from `StrutsPortlet` → `PortletController (/categories)`
  • Added `categories-legacy` portlet entry preserving the old Struts/Dojo view (displayed as Categories Legacy in the menu)
  • Added `CATEGORIES_LEGACY` enum to `PortletID`
  • Added `Language.properties` label for `categories-legacy` ("Categories Legacy")
  • Added `MenuGuard` + `reuseRoute: false` to the Angular categories route
  • Added `categories/permissions.jsp` thin wrapper to load the category and render the permissions tab
  • Updated `SerializationHelperTest` for new portlet count (51)

Tests

  • Store, list component, create/edit dialog, and import component
  • Tests for `updateSortOrder`: optimistic no-reload, correct params, success toast, error revert
  • Component tests for `onSortOrderInput`/`onSortOrderBlur` handlers

Checklist

  • Tests
  • Translations
  • Security Implications Contemplated (add notes if applicable)

This PR fixes: #34731

fmontes and others added 17 commits March 27, 2026 11:31
Replace the legacy categories portlet in dotcms-ui with a new standalone
portlet library following the dot-tags canonical pattern. Includes API
service, SignalStore with breadcrumb navigation, CRUD dialog, and full
test coverage (52 tests).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Show only 4 fields: name (required), variable (readonly, auto-generated
  from name in camelCase), key, and keywords (textarea)
- Variable field is always visible and auto-generates on create mode
- Remove sortOrder, active, description fields from the dialog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Angular Router query params (?inode=xxx) as source of truth for
category navigation. Breadcrumbs are rebuilt from the API parentList
field on each load. Row click now navigates to children while a
dedicated edit icon button opens the edit dialog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…te form

Remove childrenCount guard so users can navigate into any category to
create children. Add parentName to store state and URL query params so
the create dialog shows an info hint indicating the parent category.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the children API returns an empty list, keep the existing
breadcrumbs from the store instead of wiping them out.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The API parentList may not include the current parent when navigating
deeper. Append the current parent from query params when the last
parentList item does not match the current parentInode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop Router/ActivatedRoute query param sync and parentList API param.
Breadcrumbs are now built purely from navigation steps in the store:
navigateToChildren pushes, navigateToBreadcrumb truncates. This avoids
issues with the API parentList not matching the full navigation depth.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a row-level popup menu with ellipsis icon containing edit,
permissions, and delete actions. Edit opens the existing edit dialog,
delete confirms and removes a single category, permissions opens an
empty placeholder dialog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace per-row p-menu with a single shared menu appended to body.
Menu items are set imperatively on button click before toggling,
avoiding the change detection issue where template-bound items were
recreated on every cycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use group/group-hover Tailwind pattern to show the ellipsis button
only when hovering the row, matching the content-drive portlet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CSS grid layout to the list component host so toolbar, breadcrumb,
and table fill the available height. Enable scrollable flex mode on the
table so it scrolls within its container.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Passing null to DialogService.open causes NG0919. Created a minimal
DotCategoriesPermissionsComponent as a placeholder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The separate file import caused a runtime circular dependency error.
Define the placeholder component in the same file as the list component
to avoid the import resolution issue.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CSV export and import capabilities to the categories service, store,
and list component. Export triggers a blob download; import uses a dialog
with file upload and merge/replace mode selection. The Add button is now
a PrimeNG SplitButton with Import as a dropdown option.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hide FileUpload header via PT, use large download icon in bg-primary-50
circle, bold drag-drop text, and info-circle icon on CSV subtitle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the Area : Frontend PR changes Angular/TypeScript frontend code label Mar 27, 2026
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 27, 2026

Claude finished @hmoreras's task in 6m 52s —— View job


Claude finished @hmoreras's task in 1m 21s —— View job


I'll analyze this and get back to you.

@hmoreras hmoreras mentioned this pull request Mar 27, 2026
3 tasks
… order editing

- Add 32 i18n keys for all category UI labels (toolbar, table, empty state, dialogs, toasts)
- Replace right-click p-menu with p-contextMenu so it opens at cursor position
- Add row hover state and pointer cursor matching other portlets (content-drive pattern)
- Add breadcrumb cursor-pointer via PrimeNG passthrough API
- Add empty state matching tags portlet (full-height, centered icon + message)
- Add p-inputNumber per row for inline sort order editing (save on blur/Enter)
- Optimistic sort order update avoids table reload/flickering; reverts on error
- Show success toast after sort order save via MessageService at shell level
- Fix toast: move MessageService provider + p-toast to shell component so it's visible
- Fix children count lost after sort update: use optimistic patch instead of _sort response
- Add updateSortOrder() to DotCategoriesService (PUT /api/v1/categories/_sort)
- Center-align Children, Sort Order and Actions columns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the Area : Backend PR changes Java/Maven backend code label Mar 27, 2026
hmoreras and others added 5 commits March 27, 2026 16:03
…ve filter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add MessageService and DotMessageService providers to store spec (required
  after adding them to the store's withMethods injections)
- Add updateSortOrder mock to DotCategoriesService in store and component specs
- Add 4 new component tests for onSortOrderInput/Blur handlers
- Add 4 new store tests for updateSortOrder: no-reload (optimistic), correct
  params, success toast, and error revert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…egacy to Category Legacy #34731

- Switch portlet.xml 'categories' entry from StrutsPortlet to PortletController (/categories)
- Add 'categories-legacy' portlet entry preserving the old Struts/Dojo view
- Add CATEGORIES_LEGACY enum to PortletID
- Add Language.properties label for categories-legacy
- Add MenuGuard and reuseRoute:false to the categories Angular route
- Update SerializationHelperTest for new portlet count (51) and updated assertions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@hmoreras hmoreras requested review from nicobytes and oidacra April 7, 2026 21:20
@hmoreras hmoreras requested a review from rjvelazco April 7, 2026 21:38
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 8, 2026

Rollback Safety Analysis Complete (Re-analysis)

Analyzed all 13 rollback-unsafe categories against the incremental diff (5e1128c...6e9deac).

Verdict: ✅ Safe To Rollback

All changes are additive frontend/configuration changes — no database migrations, no ES mapping changes, no data model changes.

Label applied: AI: Safe To Rollback

View job run

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the Categories portlet from the legacy Dojo/Struts implementation to a new Angular/PrimeNG portlet, while preserving the old portlet as Categories Legacy and introducing a reusable UI dialog for rendering legacy permissions pages in an iframe.

Changes:

  • Added a new Angular Categories portlet library (core-web/libs/portlets/dot-categories/) powered by NgRx Signals, with CRUD, sorting, import/export, breadcrumbs, and bulk actions.
  • Introduced a generic DotPermissionsIframeDialogComponent (and refactored edit-content sidebar permissions to reuse it) to display legacy permissions JSPs in an iframe.
  • Updated backend portlet registration (portlet.xml, PortletID, Language.properties) and adjusted the serialization test for the new portlet count.

Reviewed changes

Copilot reviewed 57 out of 58 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
dotCMS/src/test/java/com/dotmarketing/business/portal/SerializationHelperTest.java Updates portlet count assertion and verifies both categories and categories-legacy portlets deserialize with expected configuration.
dotCMS/src/main/webapp/WEB-INF/portlet.xml Switches existing categories to categories-legacy and adds a new Spring PortletController entry for Angular /categories.
dotCMS/src/main/webapp/WEB-INF/messages/Language.properties Adds portlet title for categories-legacy, adds categories i18n keys, and adds a new dialog empty-state message key.
dotCMS/src/main/webapp/html/portlet/ext/categories/permissions.jsp Adds a thin legacy JSP wrapper to load a category and render the permissions tab include.
dotCMS/src/main/java/com/dotmarketing/util/PortletID.java Adds CATEGORIES_LEGACY("categories-legacy") enum value for the preserved legacy portlet.
core-web/tsconfig.base.json Adds TS path alias for the new dot-categories portlet package entry point.
core-web/libs/ui/src/lib/components/dot-permissions-iframe-dialog/dot-permissions-iframe-dialog.component.ts Introduces a standalone dialog component to render a permissions page URL inside an iframe.
core-web/libs/ui/src/lib/components/dot-permissions-iframe-dialog/dot-permissions-iframe-dialog.component.spec.ts Adds unit tests for iframe rendering, minHeight handling, and empty-state behavior.
core-web/libs/ui/src/lib/components/add-to-bundle/dot-add-to-bundle.component.html Disables dragging for the Add-to-Bundle dialog.
core-web/libs/ui/src/index.ts Exports the new permissions iframe dialog component from @dotcms/ui.
core-web/libs/portlets/dot-categories/tsconfig.spec.json Adds Jest tsconfig for the new portlet library.
core-web/libs/portlets/dot-categories/tsconfig.lib.json Adds build tsconfig for the new portlet library.
core-web/libs/portlets/dot-categories/tsconfig.json Adds base TS/Angular compiler configuration for the new portlet library.
core-web/libs/portlets/dot-categories/src/test-setup.ts Adds jest-preset-angular Zone test environment setup for the new portlet library.
core-web/libs/portlets/dot-categories/src/lib/lib.routes.ts Defines route(s) for the new Categories portlet shell.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-shell/dot-categories-shell.component.ts Adds a shell component providing toast/message service scope for the portlet.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/store/dot-categories-list.store.ts Implements the NgRx Signals store (load, filter, pagination/sort, breadcrumbs, CRUD, import/export, optimistic sort-order updates).
core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/store/dot-categories-list.store.spec.ts Adds tests for store behaviors including optimistic sort-order updates and error handling.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/dot-categories-list.component.ts Adds the main Categories list component wiring UI actions to the signals store and dialogs.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/dot-categories-list.component.spec.ts Adds component tests for search debounce, lazy-load paging/sort, dialogs, and sort-order input handlers.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/dot-categories-list.component.html Adds the PrimeNG table + toolbar UI for listing, filtering, selecting, and editing categories.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-import/dot-categories-import.component.ts Adds a dialog component for CSV import (merge/replace), drag/drop upload, and inline error display.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-import/dot-categories-import.component.spec.ts Adds unit tests covering import params, success close behavior, and error message rendering.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-import/dot-categories-import.component.html Adds the import dialog UI (file selection, tooltip, merge/replace options, actions).
core-web/libs/portlets/dot-categories/src/lib/dot-categories-create/dot-categories-create.component.ts Adds create/edit dialog component with reactive form and auto-generated camelCase variable name in create mode.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-create/dot-categories-create.component.spec.ts Adds tests for create mode, create-with-parent hint, and edit mode behavior.
core-web/libs/portlets/dot-categories/src/lib/dot-categories-create/dot-categories-create.component.html Adds the form UI for create/edit category dialog.
core-web/libs/portlets/dot-categories/src/index.ts Exports the new portlet routes from the library entry point.
core-web/libs/portlets/dot-categories/project.json Registers the Nx library project with lint and Jest targets.
core-web/libs/portlets/dot-categories/jest.config.ts Adds Jest config for the new portlet library.
core-web/libs/portlets/dot-categories/.eslintrc.json Adds ESLint config for the new portlet library.
core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/dot-edit-content-sidebar.component.spec.ts Updates dialog ref mocking to use a Subject for onClose.
core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/dot-edit-content-sidebar-permissions.component.ts Refactors permissions dialog to reuse the new generic iframe dialog; updates lifecycle cleanup logic.
core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/dot-edit-content-sidebar-permissions.component.spec.ts Updates tests to assert the new dialog component and URL building behavior.
core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/components/permissions-dialog/permissions-dialog.component.ts Removes the now-replaced dedicated permissions dialog component.
core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/components/permissions-dialog/permissions-dialog.component.spec.ts Removes tests for the deleted dedicated permissions dialog component.
core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/components/permissions-dialog/permissions-dialog.component.html Removes template for the deleted dedicated permissions dialog component.
core-web/libs/data-access/src/lib/dot-categories/dot-categories.service.ts Adds a shared data-access service for categories API calls (pagination, CRUD, import/export, sort order).
core-web/libs/data-access/src/lib/dot-categories/dot-categories.service.spec.ts Adds HTTP unit tests for the new categories service, including export/import behaviors.
core-web/libs/data-access/src/index.ts Exports the new DotCategoriesService from the shared data-access barrel.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories.routes.ts Removes old app-local categories routing in favor of the new library portlet.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.ts Removes the old categories permissions component (replaced by generic iframe dialog).
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.spec.ts Removes tests for the deleted old categories permissions component.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.html Removes template for the deleted old categories permissions component.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/store/dot-categories-list-store.ts Removes the old ComponentStore-based categories store.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.ts Removes the old categories list component implementation.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.spec.ts Removes old (disabled) test suite for the previous categories list component.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.scss Removes SCSS for the deleted old categories list component.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.html Removes template for the deleted old categories list component.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/store/dot-categories-create-edit.store.ts Removes old create/edit state store (no longer needed with new dialog-based UX).
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.ts Removes old create/edit component wrapper UI (tabs-based).
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.spec.ts Removes tests for the deleted old create/edit component.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.scss Removes styles for the deleted old create/edit component.
core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.html Removes template for the deleted old create/edit component.
core-web/apps/dotcms-ui/src/app/app.routes.ts Updates Categories route to use MenuGuard + reuseRoute: false and lazy-load the new portlet library routes.
core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.ts Removes old app-local PaginatorService-based categories service in favor of shared data-access service.
core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.spec.ts Removes tests for the deleted old app-local categories service.

Comment thread core-web/libs/data-access/src/lib/dot-categories/dot-categories.service.ts Outdated
- remove redundant standalone:true from all 5 new components (Angular 19+)
- add $ prefix to all signals (selectedFile, importing, errorMessage, rowMenu, toolbarMenu, ptConfig, iframeSrc, minHeight)
- convert private keyword to # ECMAScript private fields (except viewChild which requires public access)
- use FileUpload type from primeng/fileupload instead of structural type
- fix unsafe spread + cast in openEditDialog: use typed variable instead of 'as'
- add URL whitelist to DotPermissionsIframeDialogComponent: reject absolute, protocol-relative, and javascript: URLs
- fix null assertion on response.body in exportCategories
- replace inline style= attributes with Tailwind utility classes
- remove $any() cast in category row actions button
- add generic <T> to handleCategoryAction in store
- add i18n key categories.more.actions to Language.properties
- add error path tests for all major service operations
@hmoreras hmoreras added this pull request to the merge queue Apr 9, 2026
Merged via the queue into main with commit 19aaed7 Apr 9, 2026
50 checks passed
@hmoreras hmoreras deleted the issue-34731-category-portlet branch April 9, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback 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.

Dojo to Angular: Categories Portlet

4 participants