feat: add recurring event deletion with three modes
Implement three deletion modes for recurring events: - single: exclude specific occurrence via EXDATE mechanism - future: set RRULE UNTIL to stop future occurrences - all: delete entire event series Changes include: - Add exceptionDates field to CalendarEvent model - Add RecurringDeleteMode type and DeleteRecurringEventDTO - EventService.deleteRecurring() with mode-based logic using rrule library - EventController DELETE endpoint accepts mode/occurrenceDate query params - recurrenceExpander filters out exception dates during expansion - AI tools support deleteMode and occurrenceDate for proposed deletions - ChatService.confirmEvent() handles recurring delete modes - New DeleteEventModal component for unified delete confirmation UI - Calendar screen integrates modal for both recurring and non-recurring events
This commit is contained in:
30
CLAUDE.md
30
CLAUDE.md
@@ -91,7 +91,8 @@ src/
|
||||
│ ├── EventCardBase.tsx # Shared event card layout with icons (used by EventCard & ProposedEventCard)
|
||||
│ ├── EventCard.tsx # Calendar event card (uses EventCardBase + edit/delete buttons)
|
||||
│ ├── EventConfirmDialog.tsx # AI-proposed event confirmation modal (skeleton)
|
||||
│ └── ProposedEventCard.tsx # Chat event proposal (uses EventCardBase + confirm/reject buttons)
|
||||
│ ├── ProposedEventCard.tsx # Chat event proposal (uses EventCardBase + confirm/reject buttons)
|
||||
│ └── DeleteEventModal.tsx # Unified delete confirmation modal (recurring: 3 options, non-recurring: simple confirm)
|
||||
├── Themes.tsx # Theme definitions: THEMES object with defaultLight/defaultDark, Theme type
|
||||
├── logging/
|
||||
│ ├── index.ts # Re-exports
|
||||
@@ -100,8 +101,8 @@ src/
|
||||
│ ├── index.ts # Re-exports all services
|
||||
│ ├── ApiClient.ts # HTTP client with X-User-Id header injection, request logging, handles empty responses (204)
|
||||
│ ├── AuthService.ts # login(), register(), logout() - calls API and updates AuthStore
|
||||
│ ├── EventService.ts # getAll(), getById(), getByDateRange(), create(), update(), delete()
|
||||
│ └── ChatService.ts # sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation()
|
||||
│ ├── EventService.ts # getAll(), getById(), getByDateRange(), create(), update(), delete(mode, occurrenceDate)
|
||||
│ └── ChatService.ts # sendMessage(), confirmEvent(deleteMode, occurrenceDate), rejectEvent(), getConversations(), getConversation()
|
||||
└── stores/ # Zustand state management
|
||||
├── index.ts # Re-exports all stores
|
||||
├── AuthStore.ts # user, isAuthenticated, isLoading, login(), logout(), loadStoredUser()
|
||||
@@ -224,7 +225,7 @@ src/
|
||||
- `GET /api/events/:id` - Get single event (protected)
|
||||
- `POST /api/events` - Create event (protected)
|
||||
- `PUT /api/events/:id` - Update event (protected)
|
||||
- `DELETE /api/events/:id` - Delete event (protected)
|
||||
- `DELETE /api/events/:id` - Delete event (protected, query params: mode, occurrenceDate for recurring)
|
||||
- `POST /api/chat/message` - Send message to AI (protected)
|
||||
- `POST /api/chat/confirm/:conversationId/:messageId` - Confirm proposed event (protected)
|
||||
- `POST /api/chat/reject/:conversationId/:messageId` - Reject proposed event (protected)
|
||||
@@ -254,12 +255,15 @@ src/
|
||||
|
||||
**Key Types:**
|
||||
- `User`: id, email, userName, passwordHash?, createdAt?, updatedAt?
|
||||
- `CalendarEvent`: id, userId, title, description?, startTime, endTime, note?, isRecurring?, recurrenceRule?
|
||||
- `CalendarEvent`: id, userId, title, description?, startTime, endTime, note?, isRecurring?, recurrenceRule?, exceptionDates?
|
||||
- `ExpandedEvent`: Extends CalendarEvent with occurrenceStart, occurrenceEnd (for recurring event instances)
|
||||
- `ChatMessage`: id, conversationId, sender ('user' | 'assistant'), content, proposedChanges?
|
||||
- `ProposedEventChange`: id, action ('create' | 'update' | 'delete'), eventId?, event?, updates?, respondedAction?
|
||||
- `ProposedEventChange`: id, action ('create' | 'update' | 'delete'), eventId?, event?, updates?, respondedAction?, deleteMode?, occurrenceDate?
|
||||
- Each proposal has unique `id` (e.g., "proposal-0") for individual confirm/reject
|
||||
- `respondedAction` tracks user response per proposal (not per message)
|
||||
- `deleteMode` ('single' | 'future' | 'all') and `occurrenceDate` for recurring event deletion
|
||||
- `RecurringDeleteMode`: 'single' | 'future' | 'all' - delete modes for recurring events
|
||||
- `DeleteRecurringEventDTO`: mode, occurrenceDate? - DTO for recurring event deletion
|
||||
- `Conversation`: id, userId, createdAt?, updatedAt? (messages loaded separately via lazy loading)
|
||||
- `CreateUserDTO`: email, userName, password (for registration)
|
||||
- `LoginDTO`: identifier (email OR userName), password
|
||||
@@ -393,9 +397,9 @@ NODE_ENV=development # development = pretty logs, production = JSON
|
||||
- `dotenv` integration for environment variables
|
||||
- `ChatController`: sendMessage(), confirmEvent(), rejectEvent()
|
||||
- `ChatService`: processMessage() with test responses (create, update, delete actions), confirmEvent() handles all CRUD actions
|
||||
- `MongoEventRepository`: Full CRUD implemented (findById, findByUserId, findByDateRange, create, update, delete)
|
||||
- `MongoEventRepository`: Full CRUD implemented (findById, findByUserId, findByDateRange, create, update, delete, addExceptionDate)
|
||||
- `EventController`: Full CRUD (create, getById, getAll, getByDateRange, update, delete)
|
||||
- `EventService`: Full CRUD with recurring event expansion via recurrenceExpander
|
||||
- `EventService`: Full CRUD with recurring event expansion via recurrenceExpander, deleteRecurring() with three modes (single/future/all)
|
||||
- `utils/eventFormatters`: getWeeksOverview(), getMonthOverview() with German localization
|
||||
- `utils/recurrenceExpander`: expandRecurringEvents() using rrule library for RRULE parsing
|
||||
- `ChatController`: getConversations(), getConversation() with cursor-based pagination support
|
||||
@@ -405,7 +409,7 @@ NODE_ENV=development # development = pretty logs, production = JSON
|
||||
- `GPTAdapter`: Full implementation with OpenAI GPT (gpt-4o-mini model), function calling for calendar operations, collects multiple proposals per response
|
||||
- `ai/utils/`: Provider-agnostic shared utilities (systemPrompt, toolDefinitions, toolExecutor, eventFormatter)
|
||||
- `ai/utils/systemPrompt`: Includes RRULE documentation - AI knows to create separate events when times differ by day
|
||||
- `utils/recurrenceExpander`: Handles RRULE parsing, strips `RRULE:` prefix if present (AI may include it)
|
||||
- `utils/recurrenceExpander`: Handles RRULE parsing, strips `RRULE:` prefix if present (AI may include it), filters out exceptionDates
|
||||
- `logging/`: Structured logging with pino, pino-http middleware, @Logged decorator
|
||||
- All repositories and GPTAdapter decorated with @Logged for automatic method logging
|
||||
- CORS configured to allow X-User-Id header
|
||||
@@ -443,6 +447,7 @@ NODE_ENV=development # development = pretty logs, production = JSON
|
||||
- Tap-to-open modal overlay showing EventCards for selected day
|
||||
- Supports events from adjacent months visible in grid
|
||||
- Uses `useFocusEffect` for automatic reload on tab focus
|
||||
- DeleteEventModal integration for recurring event deletion with three modes
|
||||
- Chat screen fully functional with FlashList, message sending, and event confirm/reject
|
||||
- **Multiple event proposals**: AI can propose multiple events in one response
|
||||
- Arrow navigation between proposals with "Event X von Y" counter
|
||||
@@ -454,11 +459,12 @@ NODE_ENV=development # development = pretty logs, production = JSON
|
||||
- KeyboardAvoidingView for proper keyboard handling (iOS padding, Android height)
|
||||
- Auto-scroll to end on new messages and keyboard show
|
||||
- keyboardDismissMode="interactive" and keyboardShouldPersistTaps="handled"
|
||||
- `EventService`: getAll(), getById(), getByDateRange(), create(), update(), delete() - fully implemented
|
||||
- `ChatService`: sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation() - fully implemented with cursor pagination
|
||||
- `EventService`: getAll(), getById(), getByDateRange(), create(), update(), delete(mode, occurrenceDate) - fully implemented with recurring delete modes
|
||||
- `ChatService`: sendMessage(), confirmEvent(deleteMode, occurrenceDate), rejectEvent(), getConversations(), getConversation() - fully implemented with cursor pagination and recurring delete support
|
||||
- `EventCardBase`: Shared base component with event layout (header, date/time/recurring icons, description) - used by both EventCard and ProposedEventCard
|
||||
- `EventCard`: Uses EventCardBase + edit/delete buttons for calendar display
|
||||
- `ProposedEventCard`: Uses EventCardBase + confirm/reject buttons for chat proposals (supports create/update/delete actions)
|
||||
- `ProposedEventCard`: Uses EventCardBase + confirm/reject buttons for chat proposals (supports create/update/delete actions with deleteMode display)
|
||||
- `DeleteEventModal`: Unified delete confirmation modal - shows three options for recurring events (single/future/all), simple confirm for non-recurring
|
||||
- `Themes.tsx`: Theme definitions with THEMES object (defaultLight, defaultDark) including all color tokens (textPrimary, borderPrimary, eventIndicator, secondaryBg, shadowColor, etc.)
|
||||
- `EventsStore`: Zustand store with setEvents(), addEvent(), updateEvent(), deleteEvent() - stores ExpandedEvent[]
|
||||
- `ChatStore`: Zustand store with addMessage(), addMessages(), updateMessage(), clearMessages(), isWaitingForResponse/setWaitingForResponse() for typing indicator - loads from server on mount and persists across tab switches
|
||||
|
||||
Reference in New Issue
Block a user