# Feature Specification: Cancel an Event as Organizer **Feature**: `024-cancel-event` **Created**: 2026-03-06 **Status**: Draft **Source**: Migrated from spec/userstories.md ## User Scenarios & Testing ### User Story 1 - Cancel event with optional message (Priority: P1) The event organizer, from the organizer view, triggers a dedicated "Cancel event" action. A confirmation step is required before finalising. The organizer may optionally enter a cancellation message (reason or explanation). After confirmation, the server sets the event to cancelled state with the provided message. The event page immediately displays a "cancelled" state visible to all visitors. No RSVP submissions are accepted by the server from this point. **Why this priority**: Cancellation is a fundamental lifecycle action — guests need to be clearly informed when an event is cancelled rather than discovering it silently. This is the core of US-18. **Independent Test**: Can be fully tested by creating an event, triggering cancel (with and without a message), and verifying the event page shows a "cancelled" indicator and the RSVP form is absent. **Acceptance Scenarios**: 1. **Given** an organizer with a valid organizer token in localStorage, **When** they click "Cancel event", **Then** a confirmation step is shown before any change is made. 2. **Given** the organizer confirms cancellation without entering a message, **When** the server processes the request, **Then** the event is marked cancelled and the event page shows a "cancelled" state with no message. 3. **Given** the organizer confirms cancellation with a message, **When** the server processes the request, **Then** the event page displays the "cancelled" state along with the cancellation message. 4. **Given** a cancelled event, **When** a guest attempts to submit an RSVP, **Then** the server rejects the submission and the RSVP form is not shown on the event page. 5. **Given** a cancelled event that has not yet expired, **When** any visitor opens the event URL, **Then** the full event page renders with a clear "cancelled" indicator — no partial data is hidden. --- ### User Story 2 - Adjust expiry date during cancellation (Priority: P2) When cancelling, the organizer can optionally adjust the event's expiry date to control how long the cancellation notice remains visible before automatic data deletion (US-12). The adjusted date must be in the future, consistent with US-5's expiry date constraint. **Why this priority**: The expiry date adjustment is a convenience — organizers may want to keep the cancellation notice visible for a specific period (e.g. one more week) or trigger earlier cleanup. The core cancellation (P1) works without it. **Independent Test**: Can be tested by cancelling an event while setting a new expiry date in the future, then verifying the event's expiry date was updated and data persists until that date. **Acceptance Scenarios**: 1. **Given** the organizer confirms cancellation with a new expiry date set in the future, **When** the server processes the request, **Then** the event is cancelled and its expiry date is updated to the provided value. 2. **Given** the organizer provides an expiry date in the past or set to today during cancellation, **When** the server processes the request, **Then** the request is rejected with a clear validation error. 3. **Given** the organizer confirms cancellation without adjusting the expiry date, **When** the server processes the request, **Then** the existing expiry date is unchanged. --- ### User Story 3 - Edit cancellation message after cancellation (Priority: P3) After an event is cancelled, the organizer can update the cancellation message (e.g. to correct a typo or add further explanation). The cancelled state itself cannot be changed. **Why this priority**: Editing the message is a refinement capability. The core behaviour (cancellation + message at time of cancellation) is sufficient for P1 and P2. **Independent Test**: Can be tested by cancelling an event, then submitting an updated cancellation message via the organizer view, and verifying the new message is displayed on the event page. **Acceptance Scenarios**: 1. **Given** a cancelled event with a valid organizer token, **When** the organizer submits an updated cancellation message, **Then** the event page displays the new message. 2. **Given** a cancelled event with a valid organizer token, **When** the organizer attempts to un-cancel (set the event back to active), **Then** the server rejects the request — cancellation is a one-way state transition. 3. **Given** a cancelled event with an absent or invalid organizer token, **When** a request is made to edit the cancellation message, **Then** the server rejects the request. --- ### Edge Cases - Organizer token absent or invalid: the "Cancel event" action is not shown in the UI and the server rejects any cancel or message-edit request with an appropriate error response. - RSVP on a cancelled event: the server rejects RSVP submissions with a clear error; the RSVP form is hidden on the client. - Cancellation message is optional: omitting it is valid — the event still transitions to cancelled state with no message displayed. - Cancellation + expiry: after the expiry date passes, the event is deleted by the cleanup process (US-12) regardless of cancelled state; the cancellation data is removed as part of the full event deletion. - Already-expired event at time of cancellation: [NEEDS EXPANSION] — it is unclear whether cancellation should be allowed if the event has already expired but not yet been cleaned up. This edge case should be addressed during implementation. ## Requirements ### Functional Requirements - **FR-001**: The Event entity MUST persist a `is_cancelled` boolean (default false) and an optional `cancellation_message` string, both server-side. - **FR-002**: The cancel endpoint MUST require a valid organizer token; requests without a valid token are rejected. - **FR-003**: The cancel endpoint MUST accept an optional plain-text cancellation message. - **FR-004**: The cancel endpoint MUST accept an optional updated expiry date, which MUST be in the future; if provided and not in the future, the request is rejected with a clear validation error. - **FR-005**: Cancellation MUST be a one-way state transition: once cancelled, the event cannot be set back to active via any API endpoint. - **FR-006**: The event page MUST display a "cancelled" state indicator and the cancellation message (if present) for any visitor once the event is cancelled. - **FR-007**: The RSVP endpoint MUST reject submissions for cancelled events; the RSVP form MUST be hidden on the client for cancelled events. - **FR-008**: The organizer MUST be able to update the cancellation message after cancellation via a dedicated update action; this action MUST require a valid organizer token. - **FR-009**: The UI MUST present a confirmation step before submitting the cancellation request; the cancel action MUST NOT be triggerable in a single click without confirmation. - **FR-010**: The "Cancel event" action and organizer-specific cancel UI MUST NOT be rendered when no valid organizer token is present in localStorage. - **FR-011**: No personal data, IP addresses, or identifiers MUST be logged during cancellation or cancellation message updates. ### Key Entities - **CancellationState**: Value type on Event. Fields: `is_cancelled` (boolean, persistent), `cancellation_message` (optional string, persistent). Not a separate entity — stored on the Event record. ## Success Criteria ### Measurable Outcomes - **SC-001**: An organizer with a valid token can cancel an event (with or without a message) in a single confirmed action, and the cancelled state is immediately reflected on the event page. - **SC-002**: After cancellation, no guest can successfully submit an RSVP via the API, regardless of client-side state. - **SC-003**: The event page correctly renders a "cancelled" indicator (and message if provided) for any visitor after cancellation — no RSVP form, no false "active" state. - **SC-004**: Cancellation data (state and message) is removed automatically when the event expires and is deleted by the cleanup process (US-12). - **SC-005**: No client or API call can revert a cancelled event to an active state.