feat(06-06): add whole-series edit confirmation prompt
- Create SeriesEditPrompt.tsx: bottom-sheet (phone) / dialog (desktop) matching DeleteConfirmationDialog pattern; focus trap, Escape=cancel, role=dialog/aria-modal - UI-SPEC Surface 6 copy: 'Edit recurring series' heading, 'This will update all occurrences of this event.' body, 'Update series' accent-filled CTA, 'Cancel' ghost - Import SeriesEditPrompt + add seriesEditPromptOpen state to EventForm - handleSubmit gates on occurrence.hasRrule: opens prompt for recurring edits; executeSubmit() fires the existing whole-series PATCH on confirmation - Save CTA label becomes 'Update series' for recurring edits (UI-SPEC primary CTAs) - Non-recurring and create-mode Save behavior unchanged (no prompt)
This commit is contained in:
@@ -45,6 +45,7 @@ import {
|
||||
computeNewTimedEnd,
|
||||
computeNewAllDayEnd,
|
||||
} from '../lib/eventDateTime.js'
|
||||
import { SeriesEditPrompt } from './SeriesEditPrompt.js'
|
||||
|
||||
// ── Constants ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -276,6 +277,8 @@ export function EventForm() {
|
||||
// ── Validation state ────────────────────────────────────────────────────────
|
||||
|
||||
const [errors, setErrors] = useState<{ title?: string; endTime?: string; recurrenceBound?: string }>({})
|
||||
// D-08/D-09: series-edit confirmation prompt state
|
||||
const [seriesEditPromptOpen, setSeriesEditPromptOpen] = useState(false)
|
||||
|
||||
// ── Mutations ───────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -360,9 +363,13 @@ export function EventForm() {
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!validate()) return
|
||||
|
||||
/**
|
||||
* Build the payload and fire the mutation. Called directly for non-recurring edits
|
||||
* and create mode; called from the SeriesEditPrompt onConfirm for recurring edits
|
||||
* (D-08/D-09). The whole-series PATCH reuses the existing /api/events/:uid/edit route
|
||||
* which PUTs the master VEVENT — no RECURRENCE-ID, per D-08.
|
||||
*/
|
||||
const executeSubmit = () => {
|
||||
// BUG A fix: serialize timed events to an unambiguous UTC instant here in
|
||||
// the browser (operator's zone is known) instead of sending a naive local
|
||||
// wall-clock string. The API container is UTC; a naive string was being read
|
||||
@@ -403,6 +410,20 @@ export function EventForm() {
|
||||
mutation.mutate(payload)
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!validate()) return
|
||||
|
||||
const isEdit = eventFormMode === 'edit' && !!eventFormUid
|
||||
|
||||
// D-08/D-09: gate recurring-series edits behind the confirmation prompt
|
||||
if (isEdit && occurrence?.hasRrule === true) {
|
||||
setSeriesEditPromptOpen(true)
|
||||
return
|
||||
}
|
||||
|
||||
executeSubmit()
|
||||
}
|
||||
|
||||
// ── Keyboard: Escape to close ───────────────────────────────────────────────
|
||||
|
||||
useEffect(() => {
|
||||
@@ -466,11 +487,15 @@ export function EventForm() {
|
||||
|
||||
const isPhone = isPhoneBreakpoint()
|
||||
const label = eventFormMode === 'edit' ? 'Edit Event' : 'New Event'
|
||||
// D-08/D-09: CTA label is "Update series" for recurring edits (UI-SPEC "EventForm primary CTAs")
|
||||
const isEditRecurring = eventFormMode === 'edit' && occurrence?.hasRrule === true
|
||||
const saveLabel = mutation.isPending
|
||||
? 'Saving…'
|
||||
: eventFormMode === 'edit'
|
||||
? 'Save Changes'
|
||||
: 'Create Event'
|
||||
: isEditRecurring
|
||||
? 'Update series'
|
||||
: eventFormMode === 'edit'
|
||||
? 'Save Changes'
|
||||
: 'Create Event'
|
||||
|
||||
const dialogStyle: React.CSSProperties = isPhone
|
||||
? {
|
||||
@@ -1014,6 +1039,16 @@ export function EventForm() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* D-08/D-09: Series-edit confirmation prompt — shown when editing a recurring occurrence */}
|
||||
<SeriesEditPrompt
|
||||
open={seriesEditPromptOpen}
|
||||
onConfirm={() => {
|
||||
setSeriesEditPromptOpen(false)
|
||||
executeSubmit()
|
||||
}}
|
||||
onCancel={() => setSeriesEditPromptOpen(false)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user