diff --git a/src/frontend/src/components/calendar/Calendar.tsx b/src/frontend/src/components/calendar/Calendar.tsx
index 8b80347fce..dcddcdcfd1 100644
--- a/src/frontend/src/components/calendar/Calendar.tsx
+++ b/src/frontend/src/components/calendar/Calendar.tsx
@@ -1,4 +1,8 @@
-import type { CalendarOptions, DatesSetArg } from '@fullcalendar/core';
+import type {
+ CalendarOptions,
+ DatesSetArg,
+ EventContentArg
+} from '@fullcalendar/core';
import allLocales from '@fullcalendar/core/locales-all';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
@@ -15,6 +19,7 @@ import {
Box,
Button,
Group,
+ HoverCard,
Indicator,
LoadingOverlay,
Popover,
@@ -29,7 +34,13 @@ import {
IconDownload,
IconFilter
} from '@tabler/icons-react';
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import {
+ type ReactNode,
+ useCallback,
+ useEffect,
+ useMemo,
+ useState
+} from 'react';
import { useShallow } from 'zustand/react/shallow';
import {
defaultLocale,
@@ -44,6 +55,7 @@ export interface InvenTreeCalendarProps extends CalendarOptions {
enableDownload?: boolean;
enableFilters?: boolean;
enableSearch?: boolean;
+ eventTooltipContent?: (event: EventContentArg) => ReactNode;
filters?: TableFilter[];
isLoading?: boolean;
state: CalendarState;
@@ -53,6 +65,7 @@ export default function Calendar({
enableDownload,
enableFilters = false,
enableSearch,
+ eventTooltipContent,
isLoading,
filters,
state,
@@ -112,6 +125,36 @@ export default function Calendar({
[calendarProps.datesSet, state.ref, state.setMonthName]
);
+ const wrappedEventContent = useCallback(
+ (arg: EventContentArg) => {
+ const inner =
+ typeof calendarProps.eventContent === 'function'
+ ? calendarProps.eventContent(arg, null)
+ : (calendarProps.eventContent ?? null);
+
+ if (!eventTooltipContent) return inner;
+
+ const tooltip = eventTooltipContent(arg);
+
+ if (!tooltip) return inner;
+
+ return (
+
+
+ {inner}
+
+ {tooltip}
+
+ );
+ },
+ [calendarProps.eventContent, eventTooltipContent]
+ );
+
return (
<>
{state.exportModal.modal}
@@ -217,6 +260,7 @@ export default function Calendar({
footerToolbar={false}
{...calendarProps}
datesSet={datesSet}
+ eventContent={wrappedEventContent}
/>
diff --git a/src/frontend/src/components/calendar/OrderCalendar.tsx b/src/frontend/src/components/calendar/OrderCalendar.tsx
index 9c3523653e..95fd76abe3 100644
--- a/src/frontend/src/components/calendar/OrderCalendar.tsx
+++ b/src/frontend/src/components/calendar/OrderCalendar.tsx
@@ -46,12 +46,14 @@ export default function OrderCalendar({
model,
role,
params,
- filters
+ filters,
+ tooltip
}: {
model: ModelType;
role: UserRoles;
params: Record;
filters?: TableFilter[];
+ tooltip?: (event: EventContentArg) => React.ReactNode;
}) {
const navigate = useNavigate();
const user = useUserState();
@@ -106,6 +108,7 @@ export default function OrderCalendar({
const end: string = order.target_date || start;
return {
+ order: order,
id: order.pk,
title: order.reference,
description: order.description,
@@ -209,6 +212,7 @@ export default function OrderCalendar({
state={calendarState}
filters={calendarFilters}
editable={true}
+ eventTooltipContent={tooltip}
eventContent={renderOrder}
eventClick={onClickOrder}
eventChange={onEditOrder}
diff --git a/src/frontend/src/components/calendar/OrderCalendarToolTip.tsx b/src/frontend/src/components/calendar/OrderCalendarToolTip.tsx
new file mode 100644
index 0000000000..dba5259358
--- /dev/null
+++ b/src/frontend/src/components/calendar/OrderCalendarToolTip.tsx
@@ -0,0 +1,61 @@
+import type { EventContentArg } from '@fullcalendar/core';
+import type { ModelType } from '@lib/enums/ModelType';
+import { resolveItem } from '@lib/functions/Conversion';
+import { t } from '@lingui/core/macro';
+import { Badge, Divider, Group, Stack, Text } from '@mantine/core';
+import { formatDate } from '../../defaults/formatters';
+import { RenderInstance } from '../render/Instance';
+import { RenderOwner } from '../render/User';
+
+export default function OrderCalendarToolTip({
+ event,
+ instanceLookup,
+ modelType
+}: {
+ event: EventContentArg;
+ instanceLookup: string;
+ modelType: ModelType;
+}) {
+ // Extract the order instance from the event
+ const order = event?.event?._def?.extendedProps?.order;
+
+ const instance = resolveItem(order, instanceLookup);
+
+ if (!order) return null;
+
+ return (
+
+
+
+
+
+ {order.reference}
+
+ {order.description || order.title}
+
+ {order.start_date && (
+
+ {t`Start Date`}
+ {formatDate(order.start_date)}
+
+ )}
+ {order.target_date && (
+
+ {t`Target Date`}
+ {formatDate(order.target_date)}
+ {order.overdue && (
+
+ {t`Overdue`}
+
+ )}
+
+ )}
+ {order.responsible && (
+
+ {t`Responsible`}
+
+
+ )}
+
+ );
+}
diff --git a/src/frontend/src/components/render/Company.tsx b/src/frontend/src/components/render/Company.tsx
index e8050d5f12..09aeee04cc 100644
--- a/src/frontend/src/components/render/Company.tsx
+++ b/src/frontend/src/components/render/Company.tsx
@@ -50,11 +50,11 @@ export function RenderCompany(
return (
);
diff --git a/src/frontend/src/pages/build/BuildIndex.tsx b/src/frontend/src/pages/build/BuildIndex.tsx
index 287b1d9cc4..f52441923d 100644
--- a/src/frontend/src/pages/build/BuildIndex.tsx
+++ b/src/frontend/src/pages/build/BuildIndex.tsx
@@ -6,14 +6,16 @@ import {
IconTable,
IconTools
} from '@tabler/icons-react';
-import { useMemo } from 'react';
+import { useCallback, useMemo } from 'react';
+import type { EventContentArg } from '@fullcalendar/core';
import { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles';
import type { TableFilter } from '@lib/types/Filters';
import type { PanelType } from '@lib/types/Panel';
import { useLocalStorage } from '@mantine/hooks';
import OrderCalendar from '../../components/calendar/OrderCalendar';
+import OrderCalendarToolTip from '../../components/calendar/OrderCalendarToolTip';
import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup } from '../../components/panels/PanelGroup';
@@ -34,12 +36,21 @@ function BuildOrderCalendar() {
});
}, [globalSettings]);
+ const renderTooltip = useCallback((event: EventContentArg) => {
+ return OrderCalendarToolTip({
+ event: event,
+ modelType: ModelType.part,
+ instanceLookup: 'part_detail'
+ });
+ }, []);
+
return (
);
}
diff --git a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx
index 3087c9b530..bdaa271c0e 100644
--- a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx
+++ b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx
@@ -10,13 +10,15 @@ import {
IconShoppingCart,
IconTable
} from '@tabler/icons-react';
-import { useMemo } from 'react';
+import { useCallback, useMemo } from 'react';
+import type { EventContentArg } from '@fullcalendar/core';
import { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles';
import type { TableFilter } from '@lib/index';
import { useLocalStorage } from '@mantine/hooks';
import OrderCalendar from '../../components/calendar/OrderCalendar';
+import OrderCalendarToolTip from '../../components/calendar/OrderCalendarToolTip';
import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup } from '../../components/panels/PanelGroup';
@@ -37,12 +39,21 @@ function PurchaseOrderCalendar() {
return PurchaseOrderFilters({ includeDateFilters: false });
}, []);
+ const renderTooltip = useCallback((event: EventContentArg) => {
+ return OrderCalendarToolTip({
+ event: event,
+ modelType: ModelType.company,
+ instanceLookup: 'supplier_detail'
+ });
+ }, []);
+
return (
);
}
diff --git a/src/frontend/src/pages/sales/SalesIndex.tsx b/src/frontend/src/pages/sales/SalesIndex.tsx
index a36fde83da..0f1c0879b0 100644
--- a/src/frontend/src/pages/sales/SalesIndex.tsx
+++ b/src/frontend/src/pages/sales/SalesIndex.tsx
@@ -9,13 +9,15 @@ import {
IconTruckDelivery,
IconTruckReturn
} from '@tabler/icons-react';
-import { useMemo } from 'react';
+import { useCallback, useMemo } from 'react';
+import type { EventContentArg } from '@fullcalendar/core';
import { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles';
import type { TableFilter } from '@lib/index';
import { useLocalStorage } from '@mantine/hooks';
import OrderCalendar from '../../components/calendar/OrderCalendar';
+import OrderCalendarToolTip from '../../components/calendar/OrderCalendarToolTip';
import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup } from '../../components/panels/PanelGroup';
@@ -35,12 +37,21 @@ function SalesOrderCalendar() {
return SalesOrderFilters({ includeDateFilters: false });
}, []);
+ const renderTooltip = useCallback((event: EventContentArg) => {
+ return OrderCalendarToolTip({
+ event: event,
+ modelType: ModelType.company,
+ instanceLookup: 'customer_detail'
+ });
+ }, []);
+
return (
);
}
@@ -50,12 +61,21 @@ const ReturnOrderCalendar = () => {
return SalesOrderFilters({ includeDateFilters: false });
}, []);
+ const renderTooltip = useCallback((event: EventContentArg) => {
+ return OrderCalendarToolTip({
+ event: event,
+ modelType: ModelType.company,
+ instanceLookup: 'customer_detail'
+ });
+ }, []);
+
return (
);
};
diff --git a/src/frontend/src/tables/InvenTreeTableHeader.tsx b/src/frontend/src/tables/InvenTreeTableHeader.tsx
index 612b6ba06d..2defcb7a22 100644
--- a/src/frontend/src/tables/InvenTreeTableHeader.tsx
+++ b/src/frontend/src/tables/InvenTreeTableHeader.tsx
@@ -276,6 +276,7 @@ export default function InvenTreeTableHeader({
{tableState.filterSet.activeFilters?.map((filter) => (