feature/IO-3291-Tasks-Notifications: Final

This commit is contained in:
Dave Richer
2025-07-11 13:38:19 -04:00
parent 4dcfb382a9
commit e0b937474d
8 changed files with 54 additions and 16 deletions

View File

@@ -9,7 +9,7 @@ import { useTranslation } from "react-i18next";
import { FaTasks } from "react-icons/fa"; import { FaTasks } from "react-icons/fa";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { useSocket } from "../../contexts/SocketIO/useSocket.js"; import { TASKS_CENTER_POLL_INTERVAL, useSocket } from "../../contexts/SocketIO/useSocket.js";
import { GET_UNREAD_COUNT } from "../../graphql/notifications.queries.js"; import { GET_UNREAD_COUNT } from "../../graphql/notifications.queries.js";
import { QUERY_MY_TASKS_COUNT } from "../../graphql/tasks.queries.js"; import { QUERY_MY_TASKS_COUNT } from "../../graphql/tasks.queries.js";
import { selectRecentItems, selectSelectedHeader } from "../../redux/application/application.selectors"; import { selectRecentItems, selectSelectedHeader } from "../../redux/application/application.selectors";
@@ -76,7 +76,7 @@ function useIncompleteTaskCount(assignedToId, bodyshopId, isEmployee, isConnecte
variables: { assigned_to: assignedToId, bodyshopid: bodyshopId }, variables: { assigned_to: assignedToId, bodyshopid: bodyshopId },
skip: !assignedToId || !bodyshopId || !isEmployee, skip: !assignedToId || !bodyshopId || !isEmployee,
fetchPolicy: "network-only", fetchPolicy: "network-only",
pollInterval: isConnected ? 0 : day.duration(60, "seconds").asMilliseconds() pollInterval: isConnected ? 0 : TASKS_CENTER_POLL_INTERVAL
}); });
const incompleteTaskCount = taskCountData?.tasks_aggregate?.aggregate?.count ?? 0; const incompleteTaskCount = taskCountData?.tasks_aggregate?.aggregate?.count ?? 0;

View File

@@ -90,7 +90,7 @@ const TaskCenterComponent = forwardRef(
<div className="task-row-container"> <div className="task-row-container">
<div className="task-title">{task.title}</div> <div className="task-title">{task.title}</div>
<div className="task-ro-number"> <div className="task-ro-number">
{t("notifications.labels.ro-number", { {t("tasks.labels.ro-number", {
ro_number: task.job?.ro_number || t("general.labels.na") ro_number: task.job?.ro_number || t("general.labels.na")
})} })}
</div> </div>
@@ -110,7 +110,7 @@ const TaskCenterComponent = forwardRef(
<div className="task-header"> <div className="task-header">
<h3>{t("tasks.labels.my_tasks_center")}</h3> <h3>{t("tasks.labels.my_tasks_center")}</h3>
</div> </div>
<div className="error-message">{t("errors.tasks_load_failed")}</div> <div className="error-message">{t("tasks.errors.load_failed")}</div>
</div> </div>
); );
} }

View File

@@ -3,15 +3,12 @@ import { useQuery } from "@apollo/client";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import { useSocket } from "../../contexts/SocketIO/useSocket"; import { INITIAL_TASKS, TASKS_CENTER_POLL_INTERVAL, useSocket } from "../../contexts/SocketIO/useSocket";
import { useIsEmployee } from "../../utils/useIsEmployee"; import { useIsEmployee } from "../../utils/useIsEmployee";
import TaskCenterComponent from "./task-center.component"; import TaskCenterComponent from "./task-center.component";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { QUERY_TASKS_NO_DUE_DATE_PAGINATED, QUERY_TASKS_WITH_DUE_DATES } from "../../graphql/tasks.queries"; import { QUERY_TASKS_NO_DUE_DATE_PAGINATED, QUERY_TASKS_WITH_DUE_DATES } from "../../graphql/tasks.queries";
const POLL_INTERVAL = 60 * 1000; // milliseconds
const LIMIT = 5; // Tasks per page for no-due-date tasks
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser currentUser: selectCurrentUser
@@ -51,7 +48,7 @@ const TaskCenterContainer = ({
}, },
skip: !bodyshop?.id || !assignedToId || !isEmployee || !currentUser?.email, skip: !bodyshop?.id || !assignedToId || !isEmployee || !currentUser?.email,
fetchPolicy: "cache-and-network", fetchPolicy: "cache-and-network",
pollInterval: isConnected ? 0 : POLL_INTERVAL pollInterval: isConnected ? 0 : TASKS_CENTER_POLL_INTERVAL
}); });
// Query 2: Tasks with no due date (paginated) // Query 2: Tasks with no due date (paginated)
@@ -64,13 +61,13 @@ const TaskCenterContainer = ({
variables: { variables: {
bodyshop: bodyshop?.id, bodyshop: bodyshop?.id,
assigned_to: assignedToId, assigned_to: assignedToId,
order: [{ created_at: "desc" }], order: [{ priority: "asc" }, { created_at: "desc" }],
limit: LIMIT, limit: INITIAL_TASKS, // Adjust this constant as needed
offset: 0 offset: 0
}, },
skip: !bodyshop?.id || !assignedToId || !isEmployee || !currentUser?.email, skip: !bodyshop?.id || !assignedToId || !isEmployee || !currentUser?.email,
fetchPolicy: "cache-and-network", fetchPolicy: "cache-and-network",
pollInterval: isConnected ? 0 : POLL_INTERVAL pollInterval: isConnected ? 0 : TASKS_CENTER_POLL_INTERVAL
}); });
// Combine tasks from both queries // Combine tasks from both queries

