Skip to content

feat: add magistrates-standard-list and magistrates-public-list modules - #767

Open
junaidiqbalmoj wants to merge 1 commit into
masterfrom
vibe-596
Open

feat: add magistrates-standard-list and magistrates-public-list modules#767
junaidiqbalmoj wants to merge 1 commit into
masterfrom
vibe-596

Conversation

@junaidiqbalmoj

@junaidiqbalmoj junaidiqbalmoj commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds @hmcts/magistrates-standard-list lib module with rendering, PDF generation, JSON schema validation, email summary extraction, and EN/CY translations
  • Adds @hmcts/magistrates-public-list lib module with the same complete feature set
  • Registers both modules in apps/web (app.ts, package.json, tsconfig.json)

Test plan

  • Build: yarn build — 37/37 packages pass
  • Lint: yarn lint — all packages clean
  • Unit tests: yarn test — 37/37 packages pass, 1709 tests pass
  • Visit /magistrates-standard-list?artefactId=<id> to verify HTML rendering
  • Visit /magistrates-public-list?artefactId=<id> to verify HTML rendering
  • Append ?lng=cy to verify Welsh translations render correctly
  • Verify PDF generation via the PDF endpoint for each list type
  • Verify 400 returned for missing/invalid artefactId
  • Verify 403 returned for restricted content

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added two new magistrates list pages for public and standard views, including English and Welsh content, search, open justice information, and court hearing tables.
    • Introduced PDF and email summary support for both list types.
  • Bug Fixes

    • Improved handling for missing data, unreadable files, invalid input, and access-denied scenarios with clearer error pages.
    • Added provenance-based source labelling, with a fallback when no label is available.
  • Tests

    • Added coverage for rendering, validation, localisation, and error handling across both list types.

Implements two new court list modules for magistrates courts with full rendering,
PDF generation, email summary extraction, JSON schema validation, and bilingual
(EN/CY) support. Both modules follow the civil-and-family-daily-cause-list pattern
and are registered in the web app.

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

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Two new monorepo packages — @hmcts/magistrates-public-list and @hmcts/magistrates-standard-list — are added under libs/list-types/. Each provides TypeScript data models, a JSON schema, a renderer, a JSON validator, an email summary builder, a PDF generator, Nunjucks page and PDF templates, and EN/CY localisation. Both are registered in the root tsconfig.json, added as workspace dependencies in apps/web, and wired into the app's GovUK module paths.

Changes

Magistrates Public & Standard List types

