Merged PR 1654: welsh language contact us form and mailing list

Related work items: #16937, #16945
This commit is contained in:
Robert Bond
2025-06-03 18:05:40 +00:00
6 changed files with 307 additions and 93 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ module.exports = {
"pages": {
"*": ["common", "search"],
"/": ["common", "home", "case"],
"/contactus": ["newappeal", "account"],
"/myportal/contactus": ["newappeal", "account"],
"/contactus": ["newappeal", "account", "common"],
"/myportal/contactus": ["newappeal", "account", "common"],
"/error": ["common", "home", "myportal"],
"/furtherdetails": ["common", "home", "myportal"],
"/myportal": ["myportal", "common", "home", "case"],
+20 -1
View File
@@ -94,5 +94,24 @@
"awaitingsubmissiondetails-viewall-title": "Gweld pob apêl sy'n aros i gael ei chyflwyno",
"myrepresentations-viewall-title": "Gweld pob cynrychiolaeth sy'n aros i gael ei chyflwyno",
"watchedcases-viewall-title": "Gweld pob achos a wyliwyd",
"mySubmittedreps-viewall-title": "Gweld pob cynrychiolaeth a gyflwynwyd"
"mySubmittedreps-viewall-title": "Gweld pob cynrychiolaeth a gyflwynwyd",
"contact-us-title": "Cysylltwch â Ni",
"contact-us-email-label": "Eich cyfeiriad e-bost",
"contact-us-contact-type-label": "Beth hoffech chi gysylltu â ni amdano?",
"contact-us-contact-body-label": "Beth hoffech chi ei ofyn?",
"contact-us-send-button": "Anfonwch eich cwestiwn",
"contact-us-clear-form-button": "Clirio'r ffurflen",
"contact-us-thank-you-title": "Diolch am eich ymholiad",
"contact-us-thank-you-sub": "Byddwn yn cysylltu â chi cyn gynted â phosibl.",
"contact-us-para": "Mae'r wybodaeth hon yn ddilys, ac mae'n rhaid i chi ddilyn eich ymholiad. Nid oes ganddyn nhw unrhyw beth i'w wneud, ond maen nhw wedi cael eu ffafrio'n rhydd. Mae'n anodd dweud dim byd arall wrthych. Os nad ydych chi'n gwybod, maen nhw'n byw bywyd. lacinia, enim tortor bibendum lorem, ut tincidunt lacus sapien quis lorem. Aenean interdum dolor augue, ac cursus lectus suscipit vitae.",
"contact-us-select-plq": "Ymholiad deddfwriaeth gynllunio",
"contact-us-select-ipq": "Ymholiad gweithdrefn seilwaith",
"contact-us-select-pcpq": "Ymholiad gweithdrefn gwaith achos cynllunio",
"contact-us-select-pi": "Materion Porth",
"contact-us-select-appadvice": "Cyngor cyflwyno apêl",
"contact-us-select-repadvice": "Cyngor cyflwyno sylwadau",
"contact-us-select-ceq": "Ymholiad Digwyddiadau Gwaith Achos",
"contact-us-select-other": "Unrhyw Ymholiadau Eraill",
"contact-us-select-postdec": "Ar ôl Penderfyniad",
"contact-us-select-comp": "Cwynion/Canmoliaethau"
}
+20 -1
View File
@@ -94,5 +94,24 @@
"awaitingsubmissiondetails-viewall-title": "View all appeals awaiting submission",
"myrepresentations-viewall-title": "View all representations awaiting submission",
"watchedcases-viewall-title": "View all watched cases",
"mysubmittedreps-viewall-title": "View all submitted representations"
"mysubmittedreps-viewall-title": "View all submitted representations",
"contact-us-title": "Contact Us",
"contact-us-email-label": "Your email address",
"contact-us-contact-type-label": "What do you want to contact us about?",
"contact-us-contact-body-label": "What do you want to ask?",
"contact-us-send-button": "Send your question",
"contact-us-clear-form-button": "Clear form",
"contact-us-thank-you-title": "Thank you for your enquiry",
"contact-us-thank-you-sub": "Well get back to you as soon as we can.",
"contact-us-para": "Lorem ipsum dolor sit amet,consectetur adipiscing elit.Nulla congue leo ac quam pulvinar, id vulputate libero volutpat. Maecenas ut odio id nisl laoreet dictum. Nam faucibus, leo vitae egestas lacinia, enim tortor bibendum lorem, ut tincidunt lacus sapien quis lorem. Aenean interdum dolor augue, ac cursus lectus suscipit vitae.",
"contact-us-select-plq": "Planning legislation query",
"contact-us-select-ipq": "Infrastructure procedure query",
"contact-us-select-pcpq": "Planning casework procedure query",
"contact-us-select-pi": "Portal Issues",
"contact-us-select-appadvice": "Appeal submission advice",
"contact-us-select-repadvice": "Representation submission advice",
"contact-us-select-ceq": "Casework Events Query",
"contact-us-select-other": "Any Other Enquiries",
"contact-us-select-postdec": "Post Decision",
"contact-us-select-comp": "Complaints/Complements"
}
+168 -26
View File
@@ -42,6 +42,8 @@ const flattenWatchlistEntry = (entry) => {
flattened.contact_email = entry.pinswg_Contact.emailaddress1;
flattened.firstname = entry.pinswg_Contact.firstname;
flattened.lastname = entry.pinswg_Contact.lastname;
flattened.pinswg_preferredlanguage =
entry.pinswg_Contact.pinswg_preferredlanguage;
delete flattened.pinswg_Contact;
}
return flattened;
@@ -52,46 +54,125 @@ const buildNotifyPayloads = (watchlistByEmail) => {
const reference = "PEDW-CASE-UPDATES";
const templateId = "217f7f96-2f8a-4034-a27f-9ffcf8d89edb";
const formatDocuments = (docs = []) =>
docs
const templateIdCY = "09d5cece-65d3-4717-84cd-edab99c8870c";
// const formatDocuments = (docs = []) =>
// docs
// .map((doc) => {
// const ref = doc.pinswg_isharedocumentreference || "No Ref";
// const name = doc.pinswg_name || "Untitled";
// const date =
// formatDates(
// doc.pinswg_documentpublisheddate?.split("T")[0]
// ) || "No Date";
// const link =
// process.env.NEXTAUTH_URL + doc.pinswg_hashlink ||
// "[No Link]";
// return ` - ${name} (${date})\n Link: ${link}`;
// })
// .join("\n");
const formatDocuments = (docs = [], language = "en") => {
// Labels based on language
const labels = {
en: {
noRef: "No Ref",
untitled: "Untitled",
noDate: "No Date",
link: "Link",
},
cy: {
noRef: "Dim Cyfeirnod",
untitled: "Heb Deitl",
noDate: "Dim Dyddiad",
link: "Dolen",
},
};
const t = labels[language] || labels.en;
return docs
.map((doc) => {
const ref = doc.pinswg_isharedocumentreference || "No Ref";
const name = doc.pinswg_name || "Untitled";
const ref = doc.pinswg_isharedocumentreference || t.noRef;
const name = doc.pinswg_name || t.untitled;
const date =
formatDates(
doc.pinswg_documentpublisheddate?.split("T")[0]
) || "No Date";
doc.pinswg_documentpublisheddate?.split("T")[0],
language
) || t.noDate;
const link =
process.env.NEXTAUTH_URL + doc.pinswg_hashlink ||
(process.env.NEXTAUTH_URL || "") + doc.pinswg_hashlink ||
"[No Link]";
return ` - ${name} (${date})\n Link: ${link}`;
return ` - ${name} (${date})\n ${t.link}: ${link}`;
})
.join("\n");
};
const formatSipEvents = (events = []) =>
events
const formatSipEvents = (events = [], language = "en") => {
const labels = {
en: {
unnamed: "Unnamed Event",
noDate: "No Date",
on: "on",
},
cy: {
unnamed: "Digwyddiad Heb Enw",
noDate: "Dim Dyddiad",
on: "ar",
},
};
const t = labels[language] || labels.en;
return events
.map((event) => {
const name = event.pinswg_name || "Unnamed Event";
const name = event.pinswg_name || t.unnamed;
const date =
formatDates(
event.pinswg_dateeventrequested?.split("T")[0]
) || "No Date";
return ` - ${name} on ${date}`;
event.pinswg_dateeventrequested?.split("T")[0],
language
) || t.noDate;
return ` - ${name} ${t.on} ${date}`;
})
.join("\n");
};
const formatReps = (reps = []) =>
reps
const formatReps = (reps = [], language = "en") => {
const labels = {
en: {
open: "Open",
to: "to",
close: "Close",
na: "N/A",
},
cy: {
open: "Agor",
to: "i",
close: "Cau",
na: "Dim ar gael",
},
};
const t = labels[language] || labels.en;
return reps
.map((rep) => {
const start =
formatDates(rep.pinswg_consultationopen?.split("T")[0]) ||
"N/A";
formatDates(
rep.pinswg_consultationopen?.split("T")[0],
language
) || t.na;
const end =
formatDates(rep.pinswg_consultationclose?.split("T")[0]) ||
"N/A";
return ` - Open: ${start}to Close: ${end}`;
formatDates(
rep.pinswg_consultationclose?.split("T")[0],
language
) || t.na;
return ` - ${t.open}: ${start} ${t.to} ${t.close}: ${end}`;
})
.join("\n");
};
const extractNameFromEmail = (email) =>
email
@@ -115,6 +196,10 @@ const buildNotifyPayloads = (watchlistByEmail) => {
);
if (filteredEntries.length === 0) return null;
const prefLanguage = entries[0].pinswg_preferredlanguage;
console.log("Language to use for email tmeplace:", prefLanguage);
const personalisation = {
contact_name: contactName,
case_sections: filteredEntries
@@ -126,7 +211,8 @@ const buildNotifyPayloads = (watchlistByEmail) => {
const docSection = entry.documents?.length
? `Documents added or updated:\n${formatDocuments(
entry.documents
entry.documents,
"en"
)}\n`
: "";
const sipSection = entry.sipEvents?.length
@@ -159,13 +245,67 @@ const buildNotifyPayloads = (watchlistByEmail) => {
}`,
};
const personalisationCY = {
contact_name: contactName,
case_sections: filteredEntries
.map((entry) => {
const caseRef =
entry[
"_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue"
] || "Achos Anhysbys";
const docSection = entry.documents?.length
? `Dogfennau wedi'u hychwanegu neu eu diweddaru:\n${formatDocuments(
entry.documents,
"cy"
)}\n`
: "";
const sipSection = entry.sipEvents?.length
? `Digwyddiadau SIP wedi'u creu:\n${formatSipEvents(
entry.sipEvents,
"cy"
)}\n`
: "";
const repsSection = entry.repsPeriods?.length
? `Cyfnodau Ymgynghori:\n${formatReps(
entry.repsPeriods,
"cy"
)}\n`
: "";
const caseUnsubscribelink = `Dad-danysgrifio i ddiweddariadau ar yr achos hwn ${process.env.NEXTAUTH_URL}/unsubscribe/${entry.pinswg_watchlistid}`;
return `Achos: ${caseRef}\nDolen i'r achos: ${
process.env.NEXTAUTH_URL
}/case/${caseRef}\n\n${[
docSection,
sipSection,
repsSection,
caseUnsubscribelink,
]
.filter(Boolean)
.join("\n")}`;
})
.join("\n\n"),
unsubscribeLink: `Dad-danysgrifiwch drwy ddewis y ddolen hon os nad ydych chi eisiau derbyn unrhyw ddiweddariadau mwyach ${
process.env.NEXTAUTH_URL + "/unsubscribeall/" + contactId
}`,
};
return {
email_address: email,
template_id: templateId,
personalisation,
template_id:
prefLanguage == 846040001 ? templateId : templateIdCY,
personalisation:
prefLanguage == 846040001
? personalisation
: personalisationCY,
reference,
oneClickUnsubscribeURL:
process.env.NEXTAUTH_URL + "/unsubscribeall/" + contactId,
process.env.NEXTAUTH_URL +
(prefLanguage == 846040001 ? "en" : "cy") +
"/unsubscribeall/" +
contactId,
};
})
.filter(Boolean); // Remove any nulls from filtered-out users
@@ -176,7 +316,7 @@ export default async function CombinedApiProxy(req, res) {
const token = await getToken();
// Step 1: Fetch all watchlist entries
const watchlistQuery = `pinswg_watchlists?$count=true&$filter=pinswg_emailnotifications ne null&$expand=pinswg_Contact($select=contactid,emailaddress1,firstname,lastname)&$select=pinswg_emailnotifications,_pinswg_watchedcase_value`;
const watchlistQuery = `pinswg_watchlists?$count=true&$filter=pinswg_emailnotifications ne null&$expand=pinswg_Contact($select=contactid,emailaddress1,firstname,lastname,pinswg_preferredlanguage)&$select=pinswg_emailnotifications,_pinswg_watchedcase_value`;
const watchlistRes = await axios.get(
WEBAPI_URL + watchlistQuery + hashAPIPath(watchlistQuery),
azureHeaders(token.access_token)
@@ -287,6 +427,8 @@ export default async function CombinedApiProxy(req, res) {
email: payload.email_address,
reference: payload.reference,
template_id: payload.template_id,
language:
payload.pinswg_preferredlanguage == 846040001 ? "en" : "cy",
};
try {
+49 -32
View File
@@ -65,21 +65,36 @@ const RenderPickList = ({
<option value="" disabled defaultValue>
{t("newappeal:select-default")}
</option>
<option value="plq">Planning legislation query</option>
<option value="ipq">Infrastructure procedure query</option>
<option value="plq">
{t("common:contact-us-select-plq")}
</option>
<option value="ipq">
{t("common:contact-us-select-ipq")}
</option>
<option value="pcpq">
Planning casework procedure query
{t("common:contact-us-select-pcpq")}
</option>
<option value="pi">
{t("common:contact-us-select-pi")}
</option>
<option value="appadvice">
{t("common:contact-us-select-appadvice")}
</option>
<option value="pi">Portal Issues</option>
<option value="appadvice">Appeal submission advice</option>
<option value="repadvice">
Representation submission advice
{t("common:contact-us-select-repadvice")}
</option>
<option value="ceq">
{t("common:contact-us-select-ceq")}
</option>
<option value="other">
{t("common:contact-us-select-other")}
</option>
<option value="postdec">
{t("common:contact-us-select-postdec")}
</option>
<option value="comp">
{t("common:contact-us-select-comp")}
</option>
<option value="ceq">Casework Events Query</option>
<option value="other">Any Other Enquiries</option>
<option value="postdec">Post Decision</option>
<option value="comp">Complaints/Complements</option>
</select>
</div>
</>
@@ -376,35 +391,27 @@ let ContactUs = (props) => {
<div className="govuk-grid-row">
<div className="govuk-grid-column-full">
<h2 className="govuk-heading-l govuk-!-margin-bottom-7">
Contact us{" "}
{t("common:contact-us-title")}
</h2>
<div className="wgForm">
{formSubmitted ? (
<div className="govuk-panel govuk-panel--confirmation">
<h1 className="govuk-panel__title">
Thank you for your enquiry
{t(
"common:contact-us-thank-you-title"
)}
</h1>
<div className="govuk-panel__body">
Well get back to you as soon as
we can.
{t(
"common:contact-us-thank-you-sub"
)}
</div>
</div>
) : (
<>
<p className="govuk-body">
Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Nulla congue leo ac quam
pulvinar, id vulputate libero
volutpat. Maecenas ut odio id
nisl laoreet dictum. Nam
faucibus, leo vitae egestas
lacinia, enim tortor bibendum
lorem, ut tincidunt lacus sapien
quis lorem. Aenean interdum
dolor augue, ac cursus lectus
suscipit vitae.{" "}
{t("common:contact-us-para")}
</p>
<form
onSubmit={props.handleSubmit(
@@ -414,7 +421,9 @@ let ContactUs = (props) => {
<Field
name={"contactEmail"}
id={"contactEmail"}
label="Your email address"
label={t(
"common:contact-us-email-label"
)}
component={RenderTextfield}
validate={[required, email]}
className="govuk-input govuk-input--width-20"
@@ -424,7 +433,9 @@ let ContactUs = (props) => {
name="typeOfContact"
id="typeOfContact"
component={RenderPickList}
label="What do you want to contact us about?"
label={t(
"common:contact-us-contact-type-label"
)}
validate={[required]}
/>
<div className="govuk-form-group">
@@ -440,7 +451,9 @@ let ContactUs = (props) => {
name="contactbody"
id="contactbody"
component={RenderMultiline}
label="What do you want to ask?"
label={t(
"common:contact-us-contact-body-label"
)}
rows={10}
cols={80}
validate={[required]}
@@ -449,14 +462,18 @@ let ContactUs = (props) => {
type="submit"
className="govuk-button"
>
Send your question
{t(
"common:contact-us-send-button"
)}
</button>
<button
type="button"
className="govuk-button govuk-button--secondary govuk-!-margin-left-5"
onClick={props.reset}
>
Clear form
{t(
"common:contact-us-clear-form-button"
)}
</button>
</form>
</>
+48 -31
View File
@@ -65,21 +65,36 @@ const RenderPickList = ({
<option value="" disabled defaultValue>
{t("newappeal:select-default")}
</option>
<option value="plq">Planning legislation query</option>
<option value="ipq">Infrastructure procedure query</option>
<option value="plq">
{t("common:contact-us-select-plq")}
</option>
<option value="ipq">
{t("common:contact-us-select-ipq")}
</option>
<option value="pcpq">
Planning casework procedure query
{t("common:contact-us-select-pcpq")}
</option>
<option value="pi">
{t("common:contact-us-select-pi")}
</option>
<option value="appadvice">
{t("common:contact-us-select-appadvice")}
</option>
<option value="pi">Portal Issues</option>
<option value="appadvice">Appeal submission advice</option>
<option value="repadvice">
Representation submission advice
{t("common:contact-us-select-repadvice")}
</option>
<option value="ceq">
{t("common:contact-us-select-ceq")}
</option>
<option value="other">
{t("common:contact-us-select-other")}
</option>
<option value="postdec">
{t("common:contact-us-select-postdec")}
</option>
<option value="comp">
{t("common:contact-us-select-comp")}
</option>
<option value="ceq">Casework Events Query</option>
<option value="other">Any Other Enquiries</option>
<option value="postdec">Post Decision</option>
<option value="comp">Complaints/Complements</option>
</select>
</div>
</>
@@ -383,28 +398,20 @@ let ContactUs = (props) => {
{formSubmitted ? (
<div className="govuk-panel govuk-panel--confirmation">
<h1 className="govuk-panel__title">
Thank you for your enquiry
{t(
"common:contact-us-thank-you-title"
)}
</h1>
<div className="govuk-panel__body">
Well get back to you as soon as
we can.
{t(
"common:contact-us-thank-you-sub"
)}
</div>
</div>
) : (
<>
<p className="govuk-body">
Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Nulla congue leo ac quam
pulvinar, id vulputate libero
volutpat. Maecenas ut odio id
nisl laoreet dictum. Nam
faucibus, leo vitae egestas
lacinia, enim tortor bibendum
lorem, ut tincidunt lacus sapien
quis lorem. Aenean interdum
dolor augue, ac cursus lectus
suscipit vitae.{" "}
{t("common:contact-us-para")}
</p>
<form
onSubmit={props.handleSubmit(
@@ -414,7 +421,9 @@ let ContactUs = (props) => {
<Field
name={"contactEmail"}
id={"contactEmail"}
label="Your email address"
label={t(
"common:contact-us-email-label"
)}
component={RenderTextfield}
validate={[required, email]}
className="govuk-input govuk-input--width-20"
@@ -424,7 +433,9 @@ let ContactUs = (props) => {
name="typeOfContact"
id="typeOfContact"
component={RenderPickList}
label="What do you want to contact us about?"
label={t(
"common:contact-us-contact-type-label"
)}
validate={[required]}
/>
<div className="govuk-form-group">
@@ -440,7 +451,9 @@ let ContactUs = (props) => {
name="contactbody"
id="contactbody"
component={RenderMultiline}
label="What do you want to ask?"
label={t(
"common:contact-us-contact-body-label"
)}
rows={10}
cols={80}
validate={[required]}
@@ -449,14 +462,18 @@ let ContactUs = (props) => {
type="submit"
className="govuk-button"
>
Send your question
{t(
"common:contact-us-send-button"
)}
</button>
<button
type="button"
className="govuk-button govuk-button--secondary govuk-!-margin-left-5"
onClick={props.reset}
>
Clear form
{t(
"common:contact-us-clear-form-button"
)}
</button>
</form>
</>