diff --git a/client/src/components/profile-my/notification-settings.component.jsx b/client/src/components/profile-my/notification-settings.component.jsx index 054092025..659b951a7 100644 --- a/client/src/components/profile-my/notification-settings.component.jsx +++ b/client/src/components/profile-my/notification-settings.component.jsx @@ -11,22 +11,12 @@ import { QUERY_NOTIFICATION_SETTINGS, UPDATE_NOTIFICATION_SETTINGS } from "../.. import { notificationScenarios } from "../../utils/jobNotificationScenarios.js"; import LoadingSpinner from "../loading-spinner/loading-spinner.component.jsx"; -/** - * ColumnHeaderCheckbox - * - * A header checkbox for a given channel that toggles the state for all scenarios. - * - * Props: - * - channel: The notification channel (e.g. "app", "email", "fcm"). - * - form: The Ant Design form instance. - * - disabled: (Optional) If true, the checkbox will be disabled. - */ -const ColumnHeaderCheckbox = ({ channel, form, disabled = false }) => { +const ColumnHeaderCheckbox = ({ channel, form, disabled = false, onHeaderChange }) => { const { t } = useTranslation(); - // Watch the entire form values so that this component re-renders on changes. + // Subscribe to all form values so that this component re-renders on changes. const formValues = Form.useWatch([], form) || {}; - // Use the known scenarios list to decide if every row has this channel enabled. + // Determine if all scenarios for this channel are checked. const allChecked = notificationScenarios.length > 0 && notificationScenarios.every((scenario) => formValues[scenario]?.[channel]); @@ -39,7 +29,12 @@ const ColumnHeaderCheckbox = ({ channel, form, disabled = false }) => { notificationScenarios.forEach((scenario) => { newValues[scenario] = { ...newValues[scenario], [channel]: checked }; }); + // Update form values. form.setFieldsValue(newValues); + // Manually mark the form as dirty. + if (onHeaderChange) { + onHeaderChange(); + } }; return ( @@ -55,7 +50,7 @@ function NotificationSettingsForm({ currentUser }) { const [initialValues, setInitialValues] = useState({}); const [isDirty, setIsDirty] = useState(false); - // Fetch notification settings + // Fetch notification settings. const { loading, error, data } = useQuery(QUERY_NOTIFICATION_SETTINGS, { fetchPolicy: "network-only", nextFetchPolicy: "network-only", @@ -65,12 +60,11 @@ function NotificationSettingsForm({ currentUser }) { const [updateNotificationSettings, { loading: saving }] = useMutation(UPDATE_NOTIFICATION_SETTINGS); - // Populate form with fetched data + // Populate form with fetched data. useEffect(() => { if (data?.associations?.length > 0) { const settings = data.associations[0].notification_settings || {}; - // For each scenario, expect an object with keys { app, email, fcm }. - // If not present in the fetched data, default to all 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; @@ -78,20 +72,21 @@ function NotificationSettingsForm({ currentUser }) { setInitialValues(formattedValues); form.setFieldsValue(formattedValues); - setIsDirty(false); // Reset dirty state when new data loads + setIsDirty(false); // Reset dirty state when new data loads. } }, [data, form]); const handleSave = async (values) => { if (data?.associations?.length > 0) { const userId = data.associations[0].id; - // `values` now contains, for each scenario, an object with keys { app, email, fcm } + // Save the updated notification settings. await updateNotificationSettings({ variables: { id: userId, ns: values } }); setInitialValues(values); setIsDirty(false); } }; + // Mark the form as dirty on any manual change. const handleFormChange = () => { setIsDirty(true); }; @@ -113,10 +108,10 @@ function NotificationSettingsForm({ currentUser }) { width: "90%" }, { - title: , + title: setIsDirty(true)} />, dataIndex: "app", key: "app", - align: "center", // Center the cell content + align: "center", render: (_, record) => ( @@ -124,10 +119,10 @@ function NotificationSettingsForm({ currentUser }) { ) }, { - title: , + title: setIsDirty(true)} />, dataIndex: "email", key: "email", - align: "center", // Center the cell content + align: "center", render: (_, record) => ( @@ -135,10 +130,10 @@ function NotificationSettingsForm({ currentUser }) { ) }, { - title: , + title: setIsDirty(true)} />, dataIndex: "fcm", key: "fcm", - align: "center", // Center the cell content + align: "center", render: (_, record) => ( @@ -147,7 +142,6 @@ function NotificationSettingsForm({ currentUser }) { } ]; - // Create dataSource from the list of scenarios. const dataSource = notificationScenarios.map((scenario) => ({ key: scenario })); return ( @@ -178,7 +172,6 @@ function NotificationSettingsForm({ currentUser }) { ); } -// Redux connection const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser }); diff --git a/client/src/components/profile-my/profile-my.component.jsx b/client/src/components/profile-my/profile-my.component.jsx index 24f26c6f7..0a8624360 100644 --- a/client/src/components/profile-my/profile-my.component.jsx +++ b/client/src/components/profile-my/profile-my.component.jsx @@ -1,6 +1,5 @@ import { Button, Card, Col, Form, Input } from "antd"; import { LockOutlined } from "@ant-design/icons"; -import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -47,8 +46,6 @@ export default connect( } }; - const handleScenarios = async ({ values }) => {}; - return ( <> diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 0dc6fd03a..ddb1ba429 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -2706,6 +2706,8 @@ query GET_JOB_WATCHERS($jobid: uuid!) { } job: jobs_by_pk(id: $jobid) { id, + ro_number + clm_no bodyshop { id shopname diff --git a/server/notifications/scenarioBuilders/jobStatusChangeBuilder.js b/server/notifications/scenarioBuilders/jobStatusChangeBuilder.js new file mode 100644 index 000000000..10a7404e5 --- /dev/null +++ b/server/notifications/scenarioBuilders/jobStatusChangeBuilder.js @@ -0,0 +1,6 @@ +const consoleDir = require("../../utils/consoleDir"); +const jobStatusChangeBuilder = (data) => { + consoleDir(data); +}; + +module.exports = jobStatusChangeBuilder; diff --git a/server/notifications/utils/scenarioMapperr.js b/server/notifications/utils/scenarioMapperr.js index 38eb24b44..caca96341 100644 --- a/server/notifications/utils/scenarioMapperr.js +++ b/server/notifications/utils/scenarioMapperr.js @@ -5,15 +5,25 @@ // Builder: function to handle the scenario const tasksUpdatedCreatedBuilder = require("../scenarioBuilders/tasksUpdatedCreatedBuilder"); +const jobStatusChangeBuilder = require("../scenarioBuilders/jobStatusChangeBuilder"); +const jobAssignedToMeBuilder = require("../scenarioBuilders/jobAssignedToMeBuilder"); const notificationScenarios = [ { key: "job-assigned-to-me", table: "jobs", fields: ["employee_pre", "employee_body", "employee_csr", "employee_refinish"], - matchEmployee: true + matchEmployee: true, + builder: jobAssignedToMeBuilder + }, + { + key: "bill-posted", + table: "bills" + }, + { + key: "new-note-added", + table: "notes", + onNew: true }, - { key: "bill-posted", table: "bills" }, - { key: "new-note-added", table: "notes", onNew: true }, { key: "schedule-dates-changed", table: "jobs", @@ -26,16 +36,22 @@ const notificationScenarios = [ // onNew: true, builder: tasksUpdatedCreatedBuilder }, + { + key: "job-status-change", + table: "jobs", + fields: ["status"], + builder: jobStatusChangeBuilder + }, { key: "job-added-to-production", table: "jobs", fields: ["introduction"] }, - { key: "job-status-change", table: "jobs", fields: ["status"] }, { key: "alternate-transport-changed", table: "jobs", fields: ["alt_transport"] }, - { key: "payment-collected-completed" }, - { key: "new-media-added-reassigned" }, - { key: "new-time-ticket-posted" }, - { key: "intake-delivery-checklist-completed" }, + { key: "payment-collected-completed", table: "payments", onNew: true }, + // MAKE SURE YOU ARE NOT ON A LMS ENVIRONMENT + { key: "new-media-added-reassigned", table: "documents" }, + { key: "new-time-ticket-posted", table: "timetickets" }, + { key: "intake-delivery-checklist-completed", table: "jobs", fields: ["intakechecklist"] }, { key: "supplement-imported" }, - { key: "critical-parts-status-changed" }, - { key: "part-marked-back-ordered" } + { key: "critical-parts-status-changed", table: "joblines" }, + { key: "part-marked-back-ordered", table: "joblines" } ]; /**