@proca/widget
Version:
Proca is an open-source campaign toolkit designed to empower activists and organisations in their digital advocacy efforts. It provides a flexible and customisable platform for creating and managing online petitions, email campaigns, and other forms of di
361 lines (332 loc) • 11.1 kB
JavaScript
import i18n from "@lib/i18n";
import Url from "@lib/urlparser";
import dispatch from "@lib/event";
import { formatDate } from "@lib/date";
import { ReactComponent as ECLogo } from "@images/logo_eu.svg";
import { ReactComponent as SecureLogo } from "@images/secure.svg";
import React, { useState, useEffect } from "react";
import {
CardHeader,
Button,
Grid,
Snackbar,
Box,
Container,
} from "@material-ui/core";
import SendIcon from "@material-ui/icons/Send";
import { useTranslation, countries } from "./hooks/useEciTranslation";
import { useForm } from "react-hook-form";
import documents from "@data/document_number_formats.json";
import { addSupport, errorMessages } from "@lib/eci/server.js";
import Reminder from "./Reminder";
import Country from "./Country";
import General from "./General";
import Address from "./Address";
import Consent from "./Consent";
import Id from "./Id";
import Captcha from "./Captcha";
import ReadMore from "./ReadMore";
import useElementWidth from "@hooks/useElementWidth";
import useData from "@hooks/useData";
import { useCampaignConfig } from "@hooks/useConfig";
import Alert from "@material-ui/lab/Alert";
import useCount from "@hooks/useCount";
import ProgressCounter from "@components/ProgressCounter";
import { makeStyles } from "@material-ui/core/styles";
//const countries = eciLocale.common.country;
const useStyles = makeStyles(theme => ({
root: {
"& .MuiInputLabel-root": {
color: "red",
},
},
container: {
display: "flex",
flexWrap: "wrap",
},
notice: {
fontSize: theme.typography.pxToRem(13),
fontWeight: "fontWeightLight",
lineHeight: "1.3em",
color: theme.palette.text.secondary,
"& a": {
color: theme.palette.text.secondary,
},
},
}));
const Support = props => {
const classes = useStyles();
const width = useElementWidth("#proca-register");
const [token, setToken] = useState({ text: "dummy", audio: false });
const [compact, setCompact] = useState(true);
const [require, setRequire] = useState(false);
const [acceptableIds, setIds] = useState({});
const [status, setStatus] = useState("default");
const [reload, setReload] = useState("false");
const [errorDetails, setErrorDetails] = useState("");
const config = useCampaignConfig();
const { t } = useTranslation(config.lang);
const [data] = useData();
const apiUrl = config.component.eci.apiUrl || process.env.REACT_APP_API_URL;
const actionpage = config.component.eci.actionpage || config.actionPage;
const progress = config.component.eci.progress;
useCount(actionpage); // TODO, make conditional to fetch the counter?
//config.component.eci.apiUrl
const form = useForm({
mode: "onBlur",
defaultValues: data,
});
const { handleSubmit, setError, clearErrors, formState, watch, setValue } =
form;
const [nationality, country, firstname] =
watch(["nationality", "country", "firstname"]) || "";
if (data.firstname && !firstname && !reload) {
setReload("true");
}
useEffect(() => {
if (reload) {
data.firstname && setValue("firstname", data.firstname);
data.lastname && setValue("lastname", data.lastname);
data.country && setValue("nationality", data.country);
data.country && setValue("country", data.country);
setReload(false);
}
}, [reload, setValue]);
useEffect(() => {
if (
nationality &&
nationality !== "ZZ" &&
documents[nationality.toLowerCase()]
) {
const ids = documents[nationality.toLowerCase()];
setIds(documents[nationality.toLowerCase()]);
clearErrors("documentNumber");
setRequire(Object.keys(ids).length ? "id" : "address");
if (!country && Object.keys(ids).length === 0) {
setValue("country", nationality);
}
}
}, [country, nationality, setValue, clearErrors]);
if ((compact && width > 450) || (!compact && width <= 450))
setCompact(width <= 450);
const onSubmit = async data => {
if (Object.keys(acceptableIds).length === 1) {
data.documentType = Object.entries(acceptableIds)[0][0];
}
//data.tracking = {};
data.tracking = Url.utm();
if (data.birthDate) {
data.birthDate = formatDate(data.birthDate);
if (data.birthDate === false) {
setError("birthDate", {
type: "format",
message: t("eci:form.error.oct_error_invaliddateformat"),
});
return;
}
}
const result = await addSupport("support", +actionpage, data, {
captcha: token,
apiUrl: apiUrl,
test: config.test,
});
if (result.errors) {
let handled = false;
if (result.errors.fields) {
result.errors.fields.forEach(field => {
if (field.name in data) {
switch (field.name) {
case "birthDate": {
const msg = "eci:form.error.oct_error_invalidrange";
setError(field.name, { type: "server", message: t(msg) });
break;
}
case "documentNumber": {
const msg = `eci:form.error.document_${data.nationality.toLowerCase()}_${data.documentType.replace(/\./g, "_")}`;
setError(field.name, {
type: "server",
message: /* i18next-extract-disable-line */ t(msg),
});
break;
}
case "postcode": {
const msg = `eci:form.error.oct_error_${data.country.toLowerCase()}_postalcode`;
setError(field.name, {
type: "server",
message: i18n.exists(msg)
? /* i18next-extract-disable-line */ t(msg)
: t("eci:form.error.oct_error_invalidsize"),
});
break;
}
default:
setError(field.name, {
type: "server",
message: field.message,
});
}
} else if (field.name.toLowerCase() in data) {
setError(field.name.toLowerCase(), {
type: "server",
message: field.message,
});
handled = true;
}
});
}
if (!handled) {
setErrorDetails(errorMessages(result.errors));
setStatus("error");
}
return;
}
if (status === "error") {
setErrorDetails("");
setStatus("default");
}
dispatch(
"eci:complete",
{
uuid: result.contactRef,
test: !!config.test,
country: data.nationality,
},
null,
config
);
props.done &&
props.done({
firstname: data.firstname,
});
return false;
};
const handleCaptcha = token => {
setToken(token);
};
function ErrorN(props) {
if (props.display)
return (
<Snackbar open={true} autoHideDuration={6000}>
<Alert severity="error">
Sorry, we could not save your signature!
<br />
Details: {errorDetails}
</Alert>
</Snackbar>
);
return null;
}
const customValidity = t("required field");
// todo, convert the OCS text into something that can use Trans
return (
<Container component="div" maxWidth="sm">
{progress && (
<ProgressCounter actionPage={config.component.eci.actionpage} />
)}
<Box marginBottom={1}>
<Grid container spacing={1}>
<Grid item xs={12}>
<form
className={classes.container}
id="proca-register"
onSubmit={handleSubmit(onSubmit)}
method="post"
action="http://localhost"
>
<ErrorN display={status === "error"} />
{i18n.exists("step.eci.secure") && (
<Grid container spacing={4}>
<Grid item xs={10}>
{t("step.eci.secure")}
</Grid>
<Grid item xs={2}>
<SecureLogo />
</Grid>
</Grid>
)}
{config.component.reminder && <Reminder done={props.done} />}
{i18n.exists("step.eci.title") && (
<CardHeader
subheader={t("step.eci.subheader", "")}
title={t("step.eci.title", "")}
/>
)}
{config.component.eci.readMore && <ReadMore />}
<Box
variant="subtitle1"
dangerouslySetInnerHTML={{
__html: t("eci:common.requirements.text", {
url: `https://eur-lex.europa.eu/legal-content/${config.lang}/TXT/PDF/?uri=CELEX:32019R0788`,
}),
}}
/>
<Country form={form} countries={countries} name="nationality" />
<General
form={form}
birthdate={require === "address"}
compact={compact}
customValidity={customValidity}
/>
{require === "address" && (
<Address
form={form}
compact={compact}
country={nationality}
countries={countries}
geocountries={config.component.eci.geocountries}
customValidity={customValidity}
/>
)}
{require === "id" && (
<Id
form={form}
compact={compact}
ids={acceptableIds}
country={nationality}
customValidity={customValidity}
/>
)}
<Grid item xs={12}>
<Consent form={form} />
</Grid>
<Grid item xs={12}>
<Captcha
form={form}
compact={compact}
onChange={captcha => handleCaptcha(captcha)}
/>
</Grid>
<Grid item xs={12}>
<Button
color="primary"
variant="contained"
fullWidth
type="submit"
size="large"
disabled={formState.isSubmitting}
endIcon={<SendIcon />}
>
{" "}
{t("eci:form.support")}
</Button>
</Grid>
<Box className={classes.notice} m={1}>
<Grid container spacing={2}>
<Grid xs={3} item>
<ECLogo />
</Grid>
<Grid xs={9} item>
<div>{t("eci:form.support-footer1")}</div>
<div>{t("eci:form.support-footer2")}</div>
<div>{t("eci:form.support-footer3")}</div>
</Grid>
</Grid>
</Box>
</form>
</Grid>
</Grid>
</Box>
</Container>
);
};
export default Support;