Skip to content

Merge Manage and Configuration List Type Tabs - #854

Merged
junaidiqbalmoj merged 59 commits into
masterfrom
feature-manage-list-types-699
Jul 24, 2026
Merged

Merge Manage and Configuration List Type Tabs#854
junaidiqbalmoj merged 59 commits into
masterfrom
feature-manage-list-types-699

Conversation

@junaidiqbalmoj

@junaidiqbalmoj junaidiqbalmoj commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Jira link

#699

Change description

Merge Manage and Configuration List Type Tabs

Summary by CodeRabbit

  • New Features

    • Introduced a consolidated Manage List Types journey for system administrators.
    • Added options to create and edit case name and case number JSON field names.
    • Added dedicated add, edit and detail screens, with preview and success updates.
    • Added support for restoring previously deleted list types.
    • Added English and Welsh content for the new screens.
  • Bug Fixes

    • Updated redirects from retired configuration and viewing pages.
    • Improved deletion navigation and handling of associated artefacts.
    • Added validation for JSON field names up to 255 characters.

NatashaAlker and others added 30 commits May 29, 2026 12:50
# Conflicts:
#	apps/postgres/package.json
#	libs/system-admin-pages/src/reference-data-upload/services/download-service.ts
# Conflicts:
#	libs/system-admin-pages/src/reference-data-upload/services/download-service.ts
Resolve file-location conflicts caused by master renaming
libs/system-admin-pages/src/pages/ to apps/web/src/pages/(system-admin)/.
New pages added on this branch are now at the correct location.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move en.ts/cy.ts content files from apps/web/src/pages/ to libs/system-admin-pages/src/
- Move JurisdictionDataSession from apps/ to libs/system-admin-pages/src/session-types.ts
- Export all new content and types from libs/system-admin-pages/src/index.ts
- Update controllers to import from @hmcts/system-admin-pages (no relative lib imports)
- Replace req.query.lng locale detection with res.locals.locale pattern
- Fix templates to use {% block page_content %} instead of {% block content %}
- Update tests to mock @hmcts/system-admin-pages and use res.locals for locale

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-service

Extract checkNameUniqueness helper to remove the repeated findFirst
pattern across Jurisdiction, Sub-Jurisdiction, and Region branches,
bringing duplication from 3.35% to 0%.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The constraint was already added by migration 20260528115459_add_third_party_push_log
from master, so the duplicate ADD CONSTRAINT statement in this branch's
migration caused P3018 on apply.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…update form

- Add dedicated /region-data-* pages (list, create, modify, update, delete, success) separate from jurisdiction flow
- Move Manage Region Data tile to reference-data, move Manage Jurisdiction Data to system-admin-dashboard
- Remove Region from jurisdiction-data-list filter and jurisdiction-data-create type options
- Fix jurisdiction-data-update to show type dropdown only for Sub-Jurisdiction records; parent jurisdiction select shown conditionally based on session type
- Fix jurisdiction-data-delete to embed record name in heading instead of summary list
- Fix back link text on jurisdiction-data-list showing URL instead of "Back"
- Sync dist content files and update all affected tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The dev DB has stale records for removed migrations (20260527140208 and
20260528115459_add_third_party_push_log) that cause migrate deploy to
hang, leading to Helm timeout. Matches the fix already applied in PR-669.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolves SonarQube security finding - omitting --ignore-scripts allows
arbitrary shell scripts to run during package execution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…admin_audit_log

The migration was renamed to 20260605141023_add_jurisdiction_soft_delete_and_audit_log.
If the dev DB has the old name recorded, migrate deploy tries to re-apply
the same SQL and fails on already-existing tables/columns, blocking the deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deleting 20260529131401_add_soft_delete_and_admin_audit_log causes
migrate deploy to re-apply the same SQL, failing on already-existing
tables and columns. Rename it to match the current migration file name
so Prisma recognises it as already applied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…record

This migration exists as a real file on this branch and should not be
deleted from _prisma_migrations — deleting it caused migrate deploy to
re-apply the SQL and fail on already-existing tables/constraints.
Only delete the orphan record 20260527140208 which has no migration file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…anup

The dev DB has partial state from multiple failed deploys - the
admin_audit_log table and deleted_at columns may already exist under
the old migration name. Adding IF NOT EXISTS to all DDL statements lets
the migration run safely regardless of the DB's current state.

Also removes the old migration record (20260529131401) instead of
renaming it - with idempotent SQL the migration can simply re-run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…timeout

