diff --git a/components/email/__tests__/calendar-invitation-banner.test.tsx b/components/email/__tests__/calendar-invitation-banner.test.tsx
index f048e956a..71317f4c6 100644
--- a/components/email/__tests__/calendar-invitation-banner.test.tsx
+++ b/components/email/__tests__/calendar-invitation-banner.test.tsx
@@ -465,6 +465,33 @@ describe('CalendarInvitationBanner', () => {
expect(screen.getByRole('heading', { name: 'Planning Session' })).toBeInTheDocument();
});
+ // pim-p2-2: "Add to calendar" offered the account's task list, where the
+ // event would be stored but never shown, and other logins' calendars.
+ it('adds to the one calendar that shows events, never a task list or another login', async () => {
+ const before = mocks.calendarState.calendars;
+ mocks.calendarState.calendars = [
+ { id: 'cal-1', name: 'Primary', color: '#2563eb', isDefault: true },
+ { id: 'todo', name: 'To-do', color: '#16a34a', isDefault: false, isTasksOnly: true },
+ { id: 'other', name: 'Other login', color: '#dc2626', isDefault: true, localAccountId: 'login-b' },
+ ] as never;
+ try {
+ mocks.calendarState.importEvents.mockResolvedValue(1);
+ mocks.clientMock.parseCalendarEvents.mockResolvedValue([
+ { uid: 'uid-tl', title: 'Target check', start: '2026-03-22T13:00:00Z', participants: {
+ attendee: { '@type': 'Participant', email: 'user@example.com', roles: { attendee: true }, participationStatus: 'needs-action' },
+ } },
+ ]);
+ render();
+ await screen.findByRole('heading', { name: 'Target check' });
+ fireEvent.click(screen.getByText('Add to calendar'));
+ await waitFor(() => expect(mocks.calendarState.importEvents).toHaveBeenCalledTimes(1));
+ expect(JSON.stringify(mocks.calendarState.importEvents.mock.calls[0])).toContain('cal-1');
+ expect(screen.queryByText('To-do')).toBeNull();
+ } finally {
+ mocks.calendarState.calendars = before;
+ }
+ });
+
it('does not show RSVP actions when the current user is the organizer', async () => {
mocks.clientMock.parseCalendarEvents.mockResolvedValue([
{
diff --git a/components/email/calendar-invitation-banner.tsx b/components/email/calendar-invitation-banner.tsx
index c794eac84..a921ca733 100644
--- a/components/email/calendar-invitation-banner.tsx
+++ b/components/email/calendar-invitation-banner.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useState, useEffect, useCallback, useRef } from 'react';
+import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { answerInvitation } from '@/lib/calendar-rsvp';
import { createPortal } from 'react-dom';
import {
@@ -27,6 +27,7 @@ import { isDocumentRTL } from '@/i18n/direction';
import { useAuthStore } from '@/stores/auth-store';
import { useShallow } from "zustand/react/shallow";
import { useEmailStore } from '@/stores/email-store';
+import { eventTargets } from '@/lib/calendar-targets';
import { useCalendarStore } from '@/stores/calendar-store';
import { useSettingsStore } from '@/stores/settings-store';
import type { Email, CalendarEvent } from '@/lib/jmap/types';
@@ -455,7 +456,7 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
const timeFormat = useSettingsStore((s) => s.timeFormat);
// The user's zone, as getEffectiveTimeZone resolves it.
const userTimeZone = resolveTimeZone(useSettingsStore((s) => s.timeZone));
- const { calendars, supportsCalendar, importEvents, rsvpEvent, updateEvent, deleteEvent, events: storeEvents, setSelectedDate } = useCalendarStore(useShallow((state) => ({
+ const { calendars: allCalendars, supportsCalendar, importEvents, rsvpEvent, updateEvent, deleteEvent, events: storeEvents, setSelectedDate } = useCalendarStore(useShallow((state) => ({
calendars: state.calendars,
supportsCalendar: state.supportsCalendar,
importEvents: state.importEvents,
@@ -466,6 +467,16 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
setSelectedDate: state.setSelectedDate,
})));
+ // Where an invitation can go: the calendars of the account on screen that show
+ // in the grid. A task list would hide the event, and another login's calendar
+ // would put the meeting in the wrong account (pim-p2-2).
+ const activeLocalAccountId = useAuthStore((s) => s.activeAccountId);
+ const calendars = useMemo(() => {
+ const mine = allCalendars.filter((c) => !c.localAccountId || c.localAccountId === activeLocalAccountId);
+ const targets = eventTargets(mine);
+ return targets.length > 0 ? targets : mine;
+ }, [allCalendars, activeLocalAccountId]);
+
const [state, setState] = useState('loading');
const [parsedEvent, setParsedEvent] = useState | null>(null);
const [rsvpStatus, setRsvpStatus] = useState(null);