Layer / File(s) Summary
Package setup and app wiring
libs/list-types/magistrates-public-list/package.json, libs/list-types/magistrates-public-list/tsconfig.json, libs/list-types/magistrates-standard-list/package.json, libs/list-types/magistrates-standard-list/tsconfig.json, tsconfig.json, apps/web/package.json, apps/web/src/app.ts, libs/list-types/magistrates-public-list/src/config.ts, libs/list-types/magistrates-standard-list/src/config.ts, libs/list-types/magistrates-public-list/src/index.ts, libs/list-types/magistrates-standard-list/src/index.ts
Creates ESM package manifests, TypeScript configs, and config modules for both libraries; registers them as workspace dependencies and path aliases; imports and appends their moduleRoot/pageRoutes to modulePaths in app.ts.
Data models, JSON schemas, and validators
libs/list-types/magistrates-public-list/src/models/types.ts, libs/list-types/magistrates-standard-list/src/models/types.ts, libs/list-types/magistrates-public-list/src/schemas/magistrates-public-list.json, libs/list-types/magistrates-standard-list/src/schemas/magistrates-standard-list.json, libs/list-types/magistrates-*/src/validation/json-validator.ts, libs/list-types/magistrates-*/src/validation/json-validator.test.ts
Defines Party, MagistratesCase, Hearing, Sitting, Session, CourtRoom, CourtHouse, MagistratesListData, and RenderOptions interfaces for both list types; adds draft-2020-12 JSON schemas; exports validateMagistratesPublicList / validateMagistratesStandardList delegating to validateJson at version "1.0".
Data rendering layer
libs/list-types/magistrates-*/src/rendering/renderer.ts, libs/list-types/magistrates-*/src/rendering/renderer.test.ts
Exports renderMagistratesListData which resolves location names (Welsh fallback), formats sitting times and dates in Europe/London, computes duration and caseHearingChannel, extracts defendant names, joins reporting restrictions, and returns { header, openJustice, listData }. Tests cover all formatting branches and locale behaviour.
Email summary builder
libs/list-types/magistrates-*/src/email-summary/summary-builder.ts, libs/list-types/magistrates-*/src/email-summary/summary-builder.test.ts
Implements extractCaseSummary traversing the full court-list hierarchy to produce CaseSummary[] with conditional defendant name and defaulted case fields; re-exports formatCaseSummaryForEmail and SPECIAL_CATEGORY_DATA_WARNING.
Page route handlers, localisation, and Nunjucks page templates
libs/list-types/magistrates-*/src/pages/index.ts, libs/list-types/magistrates-*/src/pages/index.test.ts, libs/list-types/magistrates-*/src/pages/en.ts, libs/list-types/magistrates-*/src/pages/cy.ts, libs/list-types/magistrates-*/src/pages/magistrates-*.njk, apps/web/src/pages/(list-types)/magistrates-*/index.ts, apps/web/src/pages/(list-types)/magistrates-*/index.test.ts, apps/web/src/pages/(list-types)/magistrates-*/magistrates-*.njk
Implements GET handlers in both libs/ and apps/web/: validates artefactId, fetches artefact and list type, checks access, reads and validates the temp JSON file, renders page data, maps provenance to dataSource, and renders the Nunjucks view. Adds EN/CY localisation and GOV.UK-styled Nunjucks templates with nested accordion/table court-list rendering.
PDF generation and templates
libs/list-types/magistrates-*/src/pdf/pdf-generator.ts, libs/list-types/magistrates-*/src/pdf/pdf-generator.test.ts, libs/list-types/magistrates-*/src/pdf/pdf-template.njk
Adds generateMagistratesPublicListPdf / generateMagistratesStandardListPdf: concurrently renders list data and loads translations, derives provenanceLabel, renders pdf-template.njk, generates PDF from HTML, validates the result, and persists to storage. Nunjucks PDF templates mirror the page structure including conditional restriction rows.

Possibly related issues

  • Style Guide: Implement Magistrates Public List #596: The PR directly implements the registration and addition of the magistrates standard/public list modules and dependencies described in this issue.
  • hmcts/cath-service#610: The PR follows the same app/package/tsconfig module registration pattern described in this issue for new list types.

Possibly related PRs

  • hmcts/cath-service#192: Uses the same modulePaths/configureGovuk wiring in apps/web/src/app.ts to register new list-type modules, with identical import and array-append patterns.
  • hmcts/cath-service#204: Registers RCJ-standard list modules via the same modulePaths extension and configureGovuk call in app.ts.
  • hmcts/cath-service#663: Wires new list-type modules into app.ts by adding imports and appending to modulePaths, directly overlapping with the bootstrapping changes in this PR.
🚥 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 addition of the two new magistrates list modules.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vibe-596

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.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
13.8% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🎭 Playwright E2E Test Results

84 tests   51 ✅  6m 26s ⏱️
33 suites  32 💤
 1 files     1 ❌

For more details on these failures, see this check.

Results for commit 86a298f.

♻️ This comment has been updated with latest results.

@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: 7

🧹 Nitpick comments (3)
libs/list-types/magistrates-standard-list/src/index.ts (1)

1-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move type exports to the bottom of the barrel.

Line 1 places a type export before value exports; reorder so interfaces/types are last to match repo conventions.