The FlexibleServersDatabase ASO provisioning + DB wait can exceed the
15m Helm timeout on a first deploy. Starting the health server on port
5555 immediately lets Helm mark the pod as ready and complete the deploy,
while the DB wait loop and migrations run to completion afterwards.
The E2E tests then wait for /test-support/health to confirm migrations
are done before running.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion

DDL migrations (DROP COLUMN, DROP TABLE) require ACCESS EXCLUSIVE locks.
During a Helm rolling upgrade the old pod's Prisma Studio holds open
connections that block those locks until the old pod is terminated.
The old pod is only terminated once the new pod becomes Ready, and the
new pod only becomes Ready after migrations complete — a deadlock that
causes Helm to time out at 15m.

Starting the health server immediately lets Helm mark the pod Ready,
terminate the old pod, and release its connections before migrations run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npx can fetch and install packages on-demand; using the local binary
directly ensures only the pinned prisma@7.8.0 already installed in the
image is executed, with no network access or lifecycle script risk.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ence

With health-first startup, the postgres pod reports ready before
migrations finish. The old health check queried jurisdiction.findMany()
which passes immediately (table predates migration 20260702), so E2E
setup began while DROP COLUMN/DROP TABLE were still holding locks,
causing 500s on artefact operations.

Querying _prisma_migrations for rows with no finished_at or rolled_back_at
accurately reflects whether migrate deploy is still running, so the
global-setup retry loop waits until the DB is truly ready.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
With health-first startup, migrations run after Helm marks the pod Ready.
DDL migrations (DROP COLUMN) must wait for the old pod's termination grace
period before acquiring ACCESS EXCLUSIVE locks, then execute. The previous
30 × 2s = 60s window was too short. 60 × 5s = 5min covers this reliably.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The WORKDIR in the postgres container is apps/postgres, so node_modules
resolves to apps/postgres/node_modules which does not exist in a yarn
workspace. The hoisted binary is at the workspace root two levels up.
Also reverts the global-setup timeout increase — it was a workaround for
this bug, not the real fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
junaidiqbalmoj and others added 6 commits July 13, 2026 17:05
…dependency checks

hasDependencies was counting locationSubJurisdiction and locationRegion rows
without filtering out soft-deleted locations, causing false "linked to locations"
errors when trying to delete sub-jurisdictions or regions after their courts
had been soft-deleted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion or region

The FK constraint on location_sub_jurisdiction.sub_jurisdiction_id is RESTRICT,
so deleting a sub-jurisdiction fails if any locationSubJurisdiction rows reference
it — even when the linked locations are soft-deleted. Wrapping the deleteMany and
delete in a transaction ensures orphaned rows are cleaned up first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ssage

Replace hasDependencies (boolean) with getDependencyType which returns the
specific blocker — 'sub-jurisdictions', 'locations', or 'list-types'. The
service maps this to a precise error message so users know whether deletion
is blocked by linked locations or linked list types.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…data upload behaviour

- Fix getDependencyType to query from location model side so deletedAt
  filter works correctly for sub-jurisdiction and region active link counts
- Fix hardDeleteJurisdictionRecord to only remove orphaned junction rows
  (soft-deleted locations), not active location links
- Fix upsertLocations to clear deletedAt when re-uploading a soft-deleted
  location, restoring it as active
- Fix download CSV to exclude soft-deleted locations
- Add /jurisdiction-data link to missing sub-jurisdiction validation error
- Fix reference-data-upload-summary template to render html error content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The assertion was checking for "locations" but the mock returns
"sub-jurisdictions", making it test the wrong dependency type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@junaidiqbalmoj, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 58 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f2b1de9-6351-4cf4-94bd-7a13ce9f8f4f

📥 Commits

Reviewing files that changed from the base of the PR and between de4c916 and 73071b5.

📒 Files selected for processing (4)
  • apps/web/src/pages/(system-admin)/configure-list-type-preview/index.njk.test.ts
  • apps/web/src/pages/(system-admin)/delete-list-type-success/index.njk.test.ts
  • apps/web/src/pages/(system-admin)/delete-list-type/index.njk
  • apps/web/src/pages/(system-admin)/delete-list-type/index.njk.test.ts
📝 Walkthrough

Walkthrough

The PR adds nullable case-name and case-number JSON field names across persistence and validation layers, introduces add/edit/manage list-type pages, consolidates legacy routes under /manage-list-types, updates preview and delete flows, and adds system-admin journey coverage and ticket documentation.

Changes

List type data and validation

