import { useCallback, useEffect, useMemo, useState } from "react"; import { useMutation, useQuery } from "@apollo/client"; import { ADD_JOB_WATCHER, GET_JOB_WATCHERS, REMOVE_JOB_WATCHER } from "../../graphql/jobs.queries.js"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js"; import { useSplitTreatments } from "@splitsoftware/splitio-react"; import JobWatcherToggleComponent from "./job-watcher-toggle.component.jsx"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, currentUser: selectCurrentUser }); function JobWatcherToggleContainer({ job, currentUser, bodyshop }) { const { treatments: { Enhanced_Payroll } } = useSplitTreatments({ attributes: {}, names: ["Enhanced_Payroll"], splitKey: bodyshop && bodyshop.imexshopid }); const userEmail = currentUser.email; const jobid = job.id; const [open, setOpen] = useState(false); const [selectedWatcher, setSelectedWatcher] = useState(null); const [selectedTeam, setSelectedTeam] = useState(null); // Fetch current watchers with refetch capability const { data: watcherData, loading: watcherLoading, refetch } = useQuery(GET_JOB_WATCHERS, { variables: { jobid }, fetchPolicy: "cache-and-network" // Ensure fresh data from server }); // Refetch jobWatchers when the popover opens (open changes to true) useEffect(() => { if (open) { refetch().catch((err) => console.error(`Something went wrong fetching Notification Watchers on popover open: ${err?.message}`, { stack: err?.stack }) ); } }, [open, refetch]); const jobWatchers = useMemo(() => (watcherData?.job_watchers ? [...watcherData.job_watchers] : []), [watcherData]); const isWatching = jobWatchers.some((w) => w.user_email === userEmail); const [addWatcher, { loading: adding }] = useMutation(ADD_JOB_WATCHER, { onCompleted: () => refetch().catch((err) => console.error(`Something went wrong fetching Notification Watchers after add: ${err?.message}`, { stack: err?.stack }) ), onError: (err) => { if (err.graphQLErrors && err.graphQLErrors.length > 0) { const errorMessage = err.graphQLErrors[0].message; if ( errorMessage.includes("Uniqueness violation") || errorMessage.includes("idx_job_watchers_jobid_user_email_unique") ) { console.warn("Watcher already exists for this job and user."); refetch().catch((err) => console.error( `Something went wrong fetching Notification Watchers after uniqueness violation: ${err?.message}`, { stack: err?.stack } ) ); // Sync with server to ensure UI reflects actual state } else { console.error(`Error adding job watcher: ${errorMessage}`); } } else { console.error(`Unexpected error adding job watcher: ${err.message || JSON.stringify(err)}`); } }, update(cache, { data }) { if (!data || !data.insert_job_watchers_one) { console.warn("No data or insert_job_watchers_one returned from mutation, skipping cache update."); refetch().catch((err) => console.error(`Something went wrong updating Notification Watchers after add: ${err?.message}`, { stack: err?.stack }) ); return; } const insert_job_watchers_one = data.insert_job_watchers_one; const existingData = cache.readQuery({ query: GET_JOB_WATCHERS, variables: { jobid } }); cache.writeQuery({ query: GET_JOB_WATCHERS, variables: { jobid }, data: { ...existingData, job_watchers: [...(existingData?.job_watchers || []), insert_job_watchers_one] } }); } }); const [removeWatcher, { loading: removing }] = useMutation(REMOVE_JOB_WATCHER, { onCompleted: () => refetch().catch((err) => console.error(`Something went wrong fetching Notification Watchers after remove: ${err?.message}`, { stack: err?.stack }) ), // Refetch to sync with server after success onError: (err) => console.error(`Error removing job watcher: ${err.message}`), update(cache, { data: { delete_job_watchers } }) { const existingData = cache.readQuery({ query: GET_JOB_WATCHERS, variables: { jobid } }); const deletedWatcher = delete_job_watchers.returning[0]; const updatedWatchers = deletedWatcher ? (existingData?.job_watchers || []).filter((watcher) => watcher.user_email !== deletedWatcher.user_email) : existingData?.job_watchers || []; cache.writeQuery({ query: GET_JOB_WATCHERS, variables: { jobid }, data: { ...existingData, job_watchers: updatedWatchers } }); } }); const handleToggleSelf = useCallback(async () => { if (adding || removing) return; if (isWatching) { await removeWatcher({ variables: { jobid, userEmail } }); } else { await addWatcher({ variables: { jobid, userEmail } }); } }, [isWatching, addWatcher, removeWatcher, jobid, userEmail, adding, removing]); const handleRemoveWatcher = useCallback( async (email) => { if (removing) return; await removeWatcher({ variables: { jobid, userEmail: email } }); }, [removeWatcher, jobid, removing] ); const handleWatcherSelect = useCallback( async (selectedUser) => { if (adding || removing) return; const employee = bodyshop.employees.find((e) => e.id === selectedUser); if (!employee) return; const email = employee.user_email; const isAlreadyWatching = jobWatchers.some((w) => w.user_email === email); if (isAlreadyWatching) { await handleRemoveWatcher(email); } else { await addWatcher({ variables: { jobid, userEmail: email } }); } setSelectedWatcher(null); }, [jobWatchers, addWatcher, handleRemoveWatcher, jobid, bodyshop, adding, removing] ); const handleTeamSelect = useCallback( async (team) => { if (adding) return; const selectedTeamMembers = JSON.parse(team); const newWatchers = selectedTeamMembers.filter( (email) => !jobWatchers.some((watcher) => watcher.user_email === email) ); if (newWatchers.length === 0) { console.warn("All selected team members are already watchers."); setSelectedTeam(null); return; } await Promise.all(newWatchers.map((email) => addWatcher({ variables: { jobid, userEmail: email } }))); }, [jobWatchers, addWatcher, jobid, adding] ); return ( ); } export default connect(mapStateToProps)(JobWatcherToggleContainer);