diff --git a/components/case/representation/representationQuestionnaires/representationLPAQuestionnaire_enforcement.js b/components/case/representation/representationQuestionnaires/representationLPAQuestionnaire_enforcement.js
index b0fc7a30..a36b3447 100644
--- a/components/case/representation/representationQuestionnaires/representationLPAQuestionnaire_enforcement.js
+++ b/components/case/representation/representationQuestionnaires/representationLPAQuestionnaire_enforcement.js
@@ -1228,7 +1228,7 @@ let Enforcement_Questionnaire = (props) => {
label={t("myrepresentations:enf-section-question-date")}
component={RenderDatePicker}
validate={[required]}
- errorMsg="Select a date"
+ errorMsg={t("newappeal:select-a-date-label")}
dateStart="0"
dateEnd="1-y"
/>
diff --git a/components/elements/index.js b/components/elements/index.js
index 800d187d..b96f491d 100644
--- a/components/elements/index.js
+++ b/components/elements/index.js
@@ -116,8 +116,6 @@ export function Textfield(props) {
maxFieldLength,
hint,
} = props;
- // const required = (value) =>
- // value ? undefined : t("newappeal:is-required-label");
const required = (value) => {
let errors;
@@ -239,7 +237,7 @@ export function Textfield(props) {
className="govuk-input govuk-input--width-20"
validate={eval(validation)}
label={props.label}
- errorMsg="Is required"
+ errorMsg={t("newappeal:is-required-label")}
maxFieldLength={props.maxFieldLength}
/>
)}
@@ -319,14 +317,14 @@ export function MultiLinefield(props) {
// Check for the required field
if (!value) {
- errors = "This field is required";
+ errors = t("newappeal:is-required-label");
} else {
// Check for emojis using a regular expression
const emojiRegex =
/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}\u{1F780}-\u{1F7FF}\u{1F800}-\u{1F8FF}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{2300}-\u{23FF}\u{2B50}\u{1F004}-\u{1F0CF}\u{1F0A0}-\u{1F0A5}\u{1F170}-\u{1F251}]/gu;
if (emojiRegex.test(value)) {
- errors = "Emojis are not allowed";
+ errors = t("newappeal:emojis-not-allowed-label");
}
}
@@ -492,7 +490,8 @@ export function RichMultiLinefield(props) {
validation,
maxFieldLength,
} = props;
- const required = (value) => (value ? undefined : "Is required");
+ const required = (value) =>
+ value ? undefined : t("newappeal:is-required-label");
var showIfHasParentShowValue =
parentField != false &&
@@ -702,6 +701,7 @@ export function DateFieldPicker(props) {
dateStart,
dateEnd,
} = props;
+ let { t } = useTranslation();
const required = (value) => (value ? undefined : "Required");
var showIfHasParentShowValue =
@@ -720,7 +720,7 @@ export function DateFieldPicker(props) {
label={label}
component={RenderDatePicker}
validate={eval(validation)}
- errorMsg="Select a date"
+ errorMsg={t("newappeal:select-a-date-label")}
dateStart={dateStart}
dateEnd={dateEnd}
hint={props.hint}
@@ -733,7 +733,7 @@ export function DateFieldPicker(props) {
label={label}
component={RenderDatePicker}
validate={eval(validation)}
- errorMsg="Select a date"
+ errorMsg={t("newappeal:select-a-date-label")}
dateStart={dateStart}
dateEnd={dateEnd}
hint={props.hint}
@@ -1318,7 +1318,9 @@ const RenderPickList = ({
dropdownObj = dropdownObj[0];
const required = (value) => {
- return value || value == 0 ? undefined : "Is required";
+ return value || value == 0
+ ? undefined
+ : t("newappeal:is-required-label");
};
return (
@@ -1363,7 +1365,9 @@ export function PickList(props) {
const { name, label, datafieldname, hint } = props;
let { t } = useTranslation();
const required = (value) => {
- return value || value == 0 ? undefined : "Is required";
+ return value || value == 0
+ ? undefined
+ : t("newappeal:is-required-label");
};
return (
);
@@ -1391,12 +1395,19 @@ export function NumericField(props) {
parentField,
maxFieldLength,
} = props;
+
+ let { t } = useTranslation();
+
const required = (value) => {
- return value || value == 0 ? undefined : "Is required";
+ return value || value == 0
+ ? undefined
+ : t("newappeal:is-required-label");
};
const isNumber = (value) => {
const regex = /^\d*\.?\d{0,1}$/;
- return regex.test(value) ? undefined : "Should be a number";
+ return regex.test(value)
+ ? undefined
+ : t("newappeal:invalid-number-label");
};
const maxLength = (max) => (value) =>
@@ -1495,11 +1506,17 @@ export function DecimalField(props) {
parentField,
maxFieldLength,
} = props;
- const required = (value) => (value ? undefined : "Is required");
+
+ let { t } = useTranslation();
+
+ const required = (value) =>
+ value ? undefined : t("newappeal:is-required-label");
const isNumber = (value) => {
const regex = /^\d*\.?\d{0,1}$/;
- return regex.test(value) ? undefined : "Should be a number or decimal";
+ return regex.test(value)
+ ? undefined
+ : t("newappeal:invalid-number-label");
};
const maxLength = (max) => (value) =>
@@ -1521,6 +1538,24 @@ export function DecimalField(props) {
//console.log("parentField:", parentField, showIfHasParentShowValue);
+ const normalizeDecimal = (value) => {
+ return value ? parseFloat(value) : value;
+ };
+
+ const validateDecimal = (value) => {
+ if (!value) return t("newappeal:is-required-label");
+
+ // Regex to match whole numbers or decimal numbers (up to 7 characters including decimal)
+ const regex = /^\d{1,6}(\.\d{1,2})?$/; // Up to 6 digits before the decimal, 2 after
+
+ // Check if the value matches the regex pattern
+ if (!regex.test(value)) {
+ return t("newappeal:invalid-decimal-label");
+ }
+
+ return undefined;
+ };
+
return (
<>
{parentField != false ? (
@@ -1557,7 +1592,7 @@ export function DecimalField(props) {
pattern="^\d*\.?\d+$"
// inputMode="numeric"
// parse={Number}
- validate={validateDecimal}
+ validate={[validateDecimal]}
component={RenderDecimalField}
label={props.label}
maxFieldLength={props.maxFieldLength}
@@ -1569,24 +1604,6 @@ export function DecimalField(props) {
);
}
-const normalizeDecimal = (value) => {
- return value ? parseFloat(value) : value;
-};
-
-const validateDecimal = (value) => {
- if (!value) return "This field is required";
-
- // Regex to match whole numbers or decimal numbers (up to 7 characters including decimal)
- const regex = /^\d{1,6}(\.\d{1,2})?$/; // Up to 6 digits before the decimal, 2 after
-
- // Check if the value matches the regex pattern
- if (!regex.test(value)) {
- return "Invalid number (max 7 characters, 2 decimal places)";
- }
-
- return undefined;
-};
-
const RenderDecimalField = ({
name,
id,
@@ -2010,7 +2027,9 @@ const RenderFileUpload = (field) => {
)}
>
)}
- {blobList.length > 0 &&
Previously added files
}
+ {blobList.length > 0 && (
+ {t("newappeal:previously-added-files")}
+ )}
{blobList.map(
(blob, i) =>
blob.documentType == field.documentTypeCode && (
diff --git a/components/newappeal/buildfield.js b/components/newappeal/buildfield.js
index 6b2e6eb8..16c5590a 100644
--- a/components/newappeal/buildfield.js
+++ b/components/newappeal/buildfield.js
@@ -96,7 +96,8 @@ export default function BuildField(props) {
);
break;
case "{4273EDBD-AC1D-40d3-9FB2-095C621B552D}":
- const required = (value) => (value ? undefined : "Is required");
+ const required = (value) =>
+ value ? undefined : t("newappeal:is-required-label");
return (
{
-
- - Application form
+ - {t("newappeal:application-form")}
{props.props.props.form.hasOwnProperty(
"appealForm"
@@ -223,7 +223,9 @@ const BuildProgress = (props) => {
.values.pinswg_caseworkreason ==
"846040005" && (
-
- Registration of Application letter
+ {t(
+ "newappeal:registration-of-application-letter"
+ )}
)
: ""}
@@ -255,7 +257,11 @@ const BuildProgress = (props) => {
].values.hasOwnProperty("pinswg_attachsoc")
? props.props.props.form["appealForm"]
.values.pinswg_attachsoc ==
- "true" && - Statement of Case
+ "true" && (
+ -
+ {t("newappeal:statement-of-case")}
+
+ )
: ""}
{props.props.props.form.hasOwnProperty(
@@ -272,7 +278,11 @@ const BuildProgress = (props) => {
? props.props.props.form["appealForm"]
.values
.pinswg_attachcostapplication ==
- "true" && - Cost Application
+ "true" && (
+ -
+ {t("newappeal:cost-application")}
+
+ )
: ""}
{props.props.props.form.hasOwnProperty(
@@ -303,7 +313,8 @@ const BuildProgress = (props) => {
return - {value}
;
}
)} */}
- - Site Location Plan
+
+ - {t("newappeal:site-location-plan")}
diff --git a/components/newappeal/buildsection.js b/components/newappeal/buildsection.js
index 57da936f..31fd9f3d 100644
--- a/components/newappeal/buildsection.js
+++ b/components/newappeal/buildsection.js
@@ -626,12 +626,14 @@ const mapStateToProps = (state, ownProps) => {
formData: state.formData,
appealType: state.appealType,
initialValues: state.appealType.caseReference.caseDetails,
+ currentView: state.currentView,
}
: {
search: state.search,
searchResultsObj: state.searchResultsObj,
formData: state.formData,
appealType: state.appealType,
+ currentView: state.currentView,
//form: state.form,
};
};
@@ -658,22 +660,28 @@ const mapDispatchToProps = (dispatch) => {
const selector = formValueSelector("appealForm"); // <-- same as form name
-const validate = (values) => {
+const validate = (values, props) => {
const errors = {};
const strippedHtml = (htmlStr) => {
return htmlStr.replace(/(<([^>]+)>)/gi, "").trim();
};
+ const locale = props.currentView.locale;
+
if (values.hasOwnProperty("pinswg_attachsoc")) {
if (values.pinswg_attachsoc == "false") {
if (values.hasOwnProperty("pinswg_statementofcase")) {
if (strippedHtml(values.pinswg_statementofcase).length == 0)
errors.pinswg_statementofcase =
- "Submit your Statement of Case or select Yes to attach a separate Statement of Case Document";
+ locale != "cy"
+ ? "Submit your Statement of Case or select Yes to attach a separate Statement of Case Document"
+ : "Cyflwynwch eich Datganiad Achos neu dewiswch Ie i atodi Dogfen Datganiad Achos ar wahân";
} else {
errors.pinswg_statementofcase =
- "Submit your Statement of Case or select Yes to attach a separate Statement of Case Document";
+ locale != "cy"
+ ? "Submit your Statement of Case or select Yes to attach a separate Statement of Case Document"
+ : "Cyflwynwch eich Datganiad Achos neu dewiswch Ie i atodi Dogfen Datganiad Achos ar wahân";
}
}
}
@@ -684,10 +692,14 @@ const validate = (values) => {
if (values.hasOwnProperty("pinswg_costapplication")) {
if (strippedHtml(values.pinswg_costapplication).length == 0)
errors.pinswg_costapplication =
- "Submit a Costs application or select Yes to attach a separate Costs application";
+ locale != "cy"
+ ? "Submit a Costs application or select Yes to attach a separate Costs application"
+ : "Cyflwyno cais Costau neu ddewis Ie i atodi cais Costau ar wahân";
} else {
errors.pinswg_costapplication =
- "Submit a Costs application or select Yes to attach a separate Costs application";
+ locale != "cy"
+ ? "Submit a Costs application or select Yes to attach a separate Costs application"
+ : "Cyflwyno cais Costau neu ddewis Ie i atodi cais Costau ar wahân";
}
}
}
diff --git a/data/formsxml/planningappeals78.xml b/data/formsxml/planningappeals78.xml
index bbc92d95..152b63bf 100644
--- a/data/formsxml/planningappeals78.xml
+++ b/data/formsxml/planningappeals78.xml
@@ -46,7 +46,7 @@
-
+
@@ -142,7 +142,7 @@
-
+
@@ -286,7 +286,7 @@
-
+
@@ -316,13 +316,13 @@
-
+
-
+
diff --git a/locales/cy/newappeal.json b/locales/cy/newappeal.json
index dd99e3ac..dee9e91f 100644
--- a/locales/cy/newappeal.json
+++ b/locales/cy/newappeal.json
@@ -16,6 +16,9 @@
"appeal-confirmation-label": "Cadarnhaf fy mod yn gwneud apêl yn erbyn penderfyniad i wrthod caniatâd cynllunio neu fethiant i benderfynu ar gais cynllunio o fewn 8 wythnos.",
"select-to-confirm-label": "Cadarnhewch i barhau",
"is-required-label": "Yn ofynnol",
+ "select-a-date-label": "Dewiswch ddyddiad",
+ "invalid-decimal-label": "Rhif annilys (uchafswm o 7 nod, 2 le degol)",
+ "invalid-number-label": "Dylai fod yn rhif",
"emojis-not-allowed-label": "Ni chaniateir emojis",
"invalid-postcode-label": "Cod post annilys",
"hascas-confirmation-label": "Cadarnhewch fod angen apêl HAS/CAS",
@@ -99,5 +102,14 @@
"new-appeal-complete-back-link": "Yn ôl i Fy Mhorth",
"new-appeal-multiline-pid": "Peidiwch â chynnwys gwybodaeth bersonol nac ariannol",
"new-appeal-statement-case-error": "Cyflwynwch eich Datganiad Achos neu dewiswch Ie i atodi Dogfen Datganiad Achos ar wahân",
- "new-appeal-costs-application-error": "Cyflwyno cais Costau neu ddewis Ie i atodi cais Costau ar wahân"
+ "new-appeal-costs-application-error": "Cyflwyno cais Costau neu ddewis Ie i atodi cais Costau ar wahân",
+ "application-form": "Ffurflen gais",
+ "description-of-development-change-confirmation": "Disgrifiad o gadarnhad newid datblygiad",
+ "relevant-ownership-certificate": "Tystysgrif Perchnogaeth Berthnasol",
+ "statement-of-case": "Datganiad Achos",
+ "cost-application": "Cais Cost",
+ "planning-obligation-(s106/unilateral)": "Rhwymedigaeth gynllunio (S106/Unochrog)",
+ "site-location-plan": "Cynllun Lleoliad Safle",
+ "registration-of-application-letter": "Llythyr Cofrestru Cais",
+ "previously-added-files": "Ffeiliau a ychwanegwyd yn flaenorol"
}
\ No newline at end of file
diff --git a/locales/en/newappeal.json b/locales/en/newappeal.json
index 68abca98..1c6b73ce 100644
--- a/locales/en/newappeal.json
+++ b/locales/en/newappeal.json
@@ -1,5 +1,6 @@
{
"viewall-link": "View all",
+ "viewall-link-count": "View all {{count}} cases",
"parent-page-title": "My portal",
"page-title": "New appeal",
"select-lpa-label": "Select your Local Planning Authority (LPA)",
@@ -15,6 +16,9 @@
"appeal-confirmation-label": "I confirm that I am making an appeal against a refusal of planning permission or failure to determine a planning application within 8 weeks.",
"select-to-confirm-label": "Confirm to continue",
"is-required-label": "Is required",
+ "select-a-date-label": "Select a date",
+ "invalid-decimal-label": "Invalid number (max 7 characters, 2 decimal places)",
+ "invalid-number-label": "Should be a number",
"emojis-not-allowed-label": "Emojis are not allowed",
"invalid-postcode-label": "Invalid postcode",
"hascas-confirmation-label": "Confirm HAS/CAS appeal is required",
@@ -98,5 +102,14 @@
"new-appeal-complete-back-link": "Back to My Portal",
"new-appeal-multiline-pid": "Do not include personal or financial information.",
"new-appeal-statement-case-error": "Submit your Statement of Case or select Yes to attach a separate Statement of Case Document",
- "new-appeal-costs-application-error": "Submit a Costs application or select Yes to attach a separate Costs application"
+ "new-appeal-costs-application-error": "Submit a Costs application or select Yes to attach a separate Costs application",
+ "application-form": "Application form",
+ "description-of-development-change-confirmation": "Description of development change confirmation",
+ "relevant-ownership-certificate": "Relevant Ownership certificate",
+ "statement-of-case": "Statement of Case",
+ "cost-application": "Cost Application",
+ "planning-obligation-(s106/unilateral)": "Planning obligation (S106/Unilateral)",
+ "site-location-plan": "Site Location Plan",
+ "registration-of-application-letter": "Registration of Application letter",
+ "previously-added-files": "Previously added files"
}
\ No newline at end of file
diff --git a/pages/myportal/[appealtypes].js b/pages/myportal/[appealtypes].js
index 432849b8..c4975bb5 100644
--- a/pages/myportal/[appealtypes].js
+++ b/pages/myportal/[appealtypes].js
@@ -44,7 +44,7 @@ import {
setAwaitingSubmissionDetails,
setAwaitingSubmissionFromBlob,
} from "../../store/awaitingSubmission/action";
-import { setCurrentView } from "../../store/currentView/action";
+import { setCurrentView, setLocale } from "../../store/currentView/action";
import { setForm } from "../../store/formData/action";
import { wrapper } from "../../store/store";
import ServiceBanner from "../../components/myportal/servicebanner";
@@ -238,6 +238,9 @@ export const getServerSideProps = wrapper.getServerSideProps(
};
}
+ console.log(ctx.locale);
+ store.dispatch(setLocale(ctx.locale));
+
let loggedInUserIdent = thisSession.user.id;
let loggedInUserEmail = thisSession.user.email;
diff --git a/pages/myportal/index.js b/pages/myportal/index.js
index b8eb5d63..03cd5f14 100644
--- a/pages/myportal/index.js
+++ b/pages/myportal/index.js
@@ -34,7 +34,7 @@ import {
setAwaitingSubmissionDetails,
setAwaitingSubmissionFromBlob,
} from "../../store/awaitingSubmission/action";
-import { setShowReps } from "../../store/currentView/action";
+import { setShowReps, setLocale } from "../../store/currentView/action";
import { setMyCases, setMyCasesDetails } from "../../store/myCases/action";
import {
setMyRepresentations,
@@ -260,6 +260,8 @@ export const getServerSideProps = wrapper.getServerSideProps(
console.log("req locale :", ctx.locale);
+ store.dispatch(setLocale(ctx.locale));
+
const preferredLocale =
accountDetails.pinswg_preferredlanguage == "846040000"
? "cy"
diff --git a/store/currentView/action.js b/store/currentView/action.js
index 95dadefb..4e263ffa 100644
--- a/store/currentView/action.js
+++ b/store/currentView/action.js
@@ -11,6 +11,7 @@ export const currentViewActionTypes = {
SETREPRESENTATIONMESSAGESENT: "SETREPRESENTATIONMESSAGESENT",
SETREPRESENTATIONRESET: "SETREPRESENTATIONRESET",
SETSHOWLOGIN: "SETSHOWLOGIN",
+ SETLOCALE: "SETLOCALE",
};
export const getCurrentViewObj = () => (dispatch) => {
@@ -102,3 +103,10 @@ export const setShowReps = (showReps, showLogin) => (dispatch) => {
showReps: showReps,
});
};
+
+export const setLocale = (locale) => (dispatch) => {
+ return dispatch({
+ type: currentViewActionTypes.SETLOCALE,
+ locale: locale,
+ });
+};
diff --git a/store/currentView/reducer.js b/store/currentView/reducer.js
index e2bff8ac..41f10da2 100644
--- a/store/currentView/reducer.js
+++ b/store/currentView/reducer.js
@@ -11,6 +11,7 @@ const currentViewInitialState = {
currentPage: 1,
showReps: false,
showLogin: false,
+ locale: "",
};
export default function reducer(state = currentViewInitialState, action) {
@@ -72,6 +73,11 @@ export default function reducer(state = currentViewInitialState, action) {
showReps: action.showReps,
showLogin: action.showLogin,
};
+ case currentViewActionTypes.SETLOCALE:
+ return {
+ ...state,
+ locale: action.locale,
+ };
default:
return state;
}