545 lines
20 KiB
JavaScript
545 lines
20 KiB
JavaScript
import useTranslation from "next-translate/useTranslation";
|
||
import Head from "next/head";
|
||
import { useRouter } from "next/router";
|
||
import Breadcrumbs from "../components/breadcrumbs";
|
||
import CookieBanner from "../components/cookieBanner";
|
||
import Footer from "../components/footer";
|
||
import Header from "../components/header";
|
||
import ServiceBanner from "../components/servicebanner";
|
||
import Link from "next/link";
|
||
import {
|
||
Field,
|
||
change,
|
||
formValueSelector,
|
||
reduxForm,
|
||
touch,
|
||
SubmissionError,
|
||
} from "redux-form";
|
||
import { connect, submitError } from "react-redux";
|
||
import { createCRMTask, sendEmail } from "../actions";
|
||
|
||
import { useState } from "react";
|
||
|
||
const RenderPickList = ({
|
||
datafieldname,
|
||
picklistData,
|
||
id,
|
||
name,
|
||
label,
|
||
input,
|
||
errorMsg,
|
||
meta: { touched, error },
|
||
}) => {
|
||
let { t } = useTranslation();
|
||
|
||
const required = (value) => {
|
||
return value || value == 0
|
||
? undefined
|
||
: t("newappeal:is-required-label");
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<div
|
||
className={
|
||
touched && error
|
||
? "govuk-form-group govuk-!-margin-bottom-1 govuk-form-group--error"
|
||
: "govuk-form-group govuk-!-margin-bottom-1"
|
||
}
|
||
>
|
||
<label className="govuk-label " htmlFor={id} id={id + "_label"}>
|
||
{label}
|
||
</label>
|
||
{touched && error && (
|
||
<span id={id + "-error"} className="govuk-error-message">
|
||
<span className="govuk-visually-hidden">Error:</span>{" "}
|
||
{error}
|
||
</span>
|
||
)}
|
||
<select
|
||
{...input}
|
||
className="govuk-select govuk-input--width-20"
|
||
id={id}
|
||
name={id}
|
||
>
|
||
<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="pcpq">
|
||
Planning casework procedure query
|
||
</option>
|
||
<option value="pi">Portal Issues</option>
|
||
<option value="appadvice">Appeal submission advice</option>
|
||
<option value="repadvice">
|
||
Representation submission advice
|
||
</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>
|
||
</>
|
||
);
|
||
};
|
||
|
||
const RenderMultiline = ({
|
||
id,
|
||
className,
|
||
rows,
|
||
cols,
|
||
datafieldname,
|
||
name,
|
||
label,
|
||
input,
|
||
errorMsg,
|
||
meta: { touched, error },
|
||
...custom
|
||
}) => {
|
||
return (
|
||
<>
|
||
<div
|
||
className={
|
||
touched && error
|
||
? "govuk-form-group govuk-form-group--error"
|
||
: "govuk-form-group "
|
||
}
|
||
>
|
||
<label className="govuk-label " htmlFor={id} id={id + "_label"}>
|
||
{label}
|
||
</label>
|
||
{touched && error && (
|
||
<span id={id + "-error"} className="govuk-error-message">
|
||
<span className="govuk-visually-hidden">Error:</span>{" "}
|
||
{errorMsg || error}
|
||
</span>
|
||
)}
|
||
<textarea
|
||
id={id}
|
||
name={id}
|
||
rows={rows}
|
||
cols={cols}
|
||
className={className}
|
||
{...input}
|
||
maxLength={
|
||
custom.hasOwnProperty("maxFieldLength")
|
||
? custom.maxFieldLength
|
||
? custom.maxFieldLength
|
||
: 800
|
||
: 800
|
||
}
|
||
></textarea>
|
||
</div>
|
||
</>
|
||
);
|
||
};
|
||
|
||
const RenderTextfield = ({
|
||
id,
|
||
className,
|
||
rows,
|
||
datafieldname,
|
||
name,
|
||
label,
|
||
input,
|
||
errorMsg,
|
||
meta: { touched, error },
|
||
...custom
|
||
}) => {
|
||
return (
|
||
<>
|
||
<div
|
||
className={
|
||
touched && error
|
||
? "govuk-form-group govuk-form-group--error"
|
||
: "govuk-form-group "
|
||
}
|
||
>
|
||
<label className="govuk-label " htmlFor={id} id={id + "_label"}>
|
||
{label}
|
||
</label>
|
||
{touched && error && (
|
||
<span id={id + "-error"} className="govuk-error-message">
|
||
<span className="govuk-visually-hidden">Error:</span>{" "}
|
||
{errorMsg || error}
|
||
</span>
|
||
)}
|
||
<input
|
||
{...input}
|
||
type={"email"}
|
||
className={className}
|
||
name={id || name}
|
||
id={id || name}
|
||
maxLength={
|
||
custom.hasOwnProperty("maxFieldLength")
|
||
? custom.maxFieldLength
|
||
? custom.maxFieldLength
|
||
: 100
|
||
: 200
|
||
}
|
||
pattern={
|
||
custom.hasOwnProperty("pattern")
|
||
? custom.pattern
|
||
: undefined
|
||
}
|
||
/>
|
||
</div>
|
||
</>
|
||
);
|
||
};
|
||
|
||
const contactPurpose = (contactValue) => {
|
||
let contactPurposeBody;
|
||
|
||
switch (contactValue) {
|
||
case "plq":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">
|
||
Questions about Planning legislation or policy
|
||
</p>
|
||
);
|
||
break;
|
||
case "ipq":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">
|
||
Questions or comments about applications for major
|
||
infrastructure projects
|
||
</p>
|
||
);
|
||
break;
|
||
case "pcpq":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">
|
||
Questions or comments about planning and environmental
|
||
appeals
|
||
</p>
|
||
);
|
||
break;
|
||
case "pi":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">
|
||
Query about using the Casework Portal
|
||
</p>
|
||
);
|
||
break;
|
||
case "appadvice":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">Advice How to submit an appeal</p>
|
||
);
|
||
break;
|
||
case "repadvice":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">
|
||
Advice How to comment on an appeal or application
|
||
</p>
|
||
);
|
||
break;
|
||
case "ceq":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">
|
||
Queries about forthcoming Hearing or Inquiry event
|
||
</p>
|
||
);
|
||
break;
|
||
case "other":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">Other enquiry for PEDW</p>
|
||
);
|
||
break;
|
||
case "postdec":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">
|
||
Issues or questions about a case after the decision is
|
||
issued
|
||
</p>
|
||
);
|
||
break;
|
||
case "comp":
|
||
contactPurposeBody = (
|
||
<p className="govuk-body">General Complaints of Compliments</p>
|
||
);
|
||
break;
|
||
default:
|
||
// code block
|
||
}
|
||
|
||
return contactPurposeBody;
|
||
};
|
||
|
||
let t;
|
||
|
||
let ContactUs = (props) => {
|
||
const { footerLinks, pages } = props;
|
||
let { t } = useTranslation();
|
||
|
||
const [formSubmitted, setFormSubmitted] = useState(false);
|
||
|
||
const router = useRouter();
|
||
const { locale } = router;
|
||
|
||
const required = (value) =>
|
||
value ? undefined : t("newappeal:is-required-label");
|
||
|
||
const emailRegex =
|
||
/^(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}|(?:\[(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:.+)])$/;
|
||
|
||
const email = (value) =>
|
||
value && !emailRegex.test(value)
|
||
? t("account:invalid-email-address-errormsg")
|
||
: undefined;
|
||
|
||
const onSubmit = async (formValues) => {
|
||
// Only called if validation passes
|
||
sendContactEmail(formValues);
|
||
createCRMTask(formValues);
|
||
formValues.typeOfContact == "plq" &&
|
||
sendPETEmail(formValues, props.petEmail);
|
||
console.log("Form submitted:", formValues);
|
||
};
|
||
|
||
const sendContactEmail = async (formValues) => {
|
||
Object.assign(formValues, {
|
||
"contactSubject": contactPurpose(formValues.typeOfContact).props
|
||
.children,
|
||
});
|
||
|
||
const reference = "PEDW-CONTACT-US";
|
||
const templateId = "69228c5f-8500-40ff-a6c5-95a9434c3ec8";
|
||
const templateIdCY = "b8f1f76d-4ab9-4a2f-b294-e3623c6095a3";
|
||
|
||
try {
|
||
const res = await sendEmail(
|
||
router.locale !== "en" ? templateIdCY : templateId,
|
||
formValues.contactEmail,
|
||
formValues,
|
||
reference
|
||
);
|
||
|
||
console.log("Email sent successfully.");
|
||
setFormSubmitted(true);
|
||
} catch (error) {
|
||
console.error("Email sending failed:", error);
|
||
alert("Something went wrong. Please try again later.");
|
||
// Required: inform redux-form this is a submit error
|
||
throw new SubmissionError({
|
||
_error: "Failed to send email. Please try again later.",
|
||
});
|
||
}
|
||
};
|
||
|
||
const sendPETEmail = async (formValues, petEmail) => {
|
||
const reference = "PEDW-CONTACT-US-PET";
|
||
const templateId = "56bbdcf9-d63d-48e9-a794-3e253dd7498b";
|
||
const templateIdCY = "00b794d3-29aa-434d-b696-0ef0b0337d79";
|
||
|
||
console.log(petEmail);
|
||
|
||
try {
|
||
const res = await sendEmail(
|
||
templateId,
|
||
petEmail,
|
||
formValues,
|
||
reference
|
||
);
|
||
|
||
console.log("Email sent successfully.");
|
||
setFormSubmitted(true);
|
||
} catch (error) {
|
||
console.error("Email sending failed:", error);
|
||
alert("Something went wrong. Please try again later.");
|
||
// Required: inform redux-form this is a submit error
|
||
throw new SubmissionError({
|
||
_error: "Failed to send email. Please try again later.",
|
||
});
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div>
|
||
<Head>
|
||
<title>{t("common:service-name")}</title>
|
||
</Head>
|
||
<div id="page_wrapper">
|
||
<CookieBanner />
|
||
<Header />
|
||
<div className="govuk-width-container">
|
||
<Breadcrumbs />
|
||
<ServiceBanner props={props} />
|
||
<main
|
||
className="govuk-main-wrapper govuk-!-padding-top-9 govuk-main-wrapper--auto-spacing"
|
||
id="main-content"
|
||
role="main"
|
||
>
|
||
<div className="govuk-grid-row">
|
||
<div className="govuk-grid-column-full">
|
||
<h2 className="govuk-heading-l govuk-!-margin-bottom-7">
|
||
Contact us{" "}
|
||
</h2>
|
||
|
||
<div className="wgForm">
|
||
{formSubmitted ? (
|
||
<div className="govuk-panel govuk-panel--confirmation">
|
||
<h1 className="govuk-panel__title">
|
||
Thank you for your enquiry
|
||
</h1>
|
||
<div className="govuk-panel__body">
|
||
We’ll get back to you as soon as
|
||
we can.
|
||
</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.{" "}
|
||
</p>
|
||
<form
|
||
onSubmit={props.handleSubmit(
|
||
onSubmit
|
||
)}
|
||
>
|
||
<Field
|
||
name={"contactEmail"}
|
||
id={"contactEmail"}
|
||
label="Your email address"
|
||
component={RenderTextfield}
|
||
validate={[required, email]}
|
||
className="govuk-input govuk-input--width-20"
|
||
type="email"
|
||
/>
|
||
<Field
|
||
name="typeOfContact"
|
||
id="typeOfContact"
|
||
component={RenderPickList}
|
||
label="What do you want to contact us about?"
|
||
validate={[required]}
|
||
/>
|
||
<div className="govuk-form-group">
|
||
{props.typeOfContactValue && (
|
||
<div>
|
||
{contactPurpose(
|
||
props.typeOfContactValue
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
<Field
|
||
name="contactbody"
|
||
id="contactbody"
|
||
component={RenderMultiline}
|
||
label="What do you want to ask?"
|
||
rows={10}
|
||
cols={80}
|
||
validate={[required]}
|
||
/>
|
||
<button
|
||
type="submit"
|
||
className="govuk-button"
|
||
>
|
||
Send your question
|
||
</button>
|
||
<button
|
||
type="button"
|
||
className="govuk-button govuk-button--secondary govuk-!-margin-left-5"
|
||
onClick={props.reset}
|
||
>
|
||
Clear form
|
||
</button>
|
||
</form>
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<Footer footerLinks={footerLinks} ticketnumber={props} />
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const selector = formValueSelector("contactUsForm"); // <-- same as form name
|
||
|
||
// ContactUs = connect((state) => {
|
||
// const typeOfContactValue = selector(state, "typeOfContact");
|
||
|
||
// return {
|
||
// typeOfContactValue,
|
||
// initialValues: {
|
||
// contactEmail:
|
||
// state.accountDetails.accountDetails?.emailaddress1 || "",
|
||
// },
|
||
// };
|
||
// })(ContactUs);
|
||
|
||
const mapDispatchToProps = (dispatch) => ({});
|
||
|
||
const mapStateToProps = (state) => {
|
||
const typeOfContactValue = selector(state, "typeOfContact");
|
||
const email = state.accountDetails.accountDetails?.emailaddress1;
|
||
|
||
return {
|
||
typeOfContactValue,
|
||
initialValues: email ? { contactEmail: email } : {},
|
||
petEmail: process.env.NEXT_PUBLIC_PETEMAIL,
|
||
};
|
||
};
|
||
|
||
export default connect(
|
||
mapStateToProps,
|
||
mapDispatchToProps
|
||
)(
|
||
reduxForm({
|
||
form: "contactUsForm",
|
||
enableReinitialize: true,
|
||
destroyOnUnmount: false,
|
||
touchOnBlur: true,
|
||
touchOnChange: true,
|
||
onSubmitFail: (errors, dispatch, subnittError, props) => {
|
||
if (errors) {
|
||
// Touch all fields with errors so validation messages appear
|
||
const errorFields = Object.keys(errors);
|
||
dispatch(touch("contactUsForm", ...errorFields));
|
||
|
||
// Scroll to first error
|
||
const firstErrorField = errorFields[0];
|
||
const errorElement = document.querySelector(
|
||
`[name="${firstErrorField}"]`
|
||
);
|
||
if (
|
||
errorElement &&
|
||
typeof errorElement.scrollIntoView === "function"
|
||
) {
|
||
errorElement.scrollIntoView({
|
||
behavior: "smooth",
|
||
block: "center",
|
||
});
|
||
errorElement.focus({ preventScroll: true });
|
||
} else {
|
||
console.warn(
|
||
"Could not find element for error field:",
|
||
firstErrorField
|
||
);
|
||
}
|
||
}
|
||
|
||
console.log("Redux Form onSubmitFail:", submitError);
|
||
},
|
||
})(ContactUs)
|
||
);
|