View File

@@ -3,6 +3,8 @@ import { createContext, useContext } from "react";
const SocketContext = createContext(null); const SocketContext = createContext(null);
const INITIAL_NOTIFICATIONS = 10; const INITIAL_NOTIFICATIONS = 10;
const INITIAL_TASKS = 5;
const TASKS_CENTER_POLL_INTERVAL = 60 * 60 * 1000; // 1 hour in milliseconds
const useSocket = () => { const useSocket = () => {
const context = useContext(SocketContext); const context = useContext(SocketContext);
@@ -10,4 +12,4 @@ const useSocket = () => {
return context; return context;
}; };
export { SocketContext, INITIAL_NOTIFICATIONS, useSocket }; export { SocketContext, INITIAL_NOTIFICATIONS, INITIAL_TASKS, TASKS_CENTER_POLL_INTERVAL, useSocket };

View File

@@ -68,7 +68,7 @@ export const PARTIAL_TASK_FIELDS = gql`
`; `;
export const PARTIAL_TASK_CENTER_FIELDS = gql` export const PARTIAL_TASK_CENTER_FIELDS = gql`
fragment TaskFields on tasks { fragment PartialTaskFields on tasks {
id id
title title
description description
@@ -103,7 +103,7 @@ export const QUERY_TASKS_WITH_DUE_DATES = gql`
} }
order_by: $order order_by: $order
) { ) {
...TaskFields ...PartialTaskFields
} }
} }
`; `;
@@ -128,7 +128,7 @@ export const QUERY_TASKS_NO_DUE_DATE_PAGINATED = gql`
limit: $limit limit: $limit
offset: $offset offset: $offset
) { ) {
...TaskFields ...PartialTaskFields
} }
tasks_aggregate( tasks_aggregate(
where: { where: {

View File

@@ -3295,6 +3295,16 @@
} }
}, },
"tasks": { "tasks": {
"labels": {
"my_tasks_center": "Task Center",
"go_to_job": "Go to Job",
"overdue": "Overdue",
"due_today": "Today",
"upcoming": "Upcoming",
"no_due_date": "Incomplete",
"ro-number": "RO #{{ro_number}}",
"no_tasks": "No Tasks Found"
},
"actions": { "actions": {
"edit": "Edit Task", "edit": "Edit Task",
"new": "New Task", "new": "New Task",
@@ -3309,6 +3319,9 @@
"myTasks": "Mine", "myTasks": "Mine",
"refresh": "Refresh" "refresh": "Refresh"
}, },
"errors": {
"load_failure": "Failed to load Tasks."
},
"date_presets": { "date_presets": {
"completion": "Completion", "completion": "Completion",
"day": "Day", "day": "Day",

View File

@@ -3297,6 +3297,16 @@
} }
}, },
"tasks": { "tasks": {
"labels": {
"my_tasks_center": "",
"go_to_job": "",
"overdue": "",
"due_today": "",
"upcoming": "",
"no_due_date": "",
"ro-number": "",
"no_tasks": ""
},
"actions": { "actions": {
"edit": "", "edit": "",
"new": "", "new": "",
@@ -3311,6 +3321,9 @@
"myTasks": "", "myTasks": "",
"refresh": "" "refresh": ""
}, },
"errors": {
"load_failure": ""
},
"date_presets": { "date_presets": {
"completion": "", "completion": "",
"day": "", "day": "",

View File

@@ -3297,6 +3297,16 @@
} }
}, },
"tasks": { "tasks": {
"labels": {
"my_tasks_center": "",
"go_to_job": "",
"overdue": "",
"due_today": "",
"upcoming": "",
"no_due_date": "",
"ro-number": "",
"no_tasks": ""
},
"actions": { "actions": {
"edit": "", "edit": "",
"new": "", "new": "",
@@ -3311,6 +3321,9 @@
"myTasks": "", "myTasks": "",
"refresh": "" "refresh": ""
}, },
"errors": {
"load_failure": ""
},
"date_presets": { "date_presets": {
"completion": "", "completion": "",
"day": "", "day": "",