diff --git a/components/case/representation/elements/RepresentationActionButtons.js b/components/case/representation/elements/RepresentationActionButtons.js
new file mode 100644
index 00000000..8bc5ee9c
--- /dev/null
+++ b/components/case/representation/elements/RepresentationActionButtons.js
@@ -0,0 +1,42 @@
+const RepresentationActionButtons = ({
+ t,
+ locale,
+ isSaving,
+ isContinueDisabled,
+ onSaveExit
+}) => {
+ return (
+
+
+
+
+ {isSaving ? (
+
+ {locale == "cy" ? "Nôl data" : "Saving data"}
+
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default RepresentationActionButtons;
diff --git a/components/case/representation/elements/RepresentationTypeSelectorBlock.js b/components/case/representation/elements/RepresentationTypeSelectorBlock.js
new file mode 100644
index 00000000..38bde62b
--- /dev/null
+++ b/components/case/representation/elements/RepresentationTypeSelectorBlock.js
@@ -0,0 +1,49 @@
+import { Field } from "redux-form";
+
+import { RenderPickList } from "../representationElements";
+
+const RepresentationTypeSelectorBlock = ({
+ t,
+ headingText,
+ kindLabelText,
+ isTypeSelected,
+ selectedTypeDisplay,
+ optionsArr,
+ validate,
+ errorMsg
+}) => {
+ return (
+
+
+ {!isTypeSelected ? (
+ <>
+
{headingText}
+
+
+
+
+ >
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default RepresentationTypeSelectorBlock;
diff --git a/components/case/representation/representationAgent.js b/components/case/representation/representationAgent.js
index 14368697..6773b6aa 100644
--- a/components/case/representation/representationAgent.js
+++ b/components/case/representation/representationAgent.js
@@ -8,16 +8,17 @@ import { bytesToSize, getThumbnailIconByExtension } from "../../utils";
import {
FileUploadField,
RenderCondtionalRadioList,
- RenderPickList,
RenderRichMultiline,
- shouldDisableButton,
+ shouldDisableButton
} from "./representationElements";
+import RepresentationActionButtons from "./elements/RepresentationActionButtons";
+import RepresentationTypeSelectorBlock from "./elements/RepresentationTypeSelectorBlock";
import { useDropzone } from "react-dropzone";
import {
setRepresentationCapacity,
setRepresentationSubmit,
- setFilesForRepresentations,
+ setFilesForRepresentations
} from "../../../store/currentView/action";
const RepAgent = (props) => {
@@ -35,7 +36,7 @@ const RepAgent = (props) => {
updateRepresentation,
setSavingStatus,
savingStatus,
- setFilesForRepresentations,
+ setFilesForRepresentations
} = props;
const { acceptedFiles, getRootProps, getInputProps } = useDropzone();
@@ -63,58 +64,44 @@ const RepAgent = (props) => {
));
const required = (value) => (value ? undefined : "Required");
+ const hasTypeSelectionInline =
+ typeof formObj.representationForm.values.representationType ==
+ "undefined" && props.representationType != "Select...";
+
+ const selectedRepresentationTypeDisplay =
+ router.locale == "cy"
+ ? formObj.representationForm.values.representationType ==
+ "Questionnaire"
+ ? "Holiadur"
+ : formObj.representationForm.values.representationType ==
+ "Statement"
+ ? "Datganiad"
+ : formObj.representationForm.values.representationType ==
+ "Final comments"
+ ? "Sylwadau terfynol"
+ : formObj.representationForm.values.representationType
+ : formObj.representationForm.values.representationType;
+
+ const handleSaveExit = () => {
+ let valuesObj = props.props.props.form[props.form].values || {};
+
+ delete valuesObj["_pinswg_appellant_value"];
+
+ updateRepresentation(valuesObj, false, true);
+ };
+
return (
<>
-
-
- {typeof formObj.representationForm.values
- .representationType == "undefined" &&
- props.representationType != "Select..." ? (
- <>
-
- {t(
- "myrepresentations:representation-from-an-agent-heading"
- )}
-
-
-
-
-
- >
- ) : (
-
- )}
-
-
+
{(typeof props.representationType != "undefined" ||
typeof formObj.representationForm.values
.representationType != "undefined") && (
@@ -242,62 +229,16 @@ const RepAgent = (props) => {
)}
{typeof formObj.representationForm.values.representationType !=
"undefined" && (
-
-
-
-
- {savingStatus ? (
-
- {router.locale == "cy"
- ? "Nôl data"
- : "Saving data"}
-
-
- ) : (
-
- )}
-
-
+
)}
>
);
@@ -310,7 +251,7 @@ const mapDispatchToProps = (dispatch) => ({
setFilesForRepresentations: (fileList) => {
dispatch(setFilesForRepresentations(fileList));
- },
+ }
});
export default connect(
@@ -320,6 +261,6 @@ export default connect(
reduxForm({
form: "representationForm",
enableReinitialize: true,
- destroyOnUnmount: false,
+ destroyOnUnmount: false
})(RepAgent)
);
diff --git a/components/case/representation/representationAppellant.js b/components/case/representation/representationAppellant.js
index 59b51e25..290697f2 100644
--- a/components/case/representation/representationAppellant.js
+++ b/components/case/representation/representationAppellant.js
@@ -9,14 +9,16 @@ import {
FileUploadField,
RenderPickList,
RenderRichMultiline,
- shouldDisableButton,
+ shouldDisableButton
} from "./representationElements";
+import RepresentationActionButtons from "./elements/RepresentationActionButtons";
+import RepresentationTypeSelectorBlock from "./elements/RepresentationTypeSelectorBlock";
import { useDropzone } from "react-dropzone";
import {
setRepresentationCapacity,
setRepresentationSubmit,
- setFilesForRepresentations,
+ setFilesForRepresentations
} from "../../../store/currentView/action";
const RepAppellant = (props) => {
@@ -34,7 +36,7 @@ const RepAppellant = (props) => {
updateRepresentation,
setSavingStatus,
savingStatus,
- setFilesForRepresentations,
+ setFilesForRepresentations
} = props;
const { acceptedFiles, getRootProps, getInputProps } = useDropzone();
@@ -62,56 +64,44 @@ const RepAppellant = (props) => {
index === self.findIndex((t) => t.name === value.name)
);
+ const hasTypeSelectionInline =
+ typeof formObj.representationForm.values.representationType ==
+ "undefined" && props.representationType != "Select...";
+
+ const selectedRepresentationTypeDisplay =
+ router.locale == "cy"
+ ? formObj.representationForm.values.representationType ==
+ "Questionnaire"
+ ? "Holiadur"
+ : formObj.representationForm.values.representationType ==
+ "Statement"
+ ? "Datganiad"
+ : formObj.representationForm.values.representationType ==
+ "Final comments"
+ ? "Sylwadau terfynol"
+ : formObj.representationForm.values.representationType
+ : formObj.representationForm.values.representationType;
+
+ const handleSaveExit = () => {
+ let valuesObj = props.props.props.form[props.form].values || {};
+
+ delete valuesObj["_pinswg_appellant_value"];
+
+ updateRepresentation(valuesObj, false, true);
+ };
+
return (
<>
-
-
- {typeof formObj.representationForm.values
- .representationType == "undefined" &&
- props.representationType != "Select..." ? (
- <>
-
- {t(
- "myrepresentations:representation-from-an-appellant-heading"
- )}
-
-
-
- >
- ) : (
-
- )}
-
-
+
{(typeof props.representationType != "undefined" ||
typeof formObj.representationForm.values
.representationType != "undefined") && (
@@ -232,62 +222,16 @@ const RepAppellant = (props) => {
{typeof formObj.representationForm.values.representationType !=
"undefined" && (
-
-
-
-
- {savingStatus ? (
-
- {router.locale == "cy"
- ? "Nôl data"
- : "Saving data"}
-
-
- ) : (
-
- )}
-
-
+
)}
>
);
@@ -303,7 +247,7 @@ const mapDispatchToProps = (dispatch) => {
setFilesForRepresentations: (fileList) => {
dispatch(setFilesForRepresentations(fileList));
- },
+ }
};
};
@@ -314,6 +258,6 @@ export default connect(
reduxForm({
form: "representationForm",
enableReinitialize: true,
- destroyOnUnmount: false,
+ destroyOnUnmount: false
})(RepAppellant)
);
diff --git a/components/case/representation/representationInterestedPartyPerson.js b/components/case/representation/representationInterestedPartyPerson.js
index 63c21ab7..4f55b433 100644
--- a/components/case/representation/representationInterestedPartyPerson.js
+++ b/components/case/representation/representationInterestedPartyPerson.js
@@ -8,10 +8,11 @@ import { bytesToSize, getThumbnailIconByExtension } from "../../utils";
import {
FileUploadField,
RenderCondtionalRadioList,
- RenderPickList,
RenderRichMultiline,
shouldDisableButton
} from "./representationElements";
+import RepresentationActionButtons from "./elements/RepresentationActionButtons";
+import RepresentationTypeSelectorBlock from "./elements/RepresentationTypeSelectorBlock";
import { useDropzone } from "react-dropzone";
import {
@@ -62,63 +63,47 @@ const RepInterestedPartyPerson = (props) => {
{file.path} - {file.size} bytes
));
+
+ const hasTypeSelectionInline =
+ typeof formObj.representationForm.values.representationType ==
+ "undefined" && props.representationType != "Select...";
+
+ const selectedRepresentationTypeDisplay =
+ router.locale == "cy"
+ ? formObj.representationForm.values.representationType ==
+ "Questionnaire"
+ ? "Holiadur"
+ : formObj.representationForm.values.representationType ==
+ "Statement"
+ ? "Datganiad"
+ : formObj.representationForm.values.representationType ==
+ "Final comments"
+ ? "Sylwadau terfynol"
+ : formObj.representationForm.values.representationType
+ : formObj.representationForm.values.representationType;
+
+ const handleSaveExit = () => {
+ let valuesObj = props.props.props.form[props.form].values || {};
+
+ delete valuesObj["_pinswg_appellant_value"];
+
+ updateRepresentation(valuesObj, false, true);
+ };
+
return (
<>
-
-
- {typeof formObj.representationForm.values
- .representationType == "undefined" &&
- props.representationType != "Select..." ? (
- <>
-
- {t(
- "myrepresentations:consultation-from-an-interested-person-heading"
- )}
-
-
-
-
-
- >
- ) : (
-
- )}
-
-
+
{(typeof props.representationType != "undefined" ||
typeof formObj.representationForm.values
.representationType != "undefined") && (
@@ -235,80 +220,16 @@ const RepInterestedPartyPerson = (props) => {
)}
{typeof formObj.representationForm.values.representationType !=
"undefined" && (
-
+
)}
>
);
diff --git a/context/representations-refactor-tracker.md b/context/representations-refactor-tracker.md
index da70991a..0e48d46a 100644
--- a/context/representations-refactor-tracker.md
+++ b/context/representations-refactor-tracker.md
@@ -6,8 +6,8 @@ Base branch: `refactor`
## Status
-Current slice: Slice R3 — Journey Step Resolution Extraction
-Status: IMPLEMENTED (manual validation pending)
+Current slice: Slice R5 — Representation Elements Normalisation
+Status: COMPLETE
---
@@ -146,6 +146,28 @@ Clean up shared UI helpers and duplicated patterns.
- file upload handling (structure only, not behaviour)
- shared UI logic extraction
+**Completion notes (this slice):**
+
+- Implemented on feature branch created from `refactor`: `rep-slice-r5-elements-normalisation`.
+- Added render-only shared fragments under `components/case/representation/elements/`:
+ - `RepresentationTypeSelectorBlock.js`
+ - `RepresentationActionButtons.js`
+- Integrated these fragments into main representation variants only:
+ - `representationAppellant.js`
+ - `representationAgent.js`
+ - `representationInterestedPartyPerson.js`
+- Preserved scope constraints for Slice R5:
+ - kept parent components as logic owners
+ - no submission/update side-effect refactor
+ - no upload service behaviour changes
+ - no payload/route/query/Redux/validation business-rule changes
+ - consultation variants intentionally not normalised in this slice
+- Validation evidence:
+ - `npm run lint` completed (warnings only, no new errors).
+ - `npm run test:reps` completed: **7 passed**.
+ - Manual APP/IP/Agent/LPA checks: **passed**.
+ - Manual EN/CY parity checks: **passed**.
+
---
### Slice R6 — Async Data / Service Layer Cleanup