As per coding guidelines, **/*.ts: "Order module exports: top-level constants first, then exported functions, then other functions ordered by usage, with interfaces and types at the bottom".

Source: Coding guidelines

libs/list-types/magistrates-standard-list/src/schemas/magistrates-standard-list.json (1)

1-294: 🗄️ Data Integrity & Integration | 🔵 Trivial

Extract the shared magistrates schema Both list schemas are identical; if that is intentional, use a common schema to avoid drift between the public and standard variants.

apps/web/src/pages/(list-types)/magistrates-public-list/index.test.ts (1)

96-116: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing the 403 access-denied test case.

The lib-level suite covers canAccessPublicationData returning false, but this app-level suite jumps from "artefact not found" straight to the JSON-read path. Adding a 403 case keeps coverage aligned with the controller's access check.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 92305571-69b0-4959-92ff-8ec4663402a1

📥 Commits

Reviewing files that changed from the base of the PR and between 5a364d0 and 86a298f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (49)
  • apps/web/package.json
  • apps/web/src/app.ts
  • apps/web/src/pages/(list-types)/magistrates-public-list/index.test.ts
  • apps/web/src/pages/(list-types)/magistrates-public-list/index.ts
  • apps/web/src/pages/(list-types)/magistrates-public-list/magistrates-public-list.njk
  • apps/web/src/pages/(list-types)/magistrates-standard-list/index.test.ts
  • apps/web/src/pages/(list-types)/magistrates-standard-list/index.ts
  • apps/web/src/pages/(list-types)/magistrates-standard-list/magistrates-standard-list.njk
  • libs/list-types/magistrates-public-list/package.json
  • libs/list-types/magistrates-public-list/src/config.ts
  • libs/list-types/magistrates-public-list/src/email-summary/summary-builder.test.ts
  • libs/list-types/magistrates-public-list/src/email-summary/summary-builder.ts
  • libs/list-types/magistrates-public-list/src/index.ts
  • libs/list-types/magistrates-public-list/src/models/types.ts
  • libs/list-types/magistrates-public-list/src/pages/cy.ts
  • libs/list-types/magistrates-public-list/src/pages/en.ts
  • libs/list-types/magistrates-public-list/src/pages/index.test.ts
  • libs/list-types/magistrates-public-list/src/pages/index.ts
  • libs/list-types/magistrates-public-list/src/pages/magistrates-public-list.njk
  • libs/list-types/magistrates-public-list/src/pdf/pdf-generator.test.ts
  • libs/list-types/magistrates-public-list/src/pdf/pdf-generator.ts
  • libs/list-types/magistrates-public-list/src/pdf/pdf-template.njk
  • libs/list-types/magistrates-public-list/src/rendering/renderer.test.ts
  • libs/list-types/magistrates-public-list/src/rendering/renderer.ts
  • libs/list-types/magistrates-public-list/src/schemas/magistrates-public-list.json
  • libs/list-types/magistrates-public-list/src/validation/json-validator.test.ts
  • libs/list-types/magistrates-public-list/src/validation/json-validator.ts
  • libs/list-types/magistrates-public-list/tsconfig.json
  • libs/list-types/magistrates-standard-list/package.json
  • libs/list-types/magistrates-standard-list/src/config.ts
  • libs/list-types/magistrates-standard-list/src/email-summary/summary-builder.test.ts
  • libs/list-types/magistrates-standard-list/src/email-summary/summary-builder.ts
  • libs/list-types/magistrates-standard-list/src/index.ts
  • libs/list-types/magistrates-standard-list/src/models/types.ts
  • libs/list-types/magistrates-standard-list/src/pages/cy.ts
  • libs/list-types/magistrates-standard-list/src/pages/en.ts
  • libs/list-types/magistrates-standard-list/src/pages/index.test.ts
  • libs/list-types/magistrates-standard-list/src/pages/index.ts
  • libs/list-types/magistrates-standard-list/src/pages/magistrates-standard-list.njk
  • libs/list-types/magistrates-standard-list/src/pdf/pdf-generator.test.ts
  • libs/list-types/magistrates-standard-list/src/pdf/pdf-generator.ts
  • libs/list-types/magistrates-standard-list/src/pdf/pdf-template.njk
  • libs/list-types/magistrates-standard-list/src/rendering/renderer.test.ts
  • libs/list-types/magistrates-standard-list/src/rendering/renderer.ts
  • libs/list-types/magistrates-standard-list/src/schemas/magistrates-standard-list.json
  • libs/list-types/magistrates-standard-list/src/validation/json-validator.test.ts
  • libs/list-types/magistrates-standard-list/src/validation/json-validator.ts
  • libs/list-types/magistrates-standard-list/tsconfig.json
  • tsconfig.json

Comment on lines +88 to +98
const jsonData = JSON.parse(jsonContent);

const validationResult = validateMagistratesStandardList(jsonData);
if (!validationResult.isValid) {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Malformed JSON returns 500 instead of 400.

readFile failures are mapped to 404, and schema validation failures to 400, but JSON.parse on a corrupt temp file throws into the outer catch and produces a 500. A non-parseable payload is a bad/invalid publication, so it should be treated consistently with the validation-failure path (400).

🛠️ Proposed fix
-    const jsonData = JSON.parse(jsonContent);
-
-    const validationResult = validateMagistratesStandardList(jsonData);
-    if (!validationResult.isValid) {
+    let jsonData: unknown;
+    try {
+      jsonData = JSON.parse(jsonContent);
+    } catch {
+      return res.status(400).render("errors/common", {
+        en,
+        cy,
+        errorTitle: t.errorTitle,
+        errorMessage: t.errorMessage
+      });
+    }
+
+    const validationResult = validateMagistratesStandardList(jsonData);
+    if (!validationResult.isValid) {
📝 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 jsonData = JSON.parse(jsonContent);
const validationResult = validateMagistratesStandardList(jsonData);
if (!validationResult.isValid) {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}
let jsonData: unknown;
try {
jsonData = JSON.parse(jsonContent);
} catch {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}
const validationResult = validateMagistratesStandardList(jsonData);
if (!validationResult.isValid) {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}

Comment on lines +86 to +96
const jsonData = JSON.parse(jsonContent);

const validationResult = validateMagistratesPublicList(jsonData);
if (!validationResult.isValid) {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Malformed JSON falls through to a 500.

JSON.parse sits in the outer try, so an unparsable temp file surfaces as a generic 500 rather than the 400 used for invalid payloads. Consider wrapping the parse to return a 400 for consistency with the validation path.

🛠️ Proposed fix
-    const jsonData = JSON.parse(jsonContent);
+    let jsonData: unknown;
+    try {
+      jsonData = JSON.parse(jsonContent);
+    } catch {
+      return res.status(400).render("errors/common", {
+        en,
+        cy,
+        errorTitle: t.errorTitle,
+        errorMessage: t.errorMessage
+      });
+    }
📝 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 jsonData = JSON.parse(jsonContent);
const validationResult = validateMagistratesPublicList(jsonData);
if (!validationResult.isValid) {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}
let jsonData: unknown;
try {
jsonData = JSON.parse(jsonContent);
} catch {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}
const validationResult = validateMagistratesPublicList(jsonData);
if (!validationResult.isValid) {
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: t.errorTitle,
errorMessage: t.errorMessage
});
}

@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="en">

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

lang attribute is hardcoded to en for Welsh PDFs.

The same template renders both EN and CY output, but <html lang="en"> is fixed. For Welsh PDFs this mislabels the document language, affecting assistive technology and language metadata. Pass the locale through and set it dynamically.

🌐 Suggested fix
-<html lang="en">
+<html lang="{{ locale or 'en' }}">

You will also need to pass locale into the template model from pdf-generator.ts (env.render("pdf-template.njk", { ..., locale: options.locale })).

📝 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
<html lang="en">
<html lang="{{ locale or 'en' }}">

Comment on lines +15 to +21
function formatContentDate(date: Date, locale: string): string {
const localeCode = locale === "cy" ? "cy-GB" : "en-GB";
return date.toLocaleDateString(localeCode, {
day: "2-digit",
month: "long",
year: "numeric"
});

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
for tz in UTC America/New_York Europe/London; do
  TZ="$tz" node -e 'const date=new Date("2025-01-01"); console.log(process.env.TZ, date.toLocaleDateString("en-GB",{day:"2-digit",month:"long",year:"numeric"}));'
done

Repository: hmcts/cath-service

Length of output: 240


Pin formatContentDate to Europe/London. toLocaleDateString() uses the host timezone, so artefact.contentDate can render the previous or next day on non-London nodes. Use Intl.DateTimeFormat(..., { timeZone: "Europe/London" }) instead.

Comment on lines +24 to +37
function formatPublicationDateTime(isoDateTime: string, locale: string): string {
const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);

const dateStr = dt.toFormat("d MMMM yyyy");

const hours = dt.hour;
const minutes = dt.minute;
const period = hours >= 12 ? "pm" : "am";
const hour12 = hours % 12 || 12;

const minuteStr = minutes > 0 ? `:${minutes.toString().padStart(2, "0")}` : "";
const timeStr = `${hour12}${minuteStr}${period}`;

return `${dateStr} at ${timeStr}`;

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

Remove the hard-coded English connector from Welsh output.

Line 37 always emits " at ", so header.lastUpdated stays partially English for locale === "cy".

Suggested fix
 function formatPublicationDateTime(isoDateTime: string, locale: string): string {
   const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);
@@
-  return `${dateStr} at ${timeStr}`;
+  const connector = locale === "cy" ? "am" : "at";
+  return `${dateStr} ${connector} ${timeStr}`;
 }

As per coding guidelines, "Implement Welsh language support on all user-facing text with separate en.ts and cy.ts content files".

📝 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
function formatPublicationDateTime(isoDateTime: string, locale: string): string {
const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);
const dateStr = dt.toFormat("d MMMM yyyy");
const hours = dt.hour;
const minutes = dt.minute;
const period = hours >= 12 ? "pm" : "am";
const hour12 = hours % 12 || 12;
const minuteStr = minutes > 0 ? `:${minutes.toString().padStart(2, "0")}` : "";
const timeStr = `${hour12}${minuteStr}${period}`;
return `${dateStr} at ${timeStr}`;
function formatPublicationDateTime(isoDateTime: string, locale: string): string {
const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);
const dateStr = dt.toFormat("d MMMM yyyy");
const hours = dt.hour;
const minutes = dt.minute;
const period = hours >= 12 ? "pm" : "am";
const hour12 = hours % 12 || 12;
const minuteStr = minutes > 0 ? `:${minutes.toString().padStart(2, "0")}` : "";
const timeStr = `${hour12}${minuteStr}${period}`;
const connector = locale === "cy" ? "am" : "at";
return `${dateStr} ${connector} ${timeStr}`;
}

Source: Coding guidelines

Comment on lines +15 to +21
function formatContentDate(date: Date, locale: string): string {
const localeCode = locale === "cy" ? "cy-GB" : "en-GB";
return date.toLocaleDateString(localeCode, {
day: "2-digit",
month: "long",
year: "numeric"
});

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
for tz in UTC America/New_York Europe/London; do
  TZ="$tz" node -e 'const date=new Date("2025-01-01"); console.log(process.env.TZ, date.toLocaleDateString("en-GB",{day:"2-digit",month:"long",year:"numeric"}));'
done

Repository: hmcts/cath-service

Length of output: 240


Pin content-date formatting to Europe/London. libs/list-types/magistrates-standard-list/src/rendering/renderer.ts:15-21

formatContentDate() formats a Date in the host process timezone, so the displayed day can shift on non-UTC/London nodes.

Suggested fix
 function formatContentDate(date: Date, locale: string): string {
   const localeCode = locale === "cy" ? "cy-GB" : "en-GB";
-  return date.toLocaleDateString(localeCode, {
+  return new Intl.DateTimeFormat(localeCode, {
     day: "2-digit",
     month: "long",
-    year: "numeric"
-  });
+    year: "numeric",
+    timeZone: "Europe/London"
+  }).format(date);
 }
📝 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
function formatContentDate(date: Date, locale: string): string {
const localeCode = locale === "cy" ? "cy-GB" : "en-GB";
return date.toLocaleDateString(localeCode, {
day: "2-digit",
month: "long",
year: "numeric"
});
function formatContentDate(date: Date, locale: string): string {
const localeCode = locale === "cy" ? "cy-GB" : "en-GB";
return new Intl.DateTimeFormat(localeCode, {
day: "2-digit",
month: "long",
year: "numeric",
timeZone: "Europe/London"
}).format(date);
}

Comment on lines +24 to +37
function formatPublicationDateTime(isoDateTime: string, locale: string): string {
const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);

const dateStr = dt.toFormat("d MMMM yyyy");

const hours = dt.hour;
const minutes = dt.minute;
const period = hours >= 12 ? "pm" : "am";
const hour12 = hours % 12 || 12;

const minuteStr = minutes > 0 ? `:${minutes.toString().padStart(2, "0")}` : "";
const timeStr = `${hour12}${minuteStr}${period}`;

return `${dateStr} at ${timeStr}`;

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

Remove the hard-coded English connector from Welsh output.

Line 37 always emits " at ", so header.lastUpdated stays partially English for locale === "cy".

Suggested fix
 function formatPublicationDateTime(isoDateTime: string, locale: string): string {
   const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);
@@
-  return `${dateStr} at ${timeStr}`;
+  const connector = locale === "cy" ? "am" : "at";
+  return `${dateStr} ${connector} ${timeStr}`;
 }

As per coding guidelines, "Implement Welsh language support on all user-facing text with separate en.ts and cy.ts content files".

📝 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
function formatPublicationDateTime(isoDateTime: string, locale: string): string {
const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);
const dateStr = dt.toFormat("d MMMM yyyy");
const hours = dt.hour;
const minutes = dt.minute;
const period = hours >= 12 ? "pm" : "am";
const hour12 = hours % 12 || 12;
const minuteStr = minutes > 0 ? `:${minutes.toString().padStart(2, "0")}` : "";
const timeStr = `${hour12}${minuteStr}${period}`;
return `${dateStr} at ${timeStr}`;
function formatPublicationDateTime(isoDateTime: string, locale: string): string {
const dt = DateTime.fromISO(isoDateTime).setZone("Europe/London").setLocale(locale);
const dateStr = dt.toFormat("d MMMM yyyy");
const hours = dt.hour;
const minutes = dt.minute;
const period = hours >= 12 ? "pm" : "am";
const hour12 = hours % 12 || 12;
const minuteStr = minutes > 0 ? `:${minutes.toString().padStart(2, "0")}` : "";
const timeStr = `${hour12}${minuteStr}${period}`;
const connector = locale === "cy" ? "am" : "at";
return `${dateStr} ${connector} ${timeStr}`;
}

Source: Coding guidelines

@hmctsclaudecode

Copy link
Copy Markdown

⚠️ This PR now conflicts with master

Merging master into this branch would conflict in 36 file(s):

  • apps/web/package.json
  • apps/web/src/app.ts
  • apps/web/src/pages/(list-types)/magistrates-public-list/index.test.ts
  • apps/web/src/pages/(list-types)/magistrates-public-list/index.ts
  • apps/web/src/pages/(list-types)/magistrates-public-list/magistrates-public-list.njk
  • apps/web/src/pages/(list-types)/magistrates-standard-list/index.test.ts
  • apps/web/src/pages/(list-types)/magistrates-standard-list/index.ts
  • apps/web/src/pages/(list-types)/magistrates-standard-list/magistrates-standard-list.njk
  • libs/list-types/magistrates-public-list/package.json
  • libs/list-types/magistrates-public-list/src/config.ts
  • libs/list-types/magistrates-public-list/src/email-summary/summary-builder.test.ts
  • libs/list-types/magistrates-public-list/src/email-summary/summary-builder.ts
  • libs/list-types/magistrates-public-list/src/index.ts
  • libs/list-types/magistrates-public-list/src/pdf/pdf-generator.test.ts
  • libs/list-types/magistrates-public-list/src/pdf/pdf-generator.ts
  • libs/list-types/magistrates-public-list/src/pdf/pdf-template.njk
  • libs/list-types/magistrates-public-list/src/rendering/renderer.test.ts
  • libs/list-types/magistrates-public-list/src/rendering/renderer.ts
  • libs/list-types/magistrates-public-list/src/schemas/magistrates-public-list.json
  • libs/list-types/magistrates-public-list/src/validation/json-validator.test.ts

…and 16 more.

Nothing has been changed on your branch. Rebase with /auto-rebase locally, or git fetch origin && git rebase origin/master.

Re-checked automatically on every push to master.

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.

1 participant