diff --git a/client/src/components/Planner/DayPlanSidebar.test.tsx b/client/src/components/Planner/DayPlanSidebar.test.tsx
index 9cd1b432..114d800e 100644
--- a/client/src/components/Planner/DayPlanSidebar.test.tsx
+++ b/client/src/components/Planner/DayPlanSidebar.test.tsx
@@ -24,6 +24,10 @@ const mockDayNotesState = vi.hoisted(() => ({
moveNote: vi.fn(),
}))
+const mockPermissionsState = vi.hoisted(() => ({
+ canDo: true,
+}))
+
// ── Module mocks ────────────────────────────────────────────────────────────
vi.mock('../../api/client', async (importOriginal) => {
@@ -79,7 +83,7 @@ vi.mock('../../store/permissionsStore', async (importOriginal) => {
const actual = await importOriginal() as any
return {
...actual,
- useCanDo: () => () => true,
+ useCanDo: () => () => mockPermissionsState.canDo,
}
})
@@ -125,6 +129,7 @@ beforeEach(() => {
// Reset mutable day-notes state
mockDayNotesState.noteUi = {}
mockDayNotesState.dayNotes = {}
+ mockPermissionsState.canDo = true
seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true })
seedStore(useTripStore, { trip: buildTrip({ id: 1 }) })
seedStore(useSettingsStore, { settings: { time_format: '24h', temperature_unit: 'celsius' } } as any)
@@ -1007,6 +1012,25 @@ describe('DayPlanSidebar', () => {
revokeObjURL.mockRestore()
})
+ it('FE-PLANNER-DAYPLAN-099: ICS dialog hides delete link button without share_manage permission', async () => {
+ const user = userEvent.setup()
+ mockPermissionsState.canDo = false
+
+ const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({
+ ok: true,
+ json: () => Promise.resolve({ token: 'existing-token' }),
+ } as any)
+
+ render()
+ await user.click(screen.getByText('ICS').closest('button')!)
+
+ await waitFor(() => expect(fetchSpy).toHaveBeenCalledWith('/api/trips/1/subscribe.ics', expect.any(Object)))
+ expect(await screen.findByDisplayValue(`${window.location.origin}/api/shared/existing-token/calendar.ics`)).toBeInTheDocument()
+ expect(screen.queryByRole('button', { name: 'Delete link' })).not.toBeInTheDocument()
+
+ fetchSpy.mockRestore()
+ })
+
// ── openAddNote button click ──────────────────────────────────────────
it('FE-PLANNER-DAYPLAN-059: clicking Add Note button calls openAddNote', async () => {
diff --git a/client/src/components/Planner/DayPlanSidebar.tsx b/client/src/components/Planner/DayPlanSidebar.tsx
index 4edcd1d9..9d2f8d07 100644
--- a/client/src/components/Planner/DayPlanSidebar.tsx
+++ b/client/src/components/Planner/DayPlanSidebar.tsx
@@ -225,6 +225,7 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({
const tripActions = useRef(useTripStore.getState()).current
const can = useCanDo()
const canEditDays = can('day_edit', trip)
+ const canManageShare = can('share_manage', trip)
const { noteUi, setNoteUi, noteInputRef, dayNotes, openAddNote: _openAddNote, openEditNote: _openEditNote, cancelNote, saveNote, deleteNote: _deleteNote, moveNote: _moveNote } = useDayNotes(tripId)
@@ -2270,23 +2271,27 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({
{icsCopied ? <> {t('common.copied')}> : <> {t('common.copy')}>}
-
+ {canManageShare && (
+
+ )}
) : (
-
)}