251 lines
8.3 KiB
JavaScript
251 lines
8.3 KiB
JavaScript
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { Button, Card, Checkbox, Col, Form, Input, Modal, Row } from "antd";
|
|
import Markdown from "react-markdown";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors";
|
|
import { connect } from "react-redux";
|
|
import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries";
|
|
import { useMutation } from "@apollo/client";
|
|
import { acceptEula } from "../../redux/user/user.actions";
|
|
import { useTranslation } from "react-i18next";
|
|
import dayjs from "../../utils/day";
|
|
|
|
import "./eula.styles.scss";
|
|
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
|
|
const Eula = ({ currentEula, currentUser, acceptEula }) => {
|
|
const [formReady, setFormReady] = useState(false);
|
|
const [hasEverScrolledToBottom, setHasEverScrolledToBottom] = useState(false);
|
|
const [insertEulaAcceptance] = useMutation(INSERT_EULA_ACCEPTANCE);
|
|
const [form] = Form.useForm();
|
|
const markdownCardRef = useRef(null);
|
|
const { t } = useTranslation();
|
|
const notification = useNotification();
|
|
|
|
const handleScroll = useCallback(
|
|
(e) => {
|
|
const bottom = e.target.scrollHeight - 100 <= e.target.scrollTop + e.target.clientHeight;
|
|
if (bottom && !hasEverScrolledToBottom) {
|
|
setHasEverScrolledToBottom(true);
|
|
} else if (e.target.scrollHeight <= e.target.clientHeight && !hasEverScrolledToBottom) {
|
|
setHasEverScrolledToBottom(true);
|
|
}
|
|
},
|
|
[hasEverScrolledToBottom, setHasEverScrolledToBottom]
|
|
);
|
|
|
|
useEffect(() => {
|
|
handleScroll({ target: markdownCardRef.current });
|
|
}, [handleScroll]);
|
|
|
|
const handleChange = useCallback(() => {
|
|
form
|
|
.validateFields({ validateOnly: true })
|
|
.then(() => setFormReady(hasEverScrolledToBottom))
|
|
.catch(() => setFormReady(false));
|
|
}, [form, hasEverScrolledToBottom]);
|
|
|
|
useEffect(() => {
|
|
handleChange();
|
|
}, [handleChange, hasEverScrolledToBottom, form]);
|
|
|
|
const onFinish = async ({ ...formValues }) => {
|
|
const eulaId = currentEula.id;
|
|
const useremail = currentUser.email;
|
|
|
|
try {
|
|
const { ...otherFormValues } = formValues;
|
|
|
|
// Trim the values of the fields before submitting
|
|
const trimmedFormValues = Object.entries(otherFormValues).reduce((acc, [key, value]) => {
|
|
acc[key] = typeof value === "string" ? value?.trim() : value;
|
|
return acc;
|
|
}, {});
|
|
|
|
await insertEulaAcceptance({
|
|
variables: {
|
|
eulaAcceptance: {
|
|
eulaid: eulaId,
|
|
useremail,
|
|
...otherFormValues,
|
|
...trimmedFormValues,
|
|
date_accepted: new Date()
|
|
}
|
|
}
|
|
});
|
|
acceptEula();
|
|
} catch (err) {
|
|
notification.error({
|
|
message: t("eula.errors.acceptance.message"),
|
|
description: t("eula.errors.acceptance.description")
|
|
});
|
|
console.log(`${t("eula.errors.acceptance.message")}`);
|
|
console.dir({
|
|
message: err.message,
|
|
stack: err.stack
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Modal
|
|
title={t("eula.titles.modal")}
|
|
className="eula-modal"
|
|
width={"100vh"}
|
|
open={currentEula}
|
|
footer={() => (
|
|
<Button
|
|
className="eula-accept-button"
|
|
form="tosForm"
|
|
type="primary"
|
|
size="large"
|
|
htmlType="submit"
|
|
disabled={!formReady}
|
|
>
|
|
{t("eula.buttons.accept")}
|
|
</Button>
|
|
)}
|
|
closable={false}
|
|
>
|
|
<Card type="inner" className="eula-markdown-card" onScroll={handleScroll} ref={markdownCardRef}>
|
|
<div id="markdowndiv" className="eula-markdown-div">
|
|
<Markdown>{currentEula?.content?.replace(/\\n|\\r|\\n\\r|\\r\\n/g, "\n")}</Markdown>
|
|
</div>
|
|
</Card>
|
|
|
|
<EulaFormComponent form={form} handleChange={handleChange} onFinish={onFinish} t={t} />
|
|
|
|
{!hasEverScrolledToBottom && (
|
|
<Card className="eula-never-scrolled" type="inner">
|
|
<h3>{t("eula.content.never_scrolled")}</h3>
|
|
</Card>
|
|
)}
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
|
|
<Card type="inner" title={t("eula.titles.upper_card")} style={{ marginTop: "10px" }}>
|
|
<Form id="tosForm" onChange={handleChange} onFinish={onFinish} form={form}>
|
|
<Row gutter={24}>
|
|
<Col span={12}>
|
|
<Form.Item
|
|
label={t("eula.labels.first_name")}
|
|
name="first_name"
|
|
rules={[
|
|
{
|
|
required: true,
|
|
validator: (_, value) =>
|
|
value?.trim() !== "" ? Promise.resolve() : Promise.reject(new Error(t("eula.messages.first_name")))
|
|
}
|
|
]}
|
|
>
|
|
<Input placeholder={t("eula.labels.first_name")} aria-label={t("eula.labels.first_name")} />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col span={12}>
|
|
<Form.Item
|
|
label={t("eula.labels.last_name")}
|
|
name="last_name"
|
|
rules={[
|
|
{
|
|
required: true,
|
|
validator: (_, value) =>
|
|
value?.trim() !== "" ? Promise.resolve() : Promise.reject(new Error(t("eula.messages.last_name")))
|
|
}
|
|
]}
|
|
>
|
|
<Input placeholder={t("eula.labels.last_name")} aria-label={t("eula.labels.last_name")} />
|
|
</Form.Item>
|
|
</Col>
|
|
</Row>
|
|
<Row gutter={24}>
|
|
<Col span={12}>
|
|
<Form.Item
|
|
label={t("eula.labels.business_name")}
|
|
name="business_name"
|
|
rules={[
|
|
{
|
|
required: true,
|
|
validator: (_, value) =>
|
|
value?.trim() !== "" ? Promise.resolve() : Promise.reject(new Error(t("eula.messages.business_name")))
|
|
}
|
|
]}
|
|
>
|
|
<Input placeholder={t("eula.labels.business_name")} aria-label={t("eula.labels.business_name")} />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col span={12}>
|
|
<Form.Item
|
|
label={t("eula.labels.phone_number")}
|
|
name="phone_number"
|
|
rules={[
|
|
{
|
|
pattern: /^(\+\d{1,2}\s?)?1?-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/,
|
|
message: t("eula.messages.phone_number")
|
|
}
|
|
]}
|
|
>
|
|
<Input placeholder={t("eula.labels.phone_number")} aria-label={t("eula.labels.phone_number")} />
|
|
</Form.Item>
|
|
</Col>
|
|
</Row>
|
|
<Row gutter={24}>
|
|
<Col span={12}>
|
|
<Form.Item label={t("eula.labels.address")} name="address">
|
|
<Input placeholder={t("eula.labels.address")} aria-label={t("eula.labels.address")} />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col span={12}>
|
|
<Form.Item
|
|
label={t("eula.labels.date_accepted")}
|
|
name="date_accepted"
|
|
rules={[
|
|
{
|
|
required: true,
|
|
validator: (_, value) => {
|
|
if (dayjs(value).isSame(dayjs(), "day")) {
|
|
return Promise.resolve();
|
|
}
|
|
return Promise.reject(new Error(t("eula.messages.date_accepted")));
|
|
}
|
|
}
|
|
]}
|
|
>
|
|
<DateTimePicker isDateOnly onChange={handleChange} onlyToday aria-label={t("eula.labels.date_accepted")} />
|
|
</Form.Item>
|
|
</Col>
|
|
</Row>
|
|
<Row gutter={24}>
|
|
<Col span={24}>
|
|
<Form.Item
|
|
name="accepted_terms"
|
|
valuePropName="checked"
|
|
rules={[
|
|
{
|
|
required: true,
|
|
validator: (_, value) =>
|
|
value ? Promise.resolve() : Promise.reject(new Error(t("eula.messages.accepted_terms")))
|
|
}
|
|
]}
|
|
>
|
|
<Checkbox aria-label={t("eula.labels.accepted_terms")}>{t("eula.labels.accepted_terms")}</Checkbox>
|
|
</Form.Item>
|
|
</Col>
|
|
</Row>
|
|
</Form>
|
|
</Card>
|
|
);
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
currentEula: selectCurrentEula,
|
|
currentUser: selectCurrentUser
|
|
});
|
|
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
acceptEula: () => dispatch(acceptEula())
|
|
});
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(Eula);
|