Files
pedwfrontend/components/case/status.js
T
Robert Bond 9595ad86df Merged PR 2374: addeding domain layer extrraction
# Summary

This PR introduces a **Case Lifecycle Domain Boundary** to centralize lifecycle decision logic and reduce coupling within the appeals application.

The work is **behaviour-preserving** and introduces no intentional changes to business rules, CRM integrations, translations, dashboards, API routes, or user-facing functionality.

## What was added

New lifecycle boundary:

```text
lib/domain/case-lifecycle/
```

Key responsibilities extracted:

- Specialist process normalization
- Appeal type mapping
- Specialist process stage override mapping
- Stage case-type resolution
- Stage catalogue lookup
- Closed-case status recognition
- Lifecycle stage index resolution
- Lifecycle stage status assignment

## Behaviour preserved

Characterization tests were added before each extraction to preserve:

- Appeal type mapping and aliases
- Specialist process handling
- Lifecycle stage progression
- Closed-case handling
- Status assignment (`complete`, `in-progress`, `not-started`)
- Existing ROW behaviour
- Existing `statuscode` lifecycle semantics

Closed-case recognition remains unchanged for:

```text
1000
5
6
846040013
846040059
846040060
```

## Documentation

Added:

```text
lib/domain/case-lifecycle/README.md
```

Documenting:

- Boundary ownership
- Non-goals
- Lifecycle invariants
- Known architectural constraints
- Future extraction roadmap

## Testing

Added lifecycle characterization coverage for:

- Stage wrapper behaviour
- Specialist process normalization
- Appeal type mapping
- Specialist process stage mapping
- Stage case-type resolution
- Stage catalogue lookup
- Progress behaviour
- Closed-case status handling
- Stage index resolution
- Stage status assignment

## Validation

- Lifecycle characterization tests passed
- `npm run lint` passed with no errors

## Out of Scope

No changes to:

- Stage catalogue ownership
- Representation eligibility
- Dashboard calculations
- CRM/OData queries
- API routes
- Redux state
- EN/CY translations
- Event visibility logic

## Risk

**Low risk**

The refactor was delivered through small, characterization-first slices with no functional changes intended.

Related work items: #23527
2026-06-08 09:34:30 +00:00

162 lines
6.3 KiB
JavaScript

import { setEventDetails } from "../../store/searchOutput/action";
import { setCurrentPage } from "../../store/currentView/action";
import { connect } from "react-redux";
import useTranslation from "next-translate/useTranslation";
import { formatDates } from "../utils";
import { useState } from "react";
import { getLifecycleStagesForCase } from "../../lib/domain/case-lifecycle";
const statusClassMap = {
complete: "govuk-tag govuk-!-margin-top-2",
"in-progress": "govuk-tag govuk-tag--yellow govuk-!-margin-top-2",
"not-started": "govuk-tag govuk-tag--grey govuk-!-margin-top-2",
blocked: "govuk-tag govuk-tag--red govuk-!-margin-top-2"
};
const statusTextMap = {
complete: "status-complete",
"in-progress": "status-in-progress",
"not-started": "status-not-started",
blocked: "status-blocked"
};
const StatusDetails = (props) => {
let { t } = useTranslation();
const specialistProcess =
props.searchResultsObj?.searchDetailsObj[0]?.value[0]
?.pinswg_specialistcaseprocess;
console.log(props.currentView?.caseReference?.statuscode);
const stages = getLifecycleStagesForCase(
props.currentView?.caseReference?.appealType,
props.currentView?.caseReference?.statuscode,
specialistProcess
);
const EventView = (eventsArr) => {
eventsArr = eventsArr.value;
return eventsArr.map((item, key) => {
<div>{item.pinswg_name}</div>;
});
};
const eventsArr = props.eventsObj?.value || [];
// event publishiing flag set to true displa
function hasOwnPropertyAndNotNull(obj, property) {
return obj.hasOwnProperty(property) && obj[property] !== null;
}
const [expandAll, setExpandAll] = useState(null);
// null = use default (in-progress open)
// true = force open all
// false = force close all
const handleToggleAll = () => {
setExpandAll((prev) => (prev === true ? false : true));
};
return (
<div className="govuk-grid-row">
<div className="govuk-grid-row">
<div className="govuk-grid-column-full">
<h2 className="govuk-heading-m ">
{t("case:status-title")}
</h2>
<button
type="button"
className="govuk-!-margin-bottom-3 govuk-link watchCase govuk-!-font-size-16 "
onClick={handleToggleAll}
>
{expandAll === true
? t("case:status-hide-all-label")
: t("case:status-show-all-label")}
</button>
<ul className="govuk-task-list">
{stages.map((stage) => {
const isDefaultOpen =
stage.status === "in-progress";
const isOpen =
expandAll === null ? isDefaultOpen : expandAll;
return (
<li
className="govuk-task-list__item"
key={stage.id}
>
<div className="govuk-task-list__name-and-hint">
{stage.descriptionKey && (
<details
className="govuk-details govuk-!-margin-top-2 govuk-!-margin-bottom-2"
open={isOpen}
>
<summary className="govuk-details__summary">
<span className="govuk-details__summary-text">
{t(
`case-stages:${stage.titleKey}`
)}
</span>
</summary>
<div className="govuk-details__text">
{t(
`case-stages:${stage.descriptionKey}`
)}
</div>
</details>
)}
</div>
<div
className="govuk-task-list__status "
id={`${stage.id}-status`}
>
<strong
className={
statusClassMap[stage.status] ||
"govuk-tag govuk-tag--grey govuk-!-margin-top-2"
}
>
{t(
`case-stages:${
statusTextMap[
stage.status
] || "status-not-started"
}`
)}
</strong>
</div>
</li>
);
})}
</ul>
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
...state
};
};
const mapDispatchToProps = (dispatch) => {
return {
setEventDetails: (eventsObj) => {
dispatch(setEventDetails(eventsObj));
},
setCurrentPage: (currentPage) => {
dispatch(setCurrentPage(currentPage));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(StatusDetails);