feat: UT non-strategic publishing (UTCC, UTLC, UTAAC) #425 - #669
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR introduces three new Upper Tribunal daily hearing list modules (Tax and Chancery Chamber, Lands Chamber, Administrative Appeals Chamber), each with converters, JSON schemas, validators, renderers, email summaries, PDF generators, locales, and web pages. It adds shared PDF generator infrastructure, updates reference/seed data, wires the modules into publication, notification, and web app registries, and adds a public PDF download route. ChangesUpper Tribunal daily hearing list publishing
Sequence Diagram(s)sequenceDiagram
participant Browser
participant WebController as "UT Page GET Handler"
participant Validator as "JSON Validator"
participant Publication as "Publication Store"
participant Renderer as "renderUt*DailyHearingListData"
participant Template as "Nunjucks Template"
Browser->>WebController: GET /upper-tribunal-*-daily-hearing-list?artefactId
WebController->>Publication: getArtefactById(artefactId)
Publication-->>WebController: artefact
WebController->>Publication: getPublicationJson(artefactId)
Publication-->>WebController: jsonData
WebController->>Validator: validate(jsonData)
Validator-->>WebController: isValid
WebController->>Renderer: render(jsonData, options)
Renderer-->>WebController: header, hearings
WebController->>Template: res.render(view, header, hearings, dataSource, pdfDownloadUrl)
Template-->>Browser: Rendered HTML page
sequenceDiagram
participant Publisher as "Publication Processor"
participant PdfRegistry as "PDF_GENERATOR_REGISTRY"
participant Generator as "generateUt*DailyHearingListPdf"
participant Renderer as "renderUt*DailyHearingListData"
participant PdfEngine as "generatePdfFromHtml"
participant Storage as "Azure Blob Storage"
Publisher->>PdfRegistry: lookup(listTypeName)
PdfRegistry-->>Publisher: generateUt*DailyHearingListPdf
Publisher->>Generator: generatePdf(jsonData, options)
Generator->>Renderer: render(hearingList, options)
Renderer-->>Generator: header, hearings
Generator->>PdfEngine: generatePdfFromHtml(html)
PdfEngine-->>Generator: pdfBuffer, sizeBytes
Generator->>Storage: uploadBlob(artefactId, pdfBuffer)
Storage-->>Generator: success
Generator-->>Publisher: pdfPath, sizeBytes, exceedsMaxSize
Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
🎭 Playwright E2E Test Results84 tests 52 ✅ 6m 19s ⏱️ Results for commit 08d5738. ♻️ This comment has been updated with latest results. |
5574997 to
1eefd54
Compare
Creates three new list type modules for the Upper Tribunal (Tax and Chancery Chamber), Upper Tribunal (Lands Chamber) and Upper Tribunal (Administrative Appeals Chamber) daily hearing lists, enabling publication through the non-strategic upload route. Each module includes a JSON validation schema, Excel converter, renderer, PDF generator, email summary builder, page controller, Nunjucks template and English/Welsh content. Integration points updated in list-type-data, location-data, PDF_GENERATOR_REGISTRY, EMAIL_BUILDER_REGISTRY, app.ts and the seed script. Fixes seed-data.ts to upsert sub-jurisdictions on existing databases before seeding list types, so new sub-jurisdictions are propagated without requiring a full database reset. Closes #425 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nload route - Relax required constraints on UTAAC/UTLC/UTCC Excel config fields so rows with empty optional columns don't fail validation - Fix excel-to-json header detection to use parsed headers rather than derived object keys - Fix getField to return empty string for missing optional columns rather than throwing - Add PDF download route for public artefact serving - Add json-validator exports for UT list types - Register UT list type converters in non-strategic-upload page - Add devcontainer port forwarding and cross-platform yarn architecture support Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…chosen When a UTCC, UTLC, or UTAAC list type is selected in the non-strategic upload form, the sensitivity field now automatically defaults to Public via the existing list-type-sensitivity JS mechanism. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces [Welsh] placeholders with proper Welsh translations sourced from the existing manual-upload Welsh translations, which cover the same fields and error messages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Align JSON schemas with converter configs: only time, caseReference/caseReferenceNumber, and caseName are required fields for UTCC, UTLC, and UTAAC (judges, members, hearingType, venue etc are optional) - Mark optional fields as optional (?) in UtccHearing, UtlcHearing, and UtaacHearing TypeScript interfaces - Add Welsh translations for all three UT list type pages (replace [TRANSLATE: ...] placeholders) - Remove inline <style> blocks from all three Nunjucks templates; use govuk-!-margin-top-7 utility class instead - Replace any types in non-strategic-upload/index.ts with proper types (unknown, Record<string, string>, Session, typed option array) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds json-validator tests, converter config tests, and additional page controller and pdf-generator cases to bring all three modules above 80% coverage on all metrics (~98% statements, ~85% branches). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The three UT pdf-generator files were identical except for courtName, listTitle, renderer function, and hearing list type. Introduces createDailyHearingListPdfGenerator in @hmcts/list-types-common and replaces each file with a single-call wrapper, eliminating the duplication flagged by the quality gate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ities Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pe names Replaces all [TRANSLATE: ...] placeholders with Welsh text across: - UTCC, UTLC and UTAAC cy.ts page translation files - location-data.ts: location names, sub-jurisdiction names and National region - list-type-data.ts: welshFriendlyName for all three list types Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The always-run seed path only upserted sub-jurisdictions and list types, so Welsh names on locations (e.g. locationIds 13–15) were never applied to an existing database. Adds locations to the always-upsert block so Welsh name changes take effect without a full re-seed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
f7f3d8c to
41cb3d4
Compare
There was a problem hiding this comment.
Actionable comments posted: 19
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (4)
libs/location/src/list-type-data.ts-323-323 (1)
323-323:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winNormalise user-facing title casing in
englishFriendlyName.These new labels use
Daily Hearing list(lowercaselist), which is inconsistent with existing naming and likely to surface in UI/admin content. UseDaily Hearing Listfor consistency.Also applies to: 335-335, 347-347
apps/postgres/prisma/seed.ts-234-238 (1)
234-238:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
upsertcurrently cannot correct stalelistSearchConfigrows.Line 237 uses
update: {}, so reruns do not repair existing records if field mappings ever change. PopulateupdatewithcaseNumberFieldNameandcaseNameFieldNameto keep seeds idempotent and self-healing.libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pdf/pdf-template.njk-2-2 (1)
2-2:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTemplate language is fixed to English.
The root
langattribute is alwaysen; Welsh-rendered documents should expose the active locale in markup for accessibility tooling and correct language metadata.libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pages/index.ts-62-63 (1)
62-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTreat malformed JSON as invalid list data (400), not server failure (500).
A
JSON.parsefailure currently falls to the outer catch and returns 500. That is a client/data validation outcome and should map to the same invalid-data path as schema failures.Also applies to: 95-103
🧹 Nitpick comments (12)
libs/list-types/common/src/conversion/excel-to-json.test.ts (1)
83-98: ⚡ Quick winAdd a regression test for a missing optional header column.
Current coverage still misses the key contract: a sheet should pass when a non-required column is absent from the header row. Please add this case to lock in optional-header behaviour.
libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/rendering/renderer.ts (1)
4-20: ⚡ Quick winReorder exports to match the TypeScript module ordering convention.
RenderOptionsandRenderedDataare declared before exported function declarations. Move interfaces/types to the bottom, after exported functions, to keep module structure consistent.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".Also applies to: 22-47
Source: Coding guidelines
libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pages/index.test.ts (1)
98-98: ⚡ Quick winReplace repeated
as anycasts with a typed artefact fixture.The
as anycasts weaken compile-time contract checks in these tests and can hide shape regressions in controller dependencies.As per coding guidelines, "
**/*.{ts,tsx}: Enable TypeScript strict mode and avoidanytype without justification".Also applies to: 170-170, 201-201, 275-275, 325-325
Source: Coding guidelines
libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/index.ts (1)
4-9: ⚡ Quick winReorder barrel exports to keep types at the bottom.
Place function/module exports first and move
export type { ValidationResult }to the end for consistent module layout.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/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/rendering/renderer.ts (1)
4-22: ⚡ Quick winReorder exports to match the repository TypeScript ordering rule.
RenderOptionsandRenderedDataare exported before the exported function. Move interfaces/types to the bottom of the module after exported/other functions.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/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/rendering/renderer.test.ts (1)
6-12: ⚡ Quick winUse SCREAMING_SNAKE_CASE for module-level constants.
Rename
baseOptionsto a SCREAMING_SNAKE_CASE constant name to align with the repository’s TS/JS constant convention.As per coding guidelines, "
**/*.{ts,tsx,js}: Use SCREAMING_SNAKE_CASE for constant declarations (e.g.,MAX_FILE_SIZE,DEFAULT_TIMEOUT)".Source: Coding guidelines
libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pages/index.test.ts (1)
96-96: ⚡ Quick winRemove repeated
as anycasts from artefact mocks.These casts suppress type checks on a critical contract in controller tests. Prefer a typed fixture (or
NonNullable<Awaited<ReturnType<typeof getArtefactById>>>) so schema/controller changes break tests safely.As per coding guidelines, "
**/*.{ts,tsx}: Enable TypeScript strict mode and avoidanytype without justification".Also applies to: 168-168, 204-204, 277-277, 326-326, 376-376
Source: Coding guidelines
libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/validation/json-validator.test.ts (1)
21-22: ⚡ Quick winStrengthen schema assertion to catch miswired validators.
The
expect.any(Object)matcher is too broad; these tests would still pass if the wrong schema is imported. Assert against the concrete schema object to lock the wrapper contract.Suggested change
import { validateJson } from "`@hmcts/publication`"; +import schema from "../schemas/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.json" with { type: "json" }; import { validateUtAdministrativeAppealsChamberDailyHearingList } from "./json-validator.js"; @@ - expect(validateJson).toHaveBeenCalledWith(mockData, expect.any(Object), "1.0"); + expect(validateJson).toHaveBeenCalledWith(mockData, schema, "1.0"); @@ - expect(validateJson).toHaveBeenCalledWith(mockData, expect.any(Object), "1.0"); + expect(validateJson).toHaveBeenCalledWith(mockData, schema, "1.0");Also applies to: 35-36
libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pages/upper-tribunal-lands-chamber-daily-hearing-list.njk (1)
3-10: ⚡ Quick winRemove inline style and use GOV.UK spacing utilities for the back-to-top block.
This keeps templates style-free and avoids reintroducing per-template CSS for a single spacing rule.
Proposed fix
-{% block head %} - {{ super() }} - <style> - .back-to-top { - margin-top: 40px; - } - </style> -{% endblock %} @@ - <div class="back-to-top"> + <div class="govuk-!-margin-top-6"> <a href="`#top`" class="govuk-link">{{ t.backToTop }}</a> </div>Also applies to: 84-86
libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/index.ts (1)
4-9: ⚡ Quick winReorder exports so type exports are at the bottom.
Line 4 places a type export before runtime exports; move the
ValidationResultexport below Lines 5-9 to match the module export ordering rule.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/upper-tribunal-lands-chamber-daily-hearing-list/src/rendering/renderer.ts (1)
4-22: ⚡ Quick winReorder exports to match the project’s TypeScript module ordering rule.
Line 4 to Line 21 should appear after the exported function so this module follows the required export sequence.
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/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/index.ts (1)
4-9: ⚡ Quick winMove type exports to the bottom to match module export ordering.
Place the
ValidationResulttype export after value exports in this module.As per coding guidelines, "Order module exports: top-level constants first, then exported functions, then other functions ordered by usage, with interfaces and types at the bottom".
Suggested change
-export type { ValidationResult } from "`@hmcts/publication`"; export * from "./email-summary/summary-builder.js"; export * from "./models/types.js"; export * from "./pdf/pdf-generator.js"; export * from "./rendering/renderer.js"; export { validateUtTaxAndChanceryChamberDailyHearingList } from "./validation/json-validator.js"; +export type { ValidationResult } from "`@hmcts/publication`";Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 04605b6f-0ff6-40c6-909b-4495343b346c
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (94)
.yarnrc.ymlapps/postgres/prisma/migrations/20260527140208/migration.sqlapps/postgres/prisma/seed.tsapps/web/package.jsonapps/web/src/app.test.tsapps/web/src/app.tsapps/web/src/pages/(admin)/non-strategic-upload-summary/cy.tsapps/web/src/pages/(admin)/non-strategic-upload/cy.tsapps/web/src/pages/(admin)/non-strategic-upload/index.njkapps/web/src/pages/(admin)/non-strategic-upload/index.tsdocs/tickets/425/plan.mddocs/tickets/425/tasks.mddocs/tickets/425/ticket.mdlibs/list-types/common/src/conversion/excel-to-json.test.tslibs/list-types/common/src/conversion/excel-to-json.tslibs/list-types/common/src/index.tslibs/list-types/common/src/pdf/pdf-utilities.test.tslibs/list-types/common/src/pdf/pdf-utilities.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/package.jsonlibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/config.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/config.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/conversion/utaac-config.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/conversion/utaac-config.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/email-summary/summary-builder.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/email-summary/summary-builder.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/index.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/models/types.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pages/cy.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pages/en.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pages/index.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pages/index.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pages/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.njklibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pdf/pdf-generator.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pdf/pdf-generator.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pdf/pdf-template.njklibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/rendering/renderer.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/rendering/renderer.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/schemas/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.jsonlibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/validation/json-validator.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/validation/json-validator.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/tsconfig.jsonlibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/package.jsonlibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/config.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/config.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/conversion/utlc-config.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/conversion/utlc-config.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/email-summary/summary-builder.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/email-summary/summary-builder.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/index.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/models/types.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pages/cy.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pages/en.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pages/index.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pages/index.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pages/upper-tribunal-lands-chamber-daily-hearing-list.njklibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pdf/pdf-generator.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pdf/pdf-generator.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pdf/pdf-template.njklibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/rendering/renderer.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/rendering/renderer.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/schemas/upper-tribunal-lands-chamber-daily-hearing-list.jsonlibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/validation/json-validator.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/validation/json-validator.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/tsconfig.jsonlibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/package.jsonlibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/config.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/config.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/conversion/utcc-config.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/conversion/utcc-config.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/email-summary/summary-builder.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/email-summary/summary-builder.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/index.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/models/types.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pages/cy.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pages/en.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pages/index.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pages/index.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pages/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list.njklibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pdf/pdf-generator.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pdf/pdf-generator.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pdf/pdf-template.njklibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/rendering/renderer.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/rendering/renderer.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/schemas/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list.jsonlibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/validation/json-validator.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/validation/json-validator.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/tsconfig.jsonlibs/location/src/list-type-data.tslibs/location/src/location-data.tslibs/location/src/seed-data.tslibs/notifications/src/notification/notification-service.tslibs/public-pages/src/routes/pdf/[artefactId]/download.tslibs/publication/src/processing/service.tstsconfig.json
| const sensitivityMap = Object.fromEntries(nonStrategicListTypes.map((listType) => [listType.id.toString(), Sensitivity.PUBLIC])); | ||
| return { options, sensitivityMap }; |
There was a problem hiding this comment.
Sensitivity defaults are now hardcoded to PUBLIC for all non-strategic list types.
Line 24 ignores defaultSensitivity from list type metadata and changes behaviour globally. This should preserve each list type’s configured default, with UT-specific handling only where required.
Suggested fix
- const sensitivityMap = Object.fromEntries(nonStrategicListTypes.map((listType) => [listType.id.toString(), Sensitivity.PUBLIC]));
+ const sensitivityMap = Object.fromEntries(
+ nonStrategicListTypes.map((listType) => [
+ listType.id.toString(),
+ listType.defaultSensitivity ?? Sensitivity.PUBLIC
+ ])
+ );Also applies to: 93-104
| const actualHeaders = headers.map((h) => h.toLowerCase().trim()); | ||
| validateHeaders(actualHeaders, config.fields); |
There was a problem hiding this comment.
Header validation still enforces optional columns as mandatory.
validateHeaders(actualHeaders, config.fields) currently validates all configured headers, so uploads missing optional columns still fail before row parsing. This defeats the optional-field handling added in getField.
Proposed fix
function validateHeaders(actualHeaders: string[], fields: FieldConfig[]): void {
- const expectedHeaders = fields.map((f) => f.header.toLowerCase());
+ const requiredFields = fields.filter((f) => f.required ?? true);
+ const expectedHeaders = requiredFields.map((f) => f.header.toLowerCase());
const missingHeaders = expectedHeaders.filter((expected) => !actualHeaders.includes(expected));
if (missingHeaders.length > 0) {
- const headerNames = fields.filter((f) => missingHeaders.includes(f.header.toLowerCase())).map((f) => f.header);
+ const headerNames = requiredFields
+ .filter((f) => missingHeaders.includes(f.header.toLowerCase()))
+ .map((f) => f.header);
- throw new Error(`Excel file must contain columns: ${fields.map((f) => f.header).join(", ")}. Missing: ${headerNames.join(", ")}`);
+ throw new Error(
+ `Excel file must contain columns: ${requiredFields.map((f) => f.header).join(", ")}. Missing: ${headerNames.join(", ")}`
+ );
}
}| appellant: string; | ||
| caseReferenceNumber: string; | ||
| caseName: string; | ||
| judges: string; | ||
| members: string; | ||
| modeOfHearing: string; | ||
| venue: string; |
There was a problem hiding this comment.
Align optional hearing fields with the converter/schema contract.
Line 3 and Lines 6-9 currently require fields that are configured as optional in the converter flow. This creates a contract mismatch between parsed JSON and TypeScript models, and can leak undefined into rendering paths while the type system assumes string.
Proposed fix
export interface UtaacHearing {
time: string;
- appellant: string;
+ appellant?: string;
caseReferenceNumber: string;
caseName: string;
- judges: string;
- members: string;
- modeOfHearing: string;
- venue: string;
+ judges?: string;
+ members?: string;
+ modeOfHearing?: string;
+ venue?: string;
additionalInformation?: string;
}📝 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.
| appellant: string; | |
| caseReferenceNumber: string; | |
| caseName: string; | |
| judges: string; | |
| members: string; | |
| modeOfHearing: string; | |
| venue: string; | |
| export interface UtaacHearing { | |
| time: string; | |
| appellant?: string; | |
| caseReferenceNumber: string; | |
| caseName: string; | |
| judges?: string; | |
| members?: string; | |
| modeOfHearing?: string; | |
| venue?: string; | |
| additionalInformation?: string; | |
| } |
| "minItems": 1, | ||
| "items": { | ||
| "type": "object", | ||
| "required": ["time", "caseReference", "caseName", "judges", "members", "hearingType", "venue"], |
There was a problem hiding this comment.
Required fields are stricter than the intended upload contract.
This schema currently requires judges, members, hearingType, and venue, which will reject valid records where those optional Excel fields are blank and trigger downstream “Invalid Data” responses.
Suggested fix
- "required": ["time", "caseReference", "caseName", "judges", "members", "hearingType", "venue"],
+ "required": ["time", "caseReference", "caseName"],📝 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.
| "required": ["time", "caseReference", "caseName", "judges", "members", "hearingType", "venue"], | |
| "required": ["time", "caseReference", "caseName"], |
| const needsSeeding = await shouldSeed(); | ||
| if (!needsSeeding) { | ||
| // Seed list types even for existing DBs to pick up new entries added since initial seed | ||
| // Upsert sub-jurisdictions, locations, and list types even for existing DBs to pick up new entries added since initial seed | ||
| for (const subJurisdiction of locationData.subJurisdictions) { | ||
| await prisma.subJurisdiction.upsert({ | ||
| where: { subJurisdictionId: subJurisdiction.subJurisdictionId }, | ||
| create: { | ||
| subJurisdictionId: subJurisdiction.subJurisdictionId, | ||
| name: subJurisdiction.name, | ||
| welshName: subJurisdiction.welshName, | ||
| jurisdictionId: subJurisdiction.jurisdictionId | ||
| }, | ||
| update: { | ||
| name: subJurisdiction.name, | ||
| welshName: subJurisdiction.welshName, | ||
| jurisdictionId: subJurisdiction.jurisdictionId | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| for (const location of locationData.locations) { | ||
| await prisma.location.upsert({ | ||
| where: { locationId: location.locationId }, | ||
| create: { | ||
| locationId: location.locationId, | ||
| name: location.name, | ||
| welshName: location.welshName, | ||
| email: null, | ||
| contactNo: null | ||
| }, | ||
| update: { | ||
| name: location.name, | ||
| welshName: location.welshName | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| await seedListTypes(); | ||
| return; |
There was a problem hiding this comment.
Prod/CI seed guard is bypassed in the new !needsSeeding branch.
Lines 39–41 route all shouldSeed() === false cases into write operations, including ENVIRONMENT=prod and CI=true. That contradicts the skip contract and can mutate protected environments.
Suggested fix
export async function seedLocationData() {
console.log("Checking if location data seeding is needed...");
+ if (process.env.ENVIRONMENT === "prod") {
+ console.log("Skipping seed: ENVIRONMENT is prod");
+ return;
+ }
+ if (process.env.CI === "true") {
+ console.log("Skipping seed: Running in CI environment");
+ return;
+ }
+
const needsSeeding = await shouldSeed();
if (!needsSeeding) {| // Upsert sub-jurisdictions, locations, and list types even for existing DBs to pick up new entries added since initial seed | ||
| for (const subJurisdiction of locationData.subJurisdictions) { | ||
| await prisma.subJurisdiction.upsert({ | ||
| where: { subJurisdictionId: subJurisdiction.subJurisdictionId }, | ||
| create: { | ||
| subJurisdictionId: subJurisdiction.subJurisdictionId, | ||
| name: subJurisdiction.name, | ||
| welshName: subJurisdiction.welshName, | ||
| jurisdictionId: subJurisdiction.jurisdictionId | ||
| }, | ||
| update: { | ||
| name: subJurisdiction.name, | ||
| welshName: subJurisdiction.welshName, | ||
| jurisdictionId: subJurisdiction.jurisdictionId | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| for (const location of locationData.locations) { | ||
| await prisma.location.upsert({ | ||
| where: { locationId: location.locationId }, | ||
| create: { | ||
| locationId: location.locationId, | ||
| name: location.name, | ||
| welshName: location.welshName, | ||
| email: null, | ||
| contactNo: null | ||
| }, | ||
| update: { | ||
| name: location.name, | ||
| welshName: location.welshName | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
Existing-DB path upserts locations without rebuilding their relations.
Lines 59–74 update location rows, but the same branch skips locationRegion, locationSubJurisdiction, and locationReference writes. New entries can therefore exist without join-table links, causing incomplete location behaviour downstream.
| const MONOREPO_ROOT = path.join(__dirname, "..", "..", "..", "..", "..", ".."); | ||
| const STORAGE_BASE = path.join(MONOREPO_ROOT, "storage", "temp", "uploads"); |
There was a problem hiding this comment.
Storage path configuration is hardcoded in runtime code.
MONOREPO_ROOT/STORAGE_BASE are environment-specific configuration values and should be injected via environment variables rather than fixed relative traversal.
Suggested fix
-const MONOREPO_ROOT = path.join(__dirname, "..", "..", "..", "..", "..", "..");
-const STORAGE_BASE = path.join(MONOREPO_ROOT, "storage", "temp", "uploads");
+const STORAGE_BASE = process.env.PDF_STORAGE_PATH;
+if (!STORAGE_BASE) {
+ throw new Error("Missing PDF_STORAGE_PATH");
+}As per coding guidelines, "Use environment variables for all configuration values and secrets, never hardcode them".
Source: Coding guidelines
| const artefact = await getArtefactById(artefactId); | ||
|
|
||
| if (!artefact) { | ||
| res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate"); | ||
| return res.status(404).json({ error: "Artefact not found" }); | ||
| } | ||
|
|
||
| const now = new Date(); | ||
| if (now < artefact.displayFrom || now > artefact.displayTo) { | ||
| res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate"); | ||
| return res.status(410).json({ error: "File has expired" }); | ||
| } |
There was a problem hiding this comment.
Public download flow does not enforce public sensitivity before serving files.
After artefact lookup and display-window checks, the handler serves the PDF without verifying that the artefact is actually public. Add an explicit sensitivity gate before file access to prevent unintended exposure of non-public artefacts.
Suggested fix
if (!artefact) {
res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate");
return res.status(404).json({ error: "Artefact not found" });
}
+ if (artefact.sensitivity !== "PUBLIC") {
+ res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate");
+ return res.status(404).json({ error: "Artefact not found" });
+ }
+
const now = new 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.
| const artefact = await getArtefactById(artefactId); | |
| if (!artefact) { | |
| res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate"); | |
| return res.status(404).json({ error: "Artefact not found" }); | |
| } | |
| const now = new Date(); | |
| if (now < artefact.displayFrom || now > artefact.displayTo) { | |
| res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate"); | |
| return res.status(410).json({ error: "File has expired" }); | |
| } | |
| const artefact = await getArtefactById(artefactId); | |
| if (!artefact) { | |
| res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate"); | |
| return res.status(404).json({ error: "Artefact not found" }); | |
| } | |
| if (artefact.sensitivity !== "PUBLIC") { | |
| res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate"); | |
| return res.status(404).json({ error: "Artefact not found" }); | |
| } | |
| const now = new Date(); | |
| if (now < artefact.displayFrom || now > artefact.displayTo) { | |
| res.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store, must-revalidate"); | |
| return res.status(410).json({ error: "File has expired" }); | |
| } |
…th new architecture Move UTCC, UTLC, and UTAAC page controllers from libs/src/pages/ to apps/web/src/pages/(list-types)/, relocate translations to src/locales/, replace pageRoutes with schemaPath in configs, and export locale objects with prefixed names from module index.ts — matching the pattern established by the care-standards-tribunal-weekly-hearing-list reference implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After moving translations from src/pages/ to src/locales/, the pdf-generator files in all three UT modules still referenced the old paths, causing TS2307 build errors and test failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bump typescript to 6.0.3 and vitest to 4.1.8 in all three UT modules to match monorepo — TS 5.9.3 does not accept ignoreDeprecations "6.0" in root tsconfig, causing Docker build failures - Remove stale build:nunjucks src/pages/ reference (pages were moved to apps/web; only src/pdf/ njk files remain to copy) - Fix list-type-validator test: replace CIVIL_DAILY_CAUSE_LIST (id=1) with LEGACY_UNSUPPORTED_LIST for the "no schema" test case, since @hmcts/civil-daily-cause-list now exists and has a validate function Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
importOriginal triggers real module resolution of civil-and-family-daily-cause-list which chains through to postgres-prisma generated client, causing a 5000ms test timeout in CI where the generated client does not exist. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/pages/(list-types)/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/index.ts (1)
64-65:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle malformed JSON as invalid input, not a server error.
At Line 64,
JSON.parsefailures fall into the outer catch and return HTTP 500 at Line 99. This should return the same 400 invalid-data path used for schema failures.Suggested fix
- const jsonData: UtccHearingList = JSON.parse(jsonContent); + let jsonData: UtccHearingList; + try { + jsonData = JSON.parse(jsonContent) as UtccHearingList; + } catch { + return res.status(400).render("errors/common", { + en, + cy, + errorTitle: "Invalid Data", + errorMessage: "The list data is invalid" + }); + }Also applies to: 97-104
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 828cec30-2ca7-4a1c-aa0f-eaf0df5c3211
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (32)
apps/web/src/app.test.tsapps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/index.test.tsapps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/index.tsapps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.njkapps/web/src/pages/(list-types)/upper-tribunal-lands-chamber-daily-hearing-list/index.test.tsapps/web/src/pages/(list-types)/upper-tribunal-lands-chamber-daily-hearing-list/index.tsapps/web/src/pages/(list-types)/upper-tribunal-lands-chamber-daily-hearing-list/upper-tribunal-lands-chamber-daily-hearing-list.njkapps/web/src/pages/(list-types)/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/index.test.tsapps/web/src/pages/(list-types)/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/index.tsapps/web/src/pages/(list-types)/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list.njklibs/list-types/common/src/validation/list-type-validator.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/package.jsonlibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/config.test.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/config.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/index.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/locales/cy.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/locales/en.tslibs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pdf/pdf-generator.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/package.jsonlibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/config.test.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/config.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/index.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/locales/cy.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/locales/en.tslibs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pdf/pdf-generator.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/package.jsonlibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/config.test.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/config.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/index.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/locales/cy.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/locales/en.tslibs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pdf/pdf-generator.ts
💤 Files with no reviewable changes (9)
- libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/locales/cy.ts
- libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/locales/cy.ts
- libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/locales/cy.ts
- libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/locales/en.ts
- libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/locales/en.ts
- libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/locales/en.ts
- apps/web/src/pages/(list-types)/upper-tribunal-lands-chamber-daily-hearing-list/upper-tribunal-lands-chamber-daily-hearing-list.njk
- apps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.njk
- apps/web/src/pages/(list-types)/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list.njk
✅ Files skipped from review due to trivial changes (1)
- libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/config.ts
🚧 Files skipped from review as they are similar to previous changes (10)
- apps/web/src/app.test.ts
- libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/index.ts
- libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/index.ts
- libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/package.json
- libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/package.json
- libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/index.ts
- libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/src/pdf/pdf-generator.ts
- libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/package.json
- libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/src/pdf/pdf-generator.ts
- libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/src/pdf/pdf-generator.ts
The shared E2E database already has the artefact_list_type_id_fkey constraint from a previous migration run. Re-applying the migration fails with 42710 (constraint already exists). Wrap in a DO block so the ADD CONSTRAINT is skipped if it already exists. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…otent 20260527140208 was a branch-only migration that added artefact_list_type_id_fkey, duplicating the same constraint already added by the master migration 20260528115459_add_third_party_push_log. The duplicate left the shared E2E database _prisma_migrations table in a failed state, blocking all subsequent migrations. Remove the duplicate migration and wrap the remaining ADD CONSTRAINT in a DO block so it is skipped if the constraint already exists. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Preview Deployment Successful 🚀Your preview environment is ready:
The environment will be automatically cleaned up when this PR is closed. |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fb73c798-cecd-48a0-b926-f4382fb67209
📒 Files selected for processing (8)
apps/postgres/prisma/migrations/20260707090002/migration.sqlapps/web/package.jsonapps/web/src/app.test.tsapps/web/src/app.tsapps/web/src/pages/(admin)/non-strategic-upload/index.tsapps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.njklibs/list-types/common/src/index.tslibs/list-types/common/src/pdf/pdf-utilities.ts
💤 Files with no reviewable changes (1)
- apps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.njk
✅ Files skipped from review due to trivial changes (3)
- libs/list-types/common/src/index.ts
- apps/web/package.json
- apps/web/src/app.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/web/src/app.ts
- apps/web/src/pages/(admin)/non-strategic-upload/index.ts
- libs/list-types/common/src/pdf/pdf-utilities.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fb73c798-cecd-48a0-b926-f4382fb67209
📒 Files selected for processing (8)
apps/postgres/prisma/migrations/20260707090002/migration.sqlapps/web/package.jsonapps/web/src/app.test.tsapps/web/src/app.tsapps/web/src/pages/(admin)/non-strategic-upload/index.tsapps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.njklibs/list-types/common/src/index.tslibs/list-types/common/src/pdf/pdf-utilities.ts
💤 Files with no reviewable changes (1)
- apps/web/src/pages/(list-types)/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/upper-tribunal-administrative-appeals-chamber-daily-hearing-list.njk
✅ Files skipped from review due to trivial changes (3)
- libs/list-types/common/src/index.ts
- apps/web/package.json
- apps/web/src/app.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/web/src/app.ts
- apps/web/src/pages/(admin)/non-strategic-upload/index.ts
- libs/list-types/common/src/pdf/pdf-utilities.ts
🛑 Comments failed to post (1)
apps/postgres/prisma/migrations/20260707090002/migration.sql (1)
2-2: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Avoid validating this foreign key inline.
This
ALTER TABLEwill scanartefactand can block writes while the constraint is added. If the table is non-trivial, this risks stalling deploys. PreferNOT VALIDhere, then validate in a separate migration/window.♻️ Suggested change
-ALTER TABLE "artefact" ADD CONSTRAINT "artefact_list_type_id_fkey" FOREIGN KEY ("list_type_id") REFERENCES "list_types"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "artefact" ADD CONSTRAINT "artefact_list_type_id_fkey" FOREIGN KEY ("list_type_id") REFERENCES "list_types"("id") ON DELETE RESTRICT ON UPDATE CASCADE NOT VALID; + +-- later, in a separate migration +ALTER TABLE "artefact" VALIDATE CONSTRAINT "artefact_list_type_id_fkey";🧰 Tools
🪛 Squawk (2.59.0)
[warning] 2-2: By default new constraints require a table scan and block writes to the table while that scan occurs. Use
NOT VALIDwith a laterVALIDATE CONSTRAINTcall.(constraint-missing-not-valid)
[warning] 2-2: Adding a foreign key constraint requires a table scan and a
SHARE ROW EXCLUSIVElock on both tables, which blocks writes to each table. AddNOT VALIDto the constraint in one transaction and then VALIDATE the constraint in a separate transaction.(adding-foreign-key-constraint)
Source: Linters/SAST tools
|
Preview Deployment Successful 🚀Your preview environment is ready:
The environment will be automatically cleaned up when this PR is closed. |



Summary
What's included
libs/list-types/upper-tribunal-tax-and-chancery-chamber-daily-hearing-list/— UTCC module (list type ID 28, National region)libs/list-types/upper-tribunal-lands-chamber-daily-hearing-list/— UTLC module (list type ID 29, National region)libs/list-types/upper-tribunal-administrative-appeals-chamber-daily-hearing-list/— UTAAC module (list type ID 30, London region, landscape PDF)time,caseReference/caseReferenceNumber, andcaseNameare required; all other fields optionalTest plan
?lng=cyand verify Welsh translations displayyarn test🤖 Generated with Claude Code
Summary by CodeRabbit