|
|
|
|
@@ -1,14 +1,14 @@
|
|
|
|
|
import { useCallback, useMemo, useState } from "react";
|
|
|
|
|
import React, { useCallback, useMemo, useState } from "react";
|
|
|
|
|
import { useMutation, useQuery } from "@apollo/client";
|
|
|
|
|
import { EyeFilled, EyeOutlined, UserOutlined } from "@ant-design/icons";
|
|
|
|
|
import { ADD_JOB_WATCHER, GET_JOB_WATCHERS, REMOVE_JOB_WATCHER } from "../../graphql/jobs.queries.js";
|
|
|
|
|
import { Avatar, Button, List, Popover, Tooltip, Typography } from "antd";
|
|
|
|
|
import { Avatar, Button, Divider, List, Popover, Select, Tooltip, 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.js";
|
|
|
|
|
import EmployeeSearchSelectComponent from "../../components/employee-search-select/employee-search-select.component.jsx";
|
|
|
|
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component.jsx"; // Ensure correct path
|
|
|
|
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component.jsx";
|
|
|
|
|
|
|
|
|
|
const { Text } = Typography;
|
|
|
|
|
|
|
|
|
|
@@ -24,6 +24,7 @@ const JobWatcherToggle = ({ job, currentUser, bodyshop }) => {
|
|
|
|
|
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const [selectedWatcher, setSelectedWatcher] = useState(null); // New state for selected value
|
|
|
|
|
const [selectedTeam, setSelectedTeam] = useState(null); // New state to track selected team
|
|
|
|
|
|
|
|
|
|
// Fetch current watchers
|
|
|
|
|
const { data: watcherData, loading: watcherLoading } = useQuery(GET_JOB_WATCHERS, { variables: { jobid } });
|
|
|
|
|
@@ -75,6 +76,45 @@ const JobWatcherToggle = ({ job, currentUser, bodyshop }) => {
|
|
|
|
|
setSelectedWatcher(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleTeamSelect = (team) => {
|
|
|
|
|
const selectedTeamMembers = JSON.parse(team); // Parse the array of emails
|
|
|
|
|
|
|
|
|
|
const newWatchers = selectedTeamMembers.filter(
|
|
|
|
|
(email) => !jobWatchers.some((watcher) => watcher.user_email === email)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Add each new watcher
|
|
|
|
|
newWatchers.forEach((email) => {
|
|
|
|
|
addWatcher({ variables: { jobid, userEmail: email } }).catch((err) =>
|
|
|
|
|
console.error(`Error adding job watcher: ${err.message}`)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Clear selection
|
|
|
|
|
setSelectedTeam(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRenderItem = (watcher) => {
|
|
|
|
|
const employee = bodyshop.employees.find((e) => e.user_email === watcher.user_email);
|
|
|
|
|
const displayName = employee ? `${employee.first_name} ${employee.last_name}` : watcher.user_email;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<List.Item
|
|
|
|
|
actions={[
|
|
|
|
|
<Button type="link" danger size="small" onClick={() => handleRemoveWatcher(watcher.user_email)}>
|
|
|
|
|
{t("notifications.actions.remove")}
|
|
|
|
|
</Button>
|
|
|
|
|
]}
|
|
|
|
|
>
|
|
|
|
|
<List.Item.Meta
|
|
|
|
|
avatar={<Avatar icon={<UserOutlined />} />}
|
|
|
|
|
title={<Text>{displayName}</Text>}
|
|
|
|
|
description={watcher.user_email} // Keep the email for reference
|
|
|
|
|
/>
|
|
|
|
|
</List.Item>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Popover content
|
|
|
|
|
const popoverContent = (
|
|
|
|
|
<div style={{ width: 600 }}>
|
|
|
|
|
@@ -88,52 +128,54 @@ const JobWatcherToggle = ({ job, currentUser, bodyshop }) => {
|
|
|
|
|
>
|
|
|
|
|
{isWatching ? t("notifications.tooltips.unwatch") : t("notifications.tooltips.watch")}
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
{/* List of Watchers */}
|
|
|
|
|
<Text type="secondary" style={{ marginBottom: 8, display: "block" }}>
|
|
|
|
|
{t("notifications.labels.watching-issue")}
|
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
|
|
{watcherLoading ? (
|
|
|
|
|
<LoadingSpinner />
|
|
|
|
|
) : (
|
|
|
|
|
<List
|
|
|
|
|
dataSource={jobWatchers}
|
|
|
|
|
renderItem={(watcher) => {
|
|
|
|
|
const employee = bodyshop.employees.find((e) => e.user_email === watcher.user_email);
|
|
|
|
|
const displayName = employee ? `${employee.first_name} ${employee.last_name}` : watcher.user_email;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<List.Item
|
|
|
|
|
actions={[
|
|
|
|
|
<Button type="link" danger size="small" onClick={() => handleRemoveWatcher(watcher.user_email)}>
|
|
|
|
|
{t("notifications.actions.remove")}
|
|
|
|
|
</Button>
|
|
|
|
|
]}
|
|
|
|
|
>
|
|
|
|
|
<List.Item.Meta
|
|
|
|
|
avatar={<Avatar icon={<UserOutlined />} />}
|
|
|
|
|
title={<Text>{displayName}</Text>}
|
|
|
|
|
description={watcher.user_email} // Keep the email for reference
|
|
|
|
|
/>
|
|
|
|
|
</List.Item>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{watcherLoading ? <LoadingSpinner /> : <List dataSource={jobWatchers} renderItem={handleRenderItem} />}
|
|
|
|
|
{/* Employee Search Select (for adding watchers) */}
|
|
|
|
|
<Divider />
|
|
|
|
|
|
|
|
|
|
<Text type="secondary">{t("notifications.labels.add-watchers")}</Text>
|
|
|
|
|
<EmployeeSearchSelectComponent
|
|
|
|
|
style={{ minWidth: "100%" }}
|
|
|
|
|
options={bodyshop.employees.filter((e) => jobWatchers.every((w) => w.user_email !== e.user_email))}
|
|
|
|
|
placeholder={t("production.labels.employeesearch")}
|
|
|
|
|
placeholder={t("notifications.labels.employee-search")}
|
|
|
|
|
value={selectedWatcher} // Controlled value
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
setSelectedWatcher(value); // Update selected state
|
|
|
|
|
handleWatcherSelect(value); // Add watcher logic
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
{/* Divider for UI separation */}
|
|
|
|
|
{/* Only show team selection if there are available teams */}
|
|
|
|
|
{bodyshop?.employee_teams?.length > 0 && (
|
|
|
|
|
<>
|
|
|
|
|
<Divider />
|
|
|
|
|
<Text type="secondary">{t("notifications.labels.add-watchers-team")}</Text>
|
|
|
|
|
|
|
|
|
|
<Select
|
|
|
|
|
showSearch
|
|
|
|
|
style={{ minWidth: "100%" }}
|
|
|
|
|
placeholder={t("notifications.labels.teams-search")}
|
|
|
|
|
value={selectedTeam} // Controlled value
|
|
|
|
|
onChange={handleTeamSelect}
|
|
|
|
|
options={bodyshop.employee_teams.map((team) => {
|
|
|
|
|
const teamMembers = team.employee_team_members
|
|
|
|
|
.map((member) => {
|
|
|
|
|
const employee = bodyshop.employees.find((e) => e.id === member.employeeid);
|
|
|
|
|
return employee ? employee.user_email : null;
|
|
|
|
|
})
|
|
|
|
|
.filter(Boolean); // Remove nulls
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
value: JSON.stringify(teamMembers), // Store array as string
|
|
|
|
|
label: team.name // Use team name as label
|
|
|
|
|
};
|
|
|
|
|
})}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|