From e5690625ebe170acd44a93970dac2b21e15c9c84 Mon Sep 17 00:00:00 2001
From: Kyle Pope
Date: Fri, 6 Mar 2026 06:07:55 +0800
Subject: [PATCH] Fix member removal bug + QA fixes + shared calendar sidebar
styling
Bug fix:
- CalendarMemberRow: add type="button" to remove button (was submitting parent form)
QA fixes:
- EventDetailPanel: use axios.isAxiosError() instead of duck-typing for lock errors
- EventDetailPanel: only call onSaved on create (edits return to view mode, not close)
- CalendarForm: remove 4 redundant membersQuery.refetch() calls (mutations already invalidate)
- useEventLock: remove unused lockHeld ref from return, fix stale eventId in onSuccess
- EventLockBanner: guard against invalid date parse
UI:
- SharedCalendarSection: add purple Ghost icon next to "SHARED CALENDARS" header
Co-Authored-By: Claude Opus 4.6
---
.../src/components/calendar/CalendarForm.tsx | 4 ----
.../components/calendar/CalendarMemberRow.tsx | 1 +
.../components/calendar/EventDetailPanel.tsx | 23 +++++++++----------
.../components/calendar/EventLockBanner.tsx | 2 +-
.../calendar/SharedCalendarSection.tsx | 5 ++--
frontend/src/hooks/useEventLock.ts | 5 ++--
6 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/frontend/src/components/calendar/CalendarForm.tsx b/frontend/src/components/calendar/CalendarForm.tsx
index 9428093..d718ec8 100644
--- a/frontend/src/components/calendar/CalendarForm.tsx
+++ b/frontend/src/components/calendar/CalendarForm.tsx
@@ -101,25 +101,21 @@ export default function CalendarForm({ calendar, onClose }: CalendarFormProps) {
permission: 'read_only',
canAddOthers: false,
});
- membersQuery.refetch();
};
const handleUpdatePermission = async (memberId: number, permission: CalendarPermission) => {
if (!calendar) return;
await updateMember({ calendarId: calendar.id, memberId, permission });
- membersQuery.refetch();
};
const handleUpdateCanAddOthers = async (memberId: number, canAddOthers: boolean) => {
if (!calendar) return;
await updateMember({ calendarId: calendar.id, memberId, canAddOthers });
- membersQuery.refetch();
};
const handleRemoveMember = async (memberId: number) => {
if (!calendar) return;
await removeMember({ calendarId: calendar.id, memberId });
- membersQuery.refetch();
};
const canDelete = calendar && !calendar.is_default && !calendar.is_system;
diff --git a/frontend/src/components/calendar/CalendarMemberRow.tsx b/frontend/src/components/calendar/CalendarMemberRow.tsx
index f3ece48..f7540f6 100644
--- a/frontend/src/components/calendar/CalendarMemberRow.tsx
+++ b/frontend/src/components/calendar/CalendarMemberRow.tsx
@@ -79,6 +79,7 @@ export default function CalendarMemberRow({
)}
{!isPermanent && expiresAt && (
- Lock expires at {format(parseISO(expiresAt), 'h:mm a')}
+ Lock expires at {(() => { try { return format(parseISO(expiresAt), 'h:mm a'); } catch { return 'unknown'; } })()}
)}
diff --git a/frontend/src/components/calendar/SharedCalendarSection.tsx b/frontend/src/components/calendar/SharedCalendarSection.tsx
index 89997d7..9d52124 100644
--- a/frontend/src/components/calendar/SharedCalendarSection.tsx
+++ b/frontend/src/components/calendar/SharedCalendarSection.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react';
-import { Pencil } from 'lucide-react';
+import { Ghost, Pencil } from 'lucide-react';
import { Checkbox } from '@/components/ui/checkbox';
import type { SharedCalendarMembership } from '@/types';
import SharedCalendarSettings from './SharedCalendarSettings';
@@ -36,7 +36,8 @@ export default function SharedCalendarSection({
return (
<>
-
+
+
Shared Calendars
diff --git a/frontend/src/hooks/useEventLock.ts b/frontend/src/hooks/useEventLock.ts
index d15ce9b..9c8d695 100644
--- a/frontend/src/hooks/useEventLock.ts
+++ b/frontend/src/hooks/useEventLock.ts
@@ -14,9 +14,9 @@ export function useEventLock(eventId: number | null) {
);
return data;
},
- onSuccess: () => {
+ onSuccess: (_data, lockedId) => {
lockHeldRef.current = true;
- activeEventIdRef.current = eventId;
+ activeEventIdRef.current = lockedId;
},
});
@@ -64,6 +64,5 @@ export function useEventLock(eventId: number | null) {
release,
isAcquiring: acquireMutation.isPending,
acquireError: acquireMutation.error,
- lockHeld: lockHeldRef.current,
};
}