fix(03): WR-01 preserve existing RRULE on edit instead of resetting to none

This commit is contained in:
Lucas Berger
2026-06-09 10:39:06 -04:00
parent f645644853
commit 02aa407764
4 changed files with 80 additions and 17 deletions
+20 -3
View File
@@ -305,12 +305,19 @@ export function EventForm() {
endTime,
)
// WR-01: on EDIT, omit `recurrence` from the payload. The occurrence/expand contract
// does not expose the event's existing recurrence (D-03), so the form cannot know it
// and would otherwise send 'none' — silently stripping the RRULE and converting a
// recurring series into a single event. Omitting the field signals "unchanged"; the
// outbox worker then preserves the stored RRULE (see outboxWorker.ts WR-01). On
// CREATE the user explicitly chose a recurrence, so it is always sent.
const isEdit = eventFormMode === 'edit' && !!eventFormUid
const payload: CreateEventPayload = {
title: title.trim(),
allDay,
start: serializedStart,
end: serializedEnd,
recurrence,
...(isEdit ? {} : { recurrence }),
...(location.trim() ? { location: location.trim() } : {}),
...(description.trim() ? { description: description.trim() } : {}),
...(writableCalendars.length > 1 && calendarUrl ? { calendarUrl } : {}),
@@ -702,7 +709,11 @@ export function EventForm() {
</div>
)}
{/* Recurrence picker (D-11: whole-series only) */}
{/* Recurrence picker (D-11: whole-series only).
WR-01: disabled in edit mode — the occurrence contract does not expose the
event's recurrence, so the form cannot show/change it without risking a
silent reset. Editing recurrence is deferred until the contract exposes it;
the existing RRULE is preserved server-side on edit. */}
<div style={fieldStyle}>
<label htmlFor="event-recurrence" style={labelStyle}>
Repeat
@@ -710,8 +721,14 @@ export function EventForm() {
<select
id="event-recurrence"
value={recurrence}
disabled={eventFormMode === 'edit'}
onChange={(e) => setRecurrence(e.target.value as RecurrencePreset)}
style={{ ...inputStyle, padding: '0 var(--space-3)', cursor: 'pointer' }}
style={{
...inputStyle,
padding: '0 var(--space-3)',
cursor: eventFormMode === 'edit' ? 'not-allowed' : 'pointer',
opacity: eventFormMode === 'edit' ? 0.6 : 1,
}}
>
<option value="none">None</option>
<option value="daily">Daily</option>