Layer / File(s) Summary
JSON field persistence and validation
apps/postgres/prisma/migrations/..., libs/postgres-prisma/..., libs/system-admin-pages/src/list-type/*
Adds two nullable VARCHAR(255) fields, validates their lengths, and persists them through list-type create, update, and restore operations.

Add and edit journeys

Layer / File(s) Summary
Add and edit forms
apps/web/src/pages/(system-admin)/add-list-type/*, apps/web/src/pages/(system-admin)/edit-list-type/*
Adds GOV.UK forms, English and Welsh copy, role-protected GET/POST handlers, validation, duplicate-name checks, and session draft handling.

Management navigation

Layer / File(s) Summary
Manage, preview, redirects, and delete flow
apps/web/src/pages/(system-admin)/manage-list-type/*, manage-list-types/*, configure-list-type-*/*, delete-list-type*, view-list-types/*
Adds detail management, updates dashboard and list links, carries JSON fields through preview, redirects legacy entry points, and updates delete and success navigation.

Validation coverage

Layer / File(s) Summary
System-admin journey tests
e2e-tests/tests/system-admin/*
Replaces the previous configure/view coverage with skipped add, edit, and delete manage-list-type journey scenarios, including Welsh and accessibility checks.

Ticket records

Layer / File(s) Summary
Ticket implementation records
docs/tickets/699/*
Documents the consolidated journey, data-layer changes, acceptance criteria, implementation tasks, review findings, and outstanding translation or migration notes.

Possibly related issues

Possibly related PRs

  • hmcts/cath-service#181 — Established the list-type CRUD, service, validation, and page layers extended by this PR.
  • hmcts/cath-service#669 — Uses the same configured case JSON field names when creating list-search configuration.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarises the main change: merging the manage and configuration list type flows into one.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature-manage-list-types-699

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@junaidiqbalmoj
junaidiqbalmoj changed the base branch from feature/410-system-admin-data-management to master July 16, 2026 14:11
Resolve conflicts:
- success page njk files: use govuk-grid-column-full (master) for consistency
- region-data-delete (cy/en/njk/ts/test) and queries.ts: take master's
  findLocationsByRegionId feature (superset of feature branch)
- system-admin-dashboard cy/en: include both Manage List Types and
  Configure List Type tiles
- system-admin-dashboard test: assert 11 tiles with merged order

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🎭 Playwright E2E Test Results

82 tests   52 ✅  6m 52s ⏱️
31 suites  30 💤
 1 files     0 ❌

Results for commit 73071b5.

♻️ This comment has been updated with latest results.

… dashboard

The Configure List Type page (/configure-list-type-enter-details) now 301
redirects to /manage-list-types, so the dashboard tile duplicated the Manage
List Types tile. Remove it from en/cy content and update tests to 10 tiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment Successful 🚀

Your preview environment is ready:

The environment will be automatically cleaned up when this PR is closed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/tickets/699/tasks.md (1)

91-97: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align E2E task status with actual execution.

Both documents should distinguish implemented test definitions from runnable coverage:

  • docs/tickets/699/tasks.md#L91-L97: leave execution incomplete while the journeys remain skipped.
  • docs/tickets/699/review.md#L73-L78: report skipped journeys as a coverage gap, not completed E2E coverage.
🧹 Nitpick comments (3)
libs/system-admin-pages/src/list-type/validation.ts (1)

127-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated length-check blocks — extract a shared helper.

The two blocks differ only by field name, message, and href. Extracting a small validateOptionalFieldLength helper (mirroring the existing validateRequiredField) would avoid the duplication and make future optional-field additions (e.g. more JSON field-name columns) trivial.

♻️ Proposed refactor
+function validateOptionalFieldLength(value: string | null | undefined, field: string, maxLength: number, label: string): ValidationError | null {
+  if (value && value.length > maxLength) {
+    return { field, message: `${label} must be ${maxLength} characters or less`, href: `#${field}` };
+  }
+  return null;
+}
+
 export function validateListTypeDetails(data: ListTypeDetailsInput) {
   ...
-  if (data.caseNumberJsonFieldName && data.caseNumberJsonFieldName.length > 255) {
-    errors.push({
-      field: "caseNumberJsonFieldName",
-      message: "Case number JSON field name must be 255 characters or less",
-      href: "`#caseNumberJsonFieldName`"
-    });
-  }
-
-  if (data.caseNameJsonFieldName && data.caseNameJsonFieldName.length > 255) {
-    errors.push({
-      field: "caseNameJsonFieldName",
-      message: "Case name JSON field name must be 255 characters or less",
-      href: "`#caseNameJsonFieldName`"
-    });
-  }
+  const caseNumberError = validateOptionalFieldLength(data.caseNumberJsonFieldName, "caseNumberJsonFieldName", 255, "Case number JSON field name");
+  if (caseNumberError) errors.push(caseNumberError);
+
+  const caseNameError = validateOptionalFieldLength(data.caseNameJsonFieldName, "caseNameJsonFieldName", 255, "Case name JSON field name");
+  if (caseNameError) errors.push(caseNameError);
apps/web/src/pages/(system-admin)/delete-list-type/index.njk (1)

18-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove redundant CSS class on anchor element.

The govuk-body class is redundant on the <a> element because govuk-link already provides the appropriate link styling, and it inherits the text styles from the parent <p class="govuk-body">.

♻️ Proposed refactor
     {% if hasArtefacts %}
       <p class="govuk-body">
-        <a href="/remove-list-search" class="govuk-link govuk-body">{{ removeArtefactsLinkText }}</a>
+        <a href="/remove-list-search" class="govuk-link">{{ removeArtefactsLinkText }}</a>
       </p>
     {% endif %}
apps/web/src/pages/(system-admin)/manage-list-type/index.njk (1)

32-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use t.notSet fallback for consistency.

Other optional properties in this table use or t.notSet when the value is absent. Consider applying the same fallback to the JSON field names for a consistent user experience.

♻️ Proposed refactor
           <tr class="govuk-table__row">
             <th scope="row" class="govuk-table__header">{{ t.caseNumberJsonFieldNameLabel }}</th>
-            <td class="govuk-table__cell">{{ listType.caseNumberJsonFieldName }}</td>
+            <td class="govuk-table__cell">{{ listType.caseNumberJsonFieldName or t.notSet }}</td>
           </tr>
           <tr class="govuk-table__row">
             <th scope="row" class="govuk-table__header">{{ t.caseNameJsonFieldNameLabel }}</th>
-            <td class="govuk-table__cell">{{ listType.caseNameJsonFieldName }}</td>
+            <td class="govuk-table__cell">{{ listType.caseNameJsonFieldName or t.notSet }}</td>
           </tr>

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6a30f1af-a809-445a-ba50-4c8eeaa078c8

📥 Commits

Reviewing files that changed from the base of the PR and between 9574e39 and de4c916.

📒 Files selected for processing (59)
  • apps/postgres/prisma/migrations/20260714112456_add_list_type_json_fields/migration.sql
  • apps/web/src/pages/(system-admin)/add-list-type/cy.ts
  • apps/web/src/pages/(system-admin)/add-list-type/en.ts
  • apps/web/src/pages/(system-admin)/add-list-type/index.njk
  • apps/web/src/pages/(system-admin)/add-list-type/index.test.ts
  • apps/web/src/pages/(system-admin)/add-list-type/index.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-enter-details/index.test.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-enter-details/index.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-preview/cy.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-preview/en.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-preview/index.njk
  • apps/web/src/pages/(system-admin)/configure-list-type-preview/index.test.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-preview/index.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-select-sub-jurisdictions/index.njk
  • apps/web/src/pages/(system-admin)/configure-list-type-select-sub-jurisdictions/index.test.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-select-sub-jurisdictions/index.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-success/cy.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-success/en.ts
  • apps/web/src/pages/(system-admin)/configure-list-type-success/index.njk
  • apps/web/src/pages/(system-admin)/delete-list-type-success/index.njk
  • apps/web/src/pages/(system-admin)/delete-list-type/cy.ts
  • apps/web/src/pages/(system-admin)/delete-list-type/en.ts
  • apps/web/src/pages/(system-admin)/delete-list-type/index.njk
  • apps/web/src/pages/(system-admin)/delete-list-type/index.test.ts
  • apps/web/src/pages/(system-admin)/delete-list-type/index.ts
  • apps/web/src/pages/(system-admin)/edit-list-type/cy.ts
  • apps/web/src/pages/(system-admin)/edit-list-type/en.ts
  • apps/web/src/pages/(system-admin)/edit-list-type/index.njk
  • apps/web/src/pages/(system-admin)/edit-list-type/index.test.ts
  • apps/web/src/pages/(system-admin)/edit-list-type/index.ts
  • apps/web/src/pages/(system-admin)/manage-list-type/cy.ts
  • apps/web/src/pages/(system-admin)/manage-list-type/en.ts
  • apps/web/src/pages/(system-admin)/manage-list-type/index.njk
  • apps/web/src/pages/(system-admin)/manage-list-type/index.test.ts
  • apps/web/src/pages/(system-admin)/manage-list-type/index.ts
  • apps/web/src/pages/(system-admin)/manage-list-types/cy.ts
  • apps/web/src/pages/(system-admin)/manage-list-types/en.ts
  • apps/web/src/pages/(system-admin)/manage-list-types/index.njk
  • apps/web/src/pages/(system-admin)/manage-list-types/index.test.ts
  • apps/web/src/pages/(system-admin)/manage-list-types/index.ts
  • apps/web/src/pages/(system-admin)/system-admin-dashboard/cy.ts
  • apps/web/src/pages/(system-admin)/system-admin-dashboard/en.ts
  • apps/web/src/pages/(system-admin)/view-list-types/index.test.ts
  • apps/web/src/pages/(system-admin)/view-list-types/index.ts
  • docs/tickets/699/plan.md
  • docs/tickets/699/review.md
  • docs/tickets/699/tasks.md
  • docs/tickets/699/ticket.md
  • e2e-tests/tests/system-admin/configure-list-type.spec.ts
  • e2e-tests/tests/system-admin/manage-list-types.spec.ts
  • e2e-tests/tests/system-admin/system-admin-dashboard.spec.ts
  • libs/postgres-prisma/prisma/schema/location.prisma
  • libs/system-admin-pages/src/list-type/queries.test.ts
  • libs/system-admin-pages/src/list-type/queries.ts
  • libs/system-admin-pages/src/list-type/service.test.ts
  • libs/system-admin-pages/src/list-type/service.ts
  • libs/system-admin-pages/src/list-type/types.ts
  • libs/system-admin-pages/src/list-type/validation.test.ts
  • libs/system-admin-pages/src/list-type/validation.ts
💤 Files with no reviewable changes (2)
  • apps/web/src/pages/(system-admin)/system-admin-dashboard/en.ts
  • apps/web/src/pages/(system-admin)/system-admin-dashboard/cy.ts

Comment on lines +2 to +6
title: "[WELSH TRANSLATION REQUIRED: 'Enter list type details']",
nameLabel: "[WELSH TRANSLATION REQUIRED: 'Name']",
friendlyNameLabel: "[WELSH TRANSLATION REQUIRED: 'Friendly name']",
welshFriendlyNameLabel: "[WELSH TRANSLATION REQUIRED: 'Welsh friendly name']",
shortenedFriendlyNameLabel: "[WELSH TRANSLATION REQUIRED: 'Shortened friendly name']",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Provide proper Welsh translations.

The provided code uses placeholder strings indicating that Welsh translations are still required. As per coding guidelines, every user-facing page must provide both English and Welsh content and render localised values rather than hardcoded display strings or placeholders. While the PR objectives mention remaining translation notes, the guidelines strictly require actual translations.

  • apps/web/src/pages/(system-admin)/add-list-type/cy.ts#L2-L6: Replace the placeholder strings with accurate Welsh translations.
  • apps/web/src/pages/(system-admin)/edit-list-type/cy.ts#L2-L6: Replace the placeholder strings with accurate Welsh translations.
  • apps/web/src/pages/(system-admin)/manage-list-type/cy.ts#L2-L6: Replace the placeholder strings with accurate Welsh translations.
  • apps/web/src/pages/(system-admin)/manage-list-types/cy.ts#L7-L7: Replace the placeholder string with an accurate Welsh translation.
  • apps/web/src/pages/(system-admin)/configure-list-type-success/cy.ts#L3-L7: Replace the placeholder strings with accurate Welsh translations.
  • apps/web/src/pages/(system-admin)/delete-list-type/cy.ts#L16-L16: Replace the placeholder string with an accurate Welsh translation.
📍 Affects 6 files
  • apps/web/src/pages/(system-admin)/add-list-type/cy.ts#L2-L6 (this comment)
  • apps/web/src/pages/(system-admin)/edit-list-type/cy.ts#L2-L6
  • apps/web/src/pages/(system-admin)/manage-list-type/cy.ts#L2-L6
  • apps/web/src/pages/(system-admin)/manage-list-types/cy.ts#L7-L7
  • apps/web/src/pages/(system-admin)/configure-list-type-success/cy.ts#L3-L7
  • apps/web/src/pages/(system-admin)/delete-list-type/cy.ts#L16-L16

Source: Coding guidelines

Comment on lines +3 to +7
title: "List type updated",
banner: "List type updated successfully.",
description: "What do you want to do next?",
returnLink: "Return to System Admin dashboard"
returnLink: "Return to System Admin dashboard",
manageListTypesLink: "Manage list types"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "manageListTypesLink|List type updated" "apps/web/src/pages/(system-admin)/configure-list-type-success/cy.ts"

Repository: hmcts/cath-service

Length of output: 382


Add Welsh translations for the new success copy
apps/web/src/pages/(system-admin)/configure-list-type-success/cy.ts still has placeholders for title, banner and manageListTypesLink; this page needs matching Welsh content for the updated success screen.

Source: Path instructions

Comment on lines 62 to 70
if (!confirmDelete) {
return res.render("delete-list-type/index", {
...content,
id,
listTypeName: listType.friendlyName || listType.name,
errors: [{ text: content.errorConfirmationRequired, href: "#confirmDelete" }],
data: { confirmDelete }
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate that the confirmation value is strictly accepted.

Currently, any truthy value other than "no" bypasses this check and results in the list type being deleted. Please enforce strict validation against the expected values. As per coding guidelines, input must be validated on all endpoints.

🐛 Proposed fix
-  if (!confirmDelete) {
+  if (confirmDelete !== "yes" && confirmDelete !== "no") {
     return res.render("delete-list-type/index", {
       ...content,
       id,
       listTypeName: listType.friendlyName || listType.name,
       errors: [{ text: content.errorConfirmationRequired, href: "`#confirmDelete`" }],
       data: { confirmDelete }
     });
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!confirmDelete) {
return res.render("delete-list-type/index", {
...content,
id,
listTypeName: listType.friendlyName || listType.name,
errors: [{ text: content.errorConfirmationRequired, href: "#confirmDelete" }],
data: { confirmDelete }
});
}
if (confirmDelete !== "yes" && confirmDelete !== "no") {
return res.render("delete-list-type/index", {
...content,
id,
listTypeName: listType.friendlyName || listType.name,
errors: [{ text: content.errorConfirmationRequired, href: "`#confirmDelete`" }],
data: { confirmDelete }
});
}

Source: Coding guidelines

Comment on lines +85 to +90
items: [
{ value: "", text: "Select an option", selected: not data.defaultSensitivity },
{ value: "Public", text: "Public", selected: data.defaultSensitivity == "Public" },
{ value: "Private", text: "Private", selected: data.defaultSensitivity == "Private" },
{ value: "Classified", text: "Classified", selected: data.defaultSensitivity == "Classified" }
],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Hardcoded select option text is not localized.

"Select an option" (and the sensitivity option labels) are hardcoded English and will not render in Welsh, unlike the surrounding t.*-driven fields. Source these strings from the locale content.

As per path instructions: "Use the Nunjucks page-template pattern: ... use localized data and error summaries for forms."

🌐 Suggested localization
         items: [
-          { value: "", text: "Select an option", selected: not data.defaultSensitivity },
-          { value: "Public", text: "Public", selected: data.defaultSensitivity == "Public" },
-          { value: "Private", text: "Private", selected: data.defaultSensitivity == "Private" },
-          { value: "Classified", text: "Classified", selected: data.defaultSensitivity == "Classified" }
+          { value: "", text: t.sensitivitySelectOption, selected: not data.defaultSensitivity },
+          { value: "Public", text: t.sensitivityPublic, selected: data.defaultSensitivity == "Public" },
+          { value: "Private", text: t.sensitivityPrivate, selected: data.defaultSensitivity == "Private" },
+          { value: "Classified", text: t.sensitivityClassified, selected: data.defaultSensitivity == "Classified" }
         ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
items: [
{ value: "", text: "Select an option", selected: not data.defaultSensitivity },
{ value: "Public", text: "Public", selected: data.defaultSensitivity == "Public" },
{ value: "Private", text: "Private", selected: data.defaultSensitivity == "Private" },
{ value: "Classified", text: "Classified", selected: data.defaultSensitivity == "Classified" }
],
items: [
{ value: "", text: t.sensitivitySelectOption, selected: not data.defaultSensitivity },
{ value: "Public", text: t.sensitivityPublic, selected: data.defaultSensitivity == "Public" },
{ value: "Private", text: t.sensitivityPrivate, selected: data.defaultSensitivity == "Private" },
{ value: "Classified", text: t.sensitivityClassified, selected: data.defaultSensitivity == "Classified" }
],

Source: Path instructions

Comment on lines +11 to +18
const rawId = req.query.id as string;
const id = Number.parseInt(rawId, 10);

if (!rawId || Number.isNaN(id)) {
return res.status(400).render("errors/common", { status: 400 });
}

const listType = await findListTypeById(id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Use listTypeName for routing and list type lookup.

The new manage-list-type detail flow is querying and routing by the numeric ListType.id. As per coding guidelines, "Use listTypeName, never numeric listTypeId, for list-type guards, routing...". Please update this flow to rely on the list type name parameter instead.

  • apps/web/src/pages/(system-admin)/manage-list-type/index.ts#L11-L18: Update the controller to process req.query.name (or similar) and perform the lookup using a name-based query instead of findListTypeById.
  • apps/web/src/pages/(system-admin)/manage-list-types/index.ts#L15-L15: Update the configureUrl generation to pass the name parameter in the query string.
  • apps/web/src/pages/(system-admin)/manage-list-type/index.test.ts#L42-L44: Update the test mocks and validation tests to supply and expect the name parameter.
  • apps/web/src/pages/(system-admin)/manage-list-types/index.test.ts#L121-L121: Change the regex assertion to expect the name parameter instead of a numeric ID.
  • apps/web/src/pages/(system-admin)/delete-list-type/index.test.ts#L233-L233: Update the redirect assertion to ensure it targets the name-based route.
📍 Affects 5 files
  • apps/web/src/pages/(system-admin)/manage-list-type/index.ts#L11-L18 (this comment)
  • apps/web/src/pages/(system-admin)/manage-list-type/index.test.ts#L42-L44
  • apps/web/src/pages/(system-admin)/manage-list-types/index.ts#L15-L15
  • apps/web/src/pages/(system-admin)/manage-list-types/index.test.ts#L121-L121
  • apps/web/src/pages/(system-admin)/delete-list-type/index.test.ts#L233-L233

Source: Coding guidelines

Comment on lines +24 to +28
const subJurisdictionsText =
listType.subJurisdictions.length > 0
? listType.subJurisdictions.map((sj) => (language === "cy" ? sj.subJurisdiction.welshName : sj.subJurisdiction.name)).join(", ")
: t.noneSelected;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Provide a fallback for missing Welsh sub-jurisdiction names.

If a sub-jurisdiction lacks a Welsh name, the current mapping will output a blank string. Please consider adding a fallback to the English name to ensure the text is rendered gracefully.

💡 Proposed fix
-  const subJurisdictionsText =
-    listType.subJurisdictions.length > 0
-      ? listType.subJurisdictions.map((sj) => (language === "cy" ? sj.subJurisdiction.welshName : sj.subJurisdiction.name)).join(", ")
-      : t.noneSelected;
+  const subJurisdictionsText =
+    listType.subJurisdictions.length > 0
+      ? listType.subJurisdictions.map((sj) => (language === "cy" ? (sj.subJurisdiction.welshName || sj.subJurisdiction.name) : sj.subJurisdiction.name)).join(", ")
+      : t.noneSelected;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const subJurisdictionsText =
listType.subJurisdictions.length > 0
? listType.subJurisdictions.map((sj) => (language === "cy" ? sj.subJurisdiction.welshName : sj.subJurisdiction.name)).join(", ")
: t.noneSelected;
const subJurisdictionsText =
listType.subJurisdictions.length > 0
? listType.subJurisdictions.map((sj) => (language === "cy" ? (sj.subJurisdiction.welshName || sj.subJurisdiction.name) : sj.subJurisdiction.name)).join(", ")
: t.noneSelected;

Comment thread docs/tickets/699/plan.md
One new capability is added:
- **`caseNumberJsonFieldName` / `caseNameJsonFieldName` form fields** (requires two new nullable columns on `ListType`).

Region selection is **out of scope** for this ticket — list types will not be linked to regions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Resolve the region-selection contract across the documentation.

The ticket requires regions, but the plan and task checklist exclude or omit the required persistence and journey work; the supplied controller also does not store regionIds.

  • docs/tickets/699/plan.md#L12-L12: state clearly whether region support is in scope.
  • docs/tickets/699/plan.md#L240-L242: document the actual region UI and validation behaviour.
  • docs/tickets/699/plan.md#L334-L353: include the correct session fields.
  • docs/tickets/699/plan.md#L500-L501: ensure persistence claims match the schema.
  • docs/tickets/699/tasks.md#L3-L18: add schema and service/query tasks for regions.
  • docs/tickets/699/tasks.md#L63-L75: add region UI, controller, validation, and test tasks.
  • docs/tickets/699/ticket.md#L149-L153: retain these requirements only if they will be implemented.
📍 Affects 3 files
  • docs/tickets/699/plan.md#L12-L12 (this comment)
  • docs/tickets/699/plan.md#L240-L242
  • docs/tickets/699/plan.md#L334-L353
  • docs/tickets/699/plan.md#L500-L501
  • docs/tickets/699/tasks.md#L3-L18
  • docs/tickets/699/tasks.md#L63-L75
  • docs/tickets/699/ticket.md#L149-L153

Comment on lines +54 to +55
### 5. Welsh translations are stubs throughout
All new `cy.ts` files use `[WELSH TRANSLATION REQUIRED: '...']` markers. These need to be replaced with real Welsh translations before the feature goes live. Consider creating a follow-up ticket to track translation work.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Do not mark Welsh support complete while translations remain placeholders.

The acceptance checklist marks Welsh support as complete, but the review states that all new Welsh content still contains [WELSH TRANSLATION REQUIRED: ...] markers and must be replaced before go-live. Mark this as pending or explicitly document that placeholders are an agreed interim state.

Also applies to: 93-93

Comment on lines +88 to +91
// Confirm to create
await page.getByRole("button", { name: "Confirm" }).click();
await page.waitForURL("**/configure-list-type-success");
await expect(page.getByRole("heading", { level: 1 })).toContainText("List type updated");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect shared success page content for add vs edit wording.
fd -a 'en.ts' 'apps/web/src/pages/(system-admin)/configure-list-type-success' --exec cat -n {}
rg -nP -C2 'updated|created|added' "apps/web/src/pages/(system-admin)/configure-list-type-success"

