diff --git a/client/src/components/task-center/task-center.component.jsx b/client/src/components/task-center/task-center.component.jsx
index b2466adda..705a2bb0b 100644
--- a/client/src/components/task-center/task-center.component.jsx
+++ b/client/src/components/task-center/task-center.component.jsx
@@ -13,7 +13,7 @@ import {
} from "@ant-design/icons";
const TaskCenterComponent = forwardRef(
- ({ visible, tasks, loading, onTaskClick, onLoadMore, totalTasks, createNewTask }, ref) => {
+ ({ visible, tasks, loading, error, onTaskClick, onLoadMore, hasMore, createNewTask }, ref) => {
const { t } = useTranslation();
const virtuosoRef = useRef(null);
@@ -24,8 +24,8 @@ const TaskCenterComponent = forwardRef(
[t("tasks.labels.no_due_date")]:
};
- const groupedItems = useMemo(() => {
- const now = day("2025-07-09"); // Set to current date
+ const groups = useMemo(() => {
+ const now = day();
const today = now.startOf("day");
const overdue = tasks.filter((t) => t.due_date && day(t.due_date).isBefore(today));
@@ -35,62 +35,79 @@ const TaskCenterComponent = forwardRef(
);
const noDueDate = tasks.filter((t) => !t.due_date);
- const makeGroup = (label, data) => (data.length ? [{ type: "section", label, tasks: data }] : []);
-
return [
- ...makeGroup(t("tasks.labels.overdue"), overdue),
- ...makeGroup(t("tasks.labels.due_today"), dueToday),
- ...makeGroup(t("tasks.labels.upcoming"), upcoming),
- ...makeGroup(t("tasks.labels.no_due_date"), noDueDate)
- ];
+ { label: t("tasks.labels.overdue"), tasks: overdue },
+ { label: t("tasks.labels.due_today"), tasks: dueToday },
+ { label: t("tasks.labels.upcoming"), tasks: upcoming },
+ { label: t("tasks.labels.no_due_date"), tasks: noDueDate }
+ ].filter((group) => group.tasks.length > 0);
}, [tasks, t]);
- const getPriorityColor = (priority) => {
- switch (priority) {
- case 1:
- return "red";
- case 2:
- return "orange";
- case 3:
- return "green";
- default:
- return null;
- }
+ const groupCounts = useMemo(() => groups.map((group) => group.tasks.length), [groups]);
+
+ const flatTasks = useMemo(() => groups.flatMap((group) => group.tasks), [groups]);
+
+ const priorityColors = {
+ 1: "red",
+ 2: "orange",
+ 3: "green"
};
- const renderSection = (section, index) => (
-
+ const getPriorityColor = (priority) => priorityColors[priority] || null;
+
+ const groupContent = (groupIndex) => {
+ const { label } = groups[groupIndex];
+ return (
- {sectionIcons[section.label]}
- {section.label} ({section.tasks.length})
+ {sectionIcons[label]}
+ {label} ({groups[groupIndex].tasks.length})
-
-
- {section.tasks.map((task) => {
- const priorityColor = getPriorityColor(task.priority);
- return (
- onTaskClick(task.id)}>
-
-
- {task.title}
-
- {t("notifications.labels.ro-number", {
- ro_number: task.job?.ro_number || t("general.labels.na")
- })}
-
-
- |
-
- {task.due_date && {day(task.due_date).fromNow()}}
- {!!priorityColor && }
- |
-
- );
- })}
-
-
-
- );
+ );
+ };
+
+ const itemContent = (index) => {
+ const task = flatTasks[index];
+ const priorityColor = getPriorityColor(task.priority);
+ return (
+ onTaskClick(task.id)}
+ role="button"
+ tabIndex={0}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ onTaskClick(task.id);
+ }
+ }}
+ >
+
+
+
{task.title}
+
+ {t("notifications.labels.ro-number", {
+ ro_number: task.job?.ro_number || t("general.labels.na")
+ })}
+
+
+
+
+ {task.due_date && {day(task.due_date).fromNow()}}
+ {!!priorityColor && }
+
+
+ );
+ };
+
+ if (error) {
+ return (
+
+
+
{t("tasks.labels.my_tasks_center")}
+
+
{t("errors.tasks_load_failed")}
+
+ );
+ }
return (
@@ -104,17 +121,25 @@ const TaskCenterComponent = forwardRef(
- renderSection(section, index)}
- />
-
- {tasks.length < totalTasks && (
-
+ {tasks.length === 0 && !loading ? (
+ {t("tasks.labels.no_tasks")}
+ ) : (
+
+ loading ? (
+
+
+
+ ) : null
+ }}
+ />
)}
);
diff --git a/client/src/components/task-center/task-center.container.jsx b/client/src/components/task-center/task-center.container.jsx
index 66681fdce..f05499e95 100644
--- a/client/src/components/task-center/task-center.container.jsx
+++ b/client/src/components/task-center/task-center.container.jsx
@@ -32,7 +32,11 @@ const TaskCenterContainer = ({ visible, onClose, bodyshop, currentUser, setTaskU
}, [bodyshop, currentUser]);
// Query 1: Tasks with due dates
- const { data: dueDateData, loading: dueLoading } = useQuery(QUERY_TASKS_WITH_DUE_DATES, {
+ const {
+ data: dueDateData,
+ loading: dueLoading,
+ error: dueError
+ } = useQuery(QUERY_TASKS_WITH_DUE_DATES, {
variables: {
bodyshop: bodyshop?.id,
assigned_to: assignedToId,
@@ -47,6 +51,7 @@ const TaskCenterContainer = ({ visible, onClose, bodyshop, currentUser, setTaskU
const {
data: noDueDateData,
loading: noDueLoading,
+ error: noDueError,
fetchMore
} = useQuery(QUERY_TASKS_NO_DUE_DATE_PAGINATED, {
variables: {
@@ -68,6 +73,10 @@ const TaskCenterContainer = ({ visible, onClose, bodyshop, currentUser, setTaskU
setTasks([...dueDateTasks, ...noDueDateTasks]);
}, [dueDateData, noDueDateData]);
+ const noDueDateLength = noDueDateData?.tasks?.length || 0;
+ const totalNoDueDate = noDueDateData?.tasks_aggregate?.aggregate?.count || 0;
+ const hasMore = noDueDateLength < totalNoDueDate;
+
// Handle pagination for no-due-date tasks
const handleLoadMore = () => {
fetchMore({
@@ -109,9 +118,10 @@ const TaskCenterContainer = ({ visible, onClose, bodyshop, currentUser, setTaskU
onClose={onClose}
tasks={tasks}
loading={dueLoading || noDueLoading}
+ error={dueError || noDueError}
onTaskClick={handleTaskClick}
onLoadMore={handleLoadMore}
- totalTasks={noDueDateData?.tasks_aggregate?.aggregate?.count || 0}
+ hasMore={hasMore}
createNewTask={createNewTask}
/>
);
diff --git a/client/src/components/task-center/task-center.styles.scss b/client/src/components/task-center/task-center.styles.scss
index b033ebdef..062aa133d 100644
--- a/client/src/components/task-center/task-center.styles.scss
+++ b/client/src/components/task-center/task-center.styles.scss
@@ -66,53 +66,50 @@
margin-bottom: 15px;
}
- .task-table {
- width: 100%;
+ .task-row {
+ cursor: pointer;
+ border-bottom: 1px solid #f0f0f0;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
- border-collapse: collapse;
+ &:hover {
+ background: #f5f5f5;
+ }
- .task-row {
- cursor: pointer;
- border-bottom: 1px solid #f0f0f0;
+ .task-title-cell {
+ flex: 1;
+ padding: 6px 8px;
+ vertical-align: top;
+ //font-size: 12px;
+ line-height: 1.2;
+ max-width: 350px; // or whatever fits your layout
- &:hover {
- background: #f5f5f5;
- }
-
- td {
- padding: 6px 8px;
- vertical-align: top;
- //font-size: 12px;
- line-height: 1.2;
- }
-
- .task-title-cell {
- width: 100%;
- max-width: 350px; // or whatever fits your layout
-
- .task-title {
- font-size: 16px;
- font-weight: 550;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 100%; // Or a specific width if you want more control
- display: inline-block;
- vertical-align: middle;
-
- }
-
- .task-ro-number {
- margin-top: 20px;
- color: #1677ff;
- }
- }
-
- .task-due-cell {
- text-align: right;
+ .task-title {
+ font-size: 16px;
+ font-weight: 550;
white-space: nowrap;
- color: rgba(0, 0, 0, 0.45);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 100%; // Or a specific width if you want more control
+ display: inline-block;
+ vertical-align: middle;
}
+
+ .task-ro-number {
+ margin-top: 20px;
+ color: #1677ff;
+ }
+ }
+
+ .task-due-cell {
+ padding: 6px 8px;
+ vertical-align: top;
+ //font-size: 12px;
+ line-height: 1.2;
+ text-align: right;
+ white-space: nowrap;
+ color: rgba(0, 0, 0, 0.45);
}
}
@@ -136,8 +133,15 @@
}
}
- .ReactVirtuoso__item {
- margin: 0 !important;
- padding: 0 !important;
+ .no-tasks-message,
+ .error-message {
+ padding: 16px;
+ text-align: center;
+ color: rgba(0, 0, 0, 0.45);
+ }
+
+ .loading-footer {
+ padding: 16px;
+ text-align: center;
}
}