230 lines
8.1 KiB
JavaScript
230 lines
8.1 KiB
JavaScript
import { useMutation, useQuery } from "@apollo/client";
|
|
import { useEffect, useState } from "react";
|
|
import { Alert, Button, Card, Checkbox, Divider, Form, Space, Switch, Table, Typography } from "antd";
|
|
import { useTranslation } from "react-i18next";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
|
import AlertComponent from "../alert/alert.component";
|
|
import {
|
|
QUERY_NOTIFICATION_SETTINGS,
|
|
UPDATE_NOTIFICATION_SETTINGS,
|
|
UPDATE_NOTIFICATIONS_AUTOADD
|
|
} from "../../graphql/user.queries.js";
|
|
import { notificationScenarios } from "../../utils/jobNotificationScenarios.js";
|
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component.jsx";
|
|
import PropTypes from "prop-types";
|
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
import ColumnHeaderCheckbox from "../notification-settings/column-header-checkbox.component.jsx";
|
|
import { useIsEmployee } from "../../utils/useIsEmployee.js";
|
|
|
|
/**
|
|
* Notifications Settings Form
|
|
* @param currentUser
|
|
* @param bodyshop
|
|
* @returns {JSX.Element}
|
|
* @constructor
|
|
*/
|
|
const NotificationSettingsForm = ({ currentUser, bodyshop }) => {
|
|
const { t } = useTranslation();
|
|
const [form] = Form.useForm();
|
|
const [initialValues, setInitialValues] = useState({});
|
|
const [isDirty, setIsDirty] = useState(false);
|
|
const [autoAddEnabled, setAutoAddEnabled] = useState(false);
|
|
const [initialAutoAdd, setInitialAutoAdd] = useState(false);
|
|
const notification = useNotification();
|
|
const isEmployee = useIsEmployee(bodyshop, currentUser);
|
|
|
|
// Fetch notification settings and notifications_autoadd
|
|
const { loading, error, data } = useQuery(QUERY_NOTIFICATION_SETTINGS, {
|
|
fetchPolicy: "network-only",
|
|
nextFetchPolicy: "network-only",
|
|
variables: { email: currentUser.email },
|
|
skip: !currentUser
|
|
});
|
|
|
|
const [updateNotificationSettings, { loading: savingSettings }] = useMutation(UPDATE_NOTIFICATION_SETTINGS);
|
|
const [updateNotificationsAutoAdd, { loading: savingAutoAdd }] = useMutation(UPDATE_NOTIFICATIONS_AUTOADD);
|
|
|
|
// Populate form with fetched data
|
|
useEffect(() => {
|
|
if (data?.associations?.length > 0) {
|
|
const settings = data.associations[0].notification_settings || {};
|
|
const autoAdd = data.associations[0].notifications_autoadd ?? false;
|
|
|
|
// Ensure each scenario has an object with { app, email, fcm }
|
|
const formattedValues = notificationScenarios.reduce((acc, scenario) => {
|
|
acc[scenario] = settings[scenario] ?? { app: false, email: false, fcm: false };
|
|
return acc;
|
|
}, {});
|
|
|
|
setInitialValues(formattedValues);
|
|
form.setFieldsValue(formattedValues);
|
|
setAutoAddEnabled(autoAdd);
|
|
setInitialAutoAdd(autoAdd);
|
|
setIsDirty(false); // Reset dirty state when new data loads
|
|
}
|
|
}, [data, form]);
|
|
|
|
// Handle toggle of notifications_autoadd
|
|
const handleAutoAddToggle = async (checked) => {
|
|
if (data?.associations?.length > 0) {
|
|
const userId = data.associations[0].id;
|
|
try {
|
|
const result = await updateNotificationsAutoAdd({
|
|
variables: { id: userId, autoadd: checked }
|
|
});
|
|
if (!result?.errors) {
|
|
setAutoAddEnabled(checked);
|
|
setInitialAutoAdd(checked);
|
|
notification.success({ message: t("notifications.labels.auto-add-success") });
|
|
setIsDirty(false); // Reset dirty state if only auto-add was changed
|
|
} else {
|
|
throw new Error("Failed to update auto-add setting");
|
|
}
|
|
} catch {
|
|
setAutoAddEnabled(!checked); // Revert on error
|
|
notification.error({ message: t("notifications.labels.auto-add-failure") });
|
|
}
|
|
}
|
|
};
|
|
|
|
// Handle save of notification settings
|
|
const handleSave = async (values) => {
|
|
if (data?.associations?.length > 0) {
|
|
const userId = data.associations[0].id;
|
|
try {
|
|
const result = await updateNotificationSettings({ variables: { id: userId, ns: values } });
|
|
if (!result?.errors) {
|
|
notification.success({ message: t("notifications.labels.notification-settings-success") });
|
|
setInitialValues(values);
|
|
setIsDirty(false);
|
|
} else {
|
|
throw new Error("Failed to update notification settings");
|
|
}
|
|
} catch {
|
|
notification.error({ message: t("notifications.labels.notification-settings-failure") });
|
|
}
|
|
}
|
|
};
|
|
|
|
// Mark the form as dirty on any manual change
|
|
const handleFormChange = () => {
|
|
setIsDirty(true);
|
|
};
|
|
|
|
// Check if auto-add has changed
|
|
const isAutoAddDirty = autoAddEnabled !== initialAutoAdd;
|
|
|
|
// Handle reset of form and auto-add
|
|
const handleReset = () => {
|
|
form.setFieldsValue(initialValues);
|
|
setAutoAddEnabled(initialAutoAdd);
|
|
setIsDirty(false);
|
|
};
|
|
|
|
if (error) return <AlertComponent type="error" message={error.message} />;
|
|
if (loading) return <LoadingSpinner />;
|
|
|
|
const columns = [
|
|
{
|
|
title: t("notifications.labels.scenario"),
|
|
dataIndex: "scenarioLabel",
|
|
key: "scenario",
|
|
render: (_, record) => t(`notifications.scenarios.${record.key}`),
|
|
width: "90%"
|
|
},
|
|
{
|
|
title: <ColumnHeaderCheckbox channel="app" form={form} onHeaderChange={() => setIsDirty(true)} />,
|
|
dataIndex: "app",
|
|
key: "app",
|
|
align: "center",
|
|
render: (_, record) => (
|
|
<Form.Item name={[record.key, "app"]} valuePropName="checked" noStyle>
|
|
<Checkbox />
|
|
</Form.Item>
|
|
)
|
|
},
|
|
{
|
|
title: <ColumnHeaderCheckbox channel="email" form={form} onHeaderChange={() => setIsDirty(true)} />,
|
|
dataIndex: "email",
|
|
key: "email",
|
|
align: "center",
|
|
render: (_, record) => (
|
|
<Form.Item name={[record.key, "email"]} valuePropName="checked" noStyle>
|
|
<Checkbox />
|
|
</Form.Item>
|
|
)
|
|
}
|
|
// TODO: Disabled for now until FCM is implemented.
|
|
// {
|
|
// title: <ColumnHeaderCheckbox channel="fcm" form={form} disabled onHeaderChange={() => setIsDirty(true)} />,
|
|
// dataIndex: "fcm",
|
|
// key: "fcm",
|
|
// align: "center",
|
|
// render: (_, record) => (
|
|
// <Form.Item name={[record.key, "fcm"]} valuePropName="checked" noStyle>
|
|
// <Checkbox disabled />
|
|
// </Form.Item>
|
|
// )
|
|
// }
|
|
];
|
|
|
|
const dataSource = notificationScenarios.map((scenario) => ({ key: scenario }));
|
|
|
|
return (
|
|
<Form
|
|
form={form}
|
|
onFinish={handleSave}
|
|
onValuesChange={handleFormChange}
|
|
initialValues={initialValues}
|
|
autoComplete="off"
|
|
layout="vertical"
|
|
>
|
|
<Card
|
|
title={t("notifications.labels.notificationscenarios")}
|
|
extra={
|
|
<Space>
|
|
<Typography.Text type="secondary">{t("notifications.labels.auto-add")}</Typography.Text>
|
|
<Switch
|
|
checked={autoAddEnabled}
|
|
onChange={handleAutoAddToggle}
|
|
loading={savingAutoAdd}
|
|
// checkedChildren={t("notifications.labels.auto-add-on")}
|
|
// unCheckedChildren={t("notifications.labels.auto-add-off")}
|
|
/>
|
|
<Button type="default" onClick={handleReset} disabled={!isDirty && !isAutoAddDirty}>
|
|
{t("general.actions.clear")}
|
|
</Button>
|
|
<Button type="primary" htmlType="submit" disabled={!isDirty} loading={savingSettings}>
|
|
{t("notifications.labels.save")}
|
|
</Button>
|
|
</Space>
|
|
}
|
|
>
|
|
{!isEmployee && (
|
|
<div style={{ width: "100%", marginBottom: "10px" }}>
|
|
<Alert message={t("notifications.labels.employee-notification")} type="warning" />
|
|
</div>
|
|
)}
|
|
<Table dataSource={dataSource} columns={columns} pagination={false} bordered rowKey="key" />
|
|
<Divider />
|
|
</Card>
|
|
</Form>
|
|
);
|
|
};
|
|
|
|
NotificationSettingsForm.propTypes = {
|
|
currentUser: PropTypes.shape({
|
|
email: PropTypes.string.isRequired
|
|
}).isRequired,
|
|
bodyshop: PropTypes.object.isRequired
|
|
};
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
currentUser: selectCurrentUser,
|
|
bodyshop: selectBodyshop
|
|
});
|
|
|
|
export default connect(mapStateToProps)(NotificationSettingsForm);
|