Repository: hmcts/cath-service

Length of output: 1871


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the spec and the shared success page implementation.
sed -n '1,220p' 'e2e-tests/tests/system-admin/manage-list-types.spec.ts'
printf '\n---\n'
fd -a 'page.tsx' 'apps/web/src/pages/(system-admin)/configure-list-type-success' -x sh -c 'echo "FILE: $1"; cat -n "$1"' sh {}
printf '\n---\n'
fd -a '*.ts' 'apps/web/src/pages/(system-admin)/configure-list-type-success' -x sh -c 'echo "FILE: $1"; cat -n "$1"' sh {}

Repository: hmcts/cath-service

Length of output: 10290


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the add/edit flows that navigate to the shared success page.
rg -n -C3 'configure-list-type-success|List type updated|success\.title|success\.banner' \
  'e2e-tests/tests/system-admin/manage-list-types.spec.ts' \
  'apps/web/src/pages' \
  'apps/web/src/components' \
  'apps/web/src' || true

Repository: hmcts/cath-service

Length of output: 27962


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the add and edit assertions around the shared success page.
nl -ba 'e2e-tests/tests/system-admin/manage-list-types.spec.ts' | sed -n '70,105p'
printf '\n---\n'
nl -ba 'e2e-tests/tests/system-admin/manage-list-types.spec.ts' | sed -n '145,170p'

