Skip to content

VPR-59 [5/6] CMS migration: mobile card mode, history diffs, reorder UX, tests#249

Closed
rlorenzo wants to merge 6 commits into
VPR-59-cms-4-hub-uxfrom
VPR-59-cms-5-responsive-tests
Closed

VPR-59 [5/6] CMS migration: mobile card mode, history diffs, reorder UX, tests#249
rlorenzo wants to merge 6 commits into
VPR-59-cms-4-hub-uxfrom
VPR-59-cms-5-responsive-tests

Conversation

@rlorenzo

@rlorenzo rlorenzo commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Part 5 of 6 (stacks on #248 — the diff below shows only this slice).

Scope

  • Mobile card mode for the five dense list tables (ContentBlocks, Files, LeftNavMenus, FileAuditLog, BulkEncrypt) via shared ListCard/ListCardField, with 44px touch targets on coarse pointers scoped to the cards.
  • Content-block edit history page with version diffs (htmldiff-based ins/del markup, sanitized server-side before diffing).
  • Shared SortableList + use-reorder: unified drag/keyboard reorder for left-nav items and link collections, with polite live-region announcements and reduced-motion support.
  • Large frontend + backend test coverage expansion; routable action for the link-collection 201 Location; restore/collection-delete/tag error fixes.
  • QEditor accessible naming, aria-required on the System select, brand-token theming cleanup.

rlorenzo added 6 commits July 2, 2026 14:38
- List pages (Files, Content Blocks, Left Nav, Audit, Bulk Encrypt)
  collapse to stacked cards below the mobile breakpoint instead of
  scrolling wide tables
- a11y: give the rich-text editor an accessible name, mark the System
  select required, and make the Link card keyboard-reachable via its
  anchor
- Align the file and Effort audit-trail filter layouts (outlined
  controls; search and Clear Filters on their own row); this accounts
  for the Effort/AuditList change in a CMS commit
- Replace remaining hard-coded colors with brand tokens and move inline
  dialog widths to shared classes
- Cross-block edit history page (linked from the CMS home) with
  per-version diffs: diff a saved version against its predecessor, or
  diff the editor's current draft against any past version. Diffs use
  htmldiff.net and are re-sanitized server-side so only ins/del markup
  reaches the browser.
- Restrict the public content-block lookup to active blocks so
  soft-deleted content is no longer served to unauthenticated callers.
- Frontend: Vitest/@vue/test-utils suites for CMS utils, components,
  dialogs, selectors, list pages, and the content-history and diff views
- Backend: controller tests for files, content, left-nav, link
  collections, and user photos, plus content-block permission filtering
- Add SanitizeDiff cases proving the diff sanitizer strips XSS while
  keeping the ins/del change markers
PostLinkCollection, CreateLinkCollectionTagCategory, and PostLink passed
a local variable name to CreatedAtAction instead of an action method, so
the 201 Location header URL generation would fail at runtime through
routing. Point each at its existing collection GET action; these
resources have no get-by-id endpoint to target.
…tions

- Extract a shared, accessible SortableList (drag + up/down, "just
  moved" highlight, live-region announcements, reduced-motion fallback)
  to replace the duplicated per-page drag handlers
- Track left-nav menu settings and items as independently dirty/saved
  sections; rename "Revert" to "Discard Item Changes"
- Add a per-row Delete to link-collection links
- Restore endpoints return 204 instead of 200 with an empty body, so the
  client no longer reports a false "failed to restore" (files and blocks)
- Deleting a link collection now removes its links, link tags, and tag
  categories first, fixing a foreign-key 500
- Creating a tag category returns the generated id and validates the id
  and route/body, so the follow-up order save no longer 400s or 500s
- Content-block list filters initialize from and sync to the URL,
  matching the Files list (deep-linkable views)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR advances the CMS migration by adding a content-block edit-history viewer with HTML diffs, introducing shared mobile card-mode rendering for multiple dense QTable lists, and standardizing reorder UX via a reusable sortable list + composable. It also expands both frontend (Vitest) and backend (xUnit) test coverage around the new behaviors and a few API semantics fixes (restore endpoints, CreatedAtAction locations, delete cascade).

Changes:

  • Added server-side content-history diff generation (htmldiff.net) with a dedicated diff sanitizer, plus new CMS API endpoints and a new SPA history page + diff dialog.
  • Introduced shared mobile “card mode” list components (ListCard, ListCardField) and applied them across multiple CMS list pages, with coarse-pointer touch target adjustments.
  • Added shared reorder building blocks (SortableList, use-reorder) with a11y announcements + reduced-motion handling, plus broad unit/integration tests across new CMS components/pages.

Reviewed changes

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

Show a summary per file
File Description
web/Viper.csproj Adds htmldiff.net dependency for server-side HTML diffs.
web/Services/IHtmlSanitizerService.cs Extends sanitizer interface with SanitizeDiff API.
web/Services/HtmlSanitizerService.cs Implements a separate sanitizer policy for diff markup (ins/del).
web/Classes/Utilities/DateRangeHelper.cs Adds shared “inclusive To date” helper for filters.
web/Areas/CMS/Services/CmsNavMenu.cs Adds CMS nav entries (history + audit trail) and adjusts indentation.
web/Areas/CMS/Services/CmsFileAuditService.cs Reuses DateRangeHelper for To-date semantics.
web/Areas/CMS/Services/CmsContentBlockService.cs Adds diff building + cross-block history query/filtering APIs.
web/Areas/CMS/Models/DTOs/ContentBlockDto.cs Adds DTOs for diff view + cross-block history list entries.
web/Areas/CMS/Data/LeftNavMenu.cs Allows IUserHelper injection for testability; uses injected helper.
web/Areas/CMS/Controllers/CMSLinkCollectionLinks.cs Fixes CreatedAtAction target for POST link creation.
web/Areas/CMS/Controllers/CMSLinkCollectionController.cs Uses CmsPermissions consts; improves tag-category DTO handling; adds cascade delete.
web/Areas/CMS/Controllers/CMSFilesController.cs Uses NoContent for restore endpoint success response.
web/Areas/CMS/Controllers/CMSContentController.cs Adds history diff endpoints and cross-block history list endpoint; blocks deleted blocks in public fn route.
VueApp/src/styles/colors.css Adds neutral “surface” tokens for SPA theming consistency.
VueApp/src/styles/base.css Adds shared list-card/layout utility classes and coarse-pointer button sizing.
VueApp/src/Effort/pages/AuditList.vue Aligns filter/search/clear UX layout with CMS patterns.
VueApp/src/composables/use-reorder.ts Adds shared reorder logic + flash highlight state.
VueApp/src/composables/tests/use-reorder.test.ts Unit tests for reorder math, callback, and highlight behavior.
VueApp/src/components/SortableList.vue New shared sortable list component (drag + button reorder + a11y announcements).
VueApp/src/components/tests/sortable-list.test.ts Tests SortableList rendering, move buttons, and live-region announcements.
VueApp/src/CMS/types/index.ts Adds types for content-history diff and audit list payloads.
VueApp/src/CMS/router/routes.ts Adds route for CMS content-block edit-history page.
VueApp/src/CMS/pages/LeftNavMenus.vue Enables QTable grid mode on small screens and adds ListCard rendering.
VueApp/src/CMS/pages/Files.vue Enables QTable grid mode on small screens and adds ListCard rendering.
VueApp/src/CMS/pages/FileAuditLog.vue Standardizes filters (DateRangeFilter) + enables grid mode + ListCard rendering.
VueApp/src/CMS/pages/ContentBlocks.vue URL-synced filters + grid mode + ListCard rendering.
VueApp/src/CMS/pages/ContentBlockHistory.vue New edit-history page with filters, pagination, and diff viewer integration.
VueApp/src/CMS/pages/ContentBlockEdit.vue Adds diff-vs-current UI + QEditor accessible naming + required a11y attributes.
VueApp/src/CMS/pages/CmsHome.vue Expands recent activity gating (includes left-nav menus).
VueApp/src/CMS/pages/BulkEncrypt.vue Enables grid mode + ListCard rendering for small screens.
VueApp/src/CMS/components/RecentActivity.vue Adds left-nav activity source and improves item labeling.
VueApp/src/CMS/components/ModifiedStamp.vue Supports both table-cell and card-mode rendering paths.
VueApp/src/CMS/components/ListCardField.vue New card field row component for grid-mode list cards.
VueApp/src/CMS/components/ListCard.vue New card shell component for QTable grid mode rows.
VueApp/src/CMS/components/LinkCollections.vue Adds debounce to search input.
VueApp/src/CMS/components/Link.vue Simplifies link card interaction and removes unused watch/click-open logic.
VueApp/src/CMS/components/LeftNavMenuDialog.vue Replaces inline dialog card widths with shared CSS class.
VueApp/src/CMS/components/FileFormDialog.vue Replaces inline dialog card widths with shared CSS classes.
VueApp/src/CMS/components/DateRangeFilter.vue New shared From/To date-range input component.
VueApp/src/CMS/components/ContentDiffDialog.vue New dialog for displaying content diffs with legend/states/loading.
VueApp/src/CMS/tests/url.test.ts Adds comprehensive tests for CMS URL safety helpers.
VueApp/src/CMS/tests/test-utils.ts Adds shared CMS test harness (Quasar + router + apiURL provide).
VueApp/src/CMS/tests/recent-activity.test.ts Tests multi-source merge/sort/cap/failure semantics in RecentActivity.
VueApp/src/CMS/tests/person-selector.test.ts Tests PersonSelector min-chars gate + fetch semantics.
VueApp/src/CMS/tests/permission-selector.test.ts Tests PermissionSelector lazy-load + filter caching semantics.
VueApp/src/CMS/tests/modified-stamp.test.ts Tests ModifiedStamp table vs card rendering and formatting.
VueApp/src/CMS/tests/link.test.ts Tests Link URL safety rendering and tag badge color cycling.
VueApp/src/CMS/tests/link-collections.test.ts Tests LinkCollections filtering/grouping behaviors.
VueApp/src/CMS/tests/left-nav-menus.test.ts Tests LeftNavMenus query wiring + deep-link dialog open.
VueApp/src/CMS/tests/left-nav-menu-dialog.test.ts Tests LeftNavMenuDialog validation and POST success/failure flows.
VueApp/src/CMS/tests/files.test.ts Tests Files page query construction + pagination passthrough.
VueApp/src/CMS/tests/file-form-dialog.test.ts Tests FileFormDialog add/edit validation and FormData payloads.
VueApp/src/CMS/tests/date-range-filter.test.ts Tests DateRangeFilter v-model bindings + change emission.
VueApp/src/CMS/tests/content-diff-dialog.test.ts Tests ContentDiffDialog close, legend visibility, and empty/identical states.
VueApp/src/CMS/tests/content-blocks.test.ts Tests ContentBlocks filter→URL sync and list request params.
VueApp/src/CMS/tests/content-block-history.test.ts Tests ContentBlockHistory list query, URL sync, and diff-viewer flows.
VueApp/src/CMS/tests/content-block-edit-diff.test.ts Tests ContentBlockEdit diff-vs-current POST flow + gating behavior.
test/Services/HtmlSanitizerServiceTests.cs Adds security-focused tests for SanitizeDiff behavior.
test/CMS/CMSUserPhotoControllerTests.cs Adds controller wiring tests for CMS user photo endpoints.
test/CMS/CMSLinkCollectionControllerTests.cs Adds EF in-memory tests covering collections and tag-category CRUD/reorder/delete.
test/CMS/CmsLeftNavDisplayTests.cs Adds permission-filtering display tests for left-nav menus.
test/CMS/CMSLeftNavControllerTests.cs Adds controller wiring tests for left-nav controller behaviors.
test/CMS/CMSContentPermissionTests.cs Adds public-display permission gate tests for content blocks.
test/CMS/CmsContentBlockServiceTests.cs Adds cross-block history + diff behavior tests for content block service.
test/Classes/Utilities/DateRangeHelperTests.cs Adds unit tests for DateRangeHelper.ExclusiveUpperBound.

Comment on lines +90 to +95
<q-badge
v-if="cellProps.row.blockDeleted"
color="negative"
class="q-ml-sm"
label="deleted"
/>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Already fixed on the branch: the deleted pill uses the shared StatusBadge. In #253.

Comment on lines +132 to +136
<q-badge
v-if="row.blockDeleted"
color="negative"
label="deleted"
/>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Already fixed on the branch: the card-header marker uses StatusBadge as well. In #253.

@rlorenzo

rlorenzo commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the restacked 3-PR stack: #251 (files/photos/import/rate-limit backend) -> #252 (content blocks/left nav/link collections backend) -> #253 (management SPA). These historical cut-point slices predated the branch's later OS-independent path handling and cleanup commits, so they failed CI on Linux runners; the new slices are built from the CI-verified branch tip, carry every review fix (see thread replies), and each passes the full gate set. All feedback on this PR has been answered inline.

@rlorenzo rlorenzo closed this Jul 3, 2026
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