Repository: hmcts/cath-service

Length of output: 196


Use create-specific success copy
The shared success page shows List type updated for the add journey as well as edit, so newly created list types get edit wording. Use create-specific copy or a neutral success message.

junaidiqbalmoj and others added 3 commits July 21, 2026 15:19
… tab merge

Restore the cancel link and xl heading on the delete-list-type page (lost in
the merge) and point it at /manage-list-type?id=. Update the preview and
delete-success template tests to assert the consolidated /manage-list-types and
changeDetailsHref routes instead of the pre-merge /view-list-types and
/configure-list-type-enter-details paths.

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

Copy link
Copy Markdown
Contributor Author

🤖 Addressed CI failures (issue #699):

  • Restored the cancel link and govuk-heading-xl on the delete-list-type page (both lost in the manage/configure tab merge); the cancel link now points to /manage-list-type?id=.
  • Updated the configure-list-type-preview and delete-list-type-success template tests to assert the consolidated /manage-list-types and changeDetailsHref (/add-list-type) routes instead of the pre-merge /view-list-types and /configure-list-type-enter-details paths.

All 6 previously-failing @hmcts/web unit tests now pass locally.

Note: the E2E Tests Build packages failure (Cannot find module '../generated/prisma/client.js') did not reproduce locally after a clean db:generate && build, and E2E passes on master with the same workflow — it looks like a transient CI issue. Re-watching CI now.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment Successful 🚀

Your preview environment is ready:

The environment will be automatically cleaned up when this PR is closed.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment Successful 🚀

Your preview environment is ready:

The environment will be automatically cleaned up when this PR is closed.

@junaidiqbalmoj
junaidiqbalmoj merged commit 7ead600 into master Jul 24, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Manage List Type Screens (Screens for adding jurisdictions and regions)

2 participants