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:
@@ -1,4 +1,5 @@
|
||||
import { Response } from "express";
|
||||
import { RecurringDeleteMode } from "@calchat/shared";
|
||||
import { EventService } from "../services";
|
||||
import { createLogger } from "../logging";
|
||||
import { AuthenticatedRequest } from "./AuthMiddleware";
|
||||
@@ -72,7 +73,10 @@ export class EventController {
|
||||
);
|
||||
res.json(events);
|
||||
} catch (error) {
|
||||
log.error({ error, start: req.query.start, end: req.query.end }, "Error getting events by range");
|
||||
log.error(
|
||||
{ error, start: req.query.start, end: req.query.end },
|
||||
"Error getting events by range",
|
||||
);
|
||||
res.status(500).json({ error: "Failed to get events" });
|
||||
}
|
||||
}
|
||||
@@ -97,6 +101,38 @@ export class EventController {
|
||||
|
||||
async delete(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const { mode, occurrenceDate } = req.query as {
|
||||
mode?: RecurringDeleteMode;
|
||||
occurrenceDate?: string;
|
||||
};
|
||||
|
||||
// If mode is specified, use deleteRecurring
|
||||
if (mode) {
|
||||
const result = await this.eventService.deleteRecurring(
|
||||
req.params.id,
|
||||
req.user!.userId,
|
||||
mode,
|
||||
occurrenceDate,
|
||||
);
|
||||
|
||||
// For 'all' mode or when event was completely deleted, return 204
|
||||
if (result === null && mode === "all") {
|
||||
res.status(204).send();
|
||||
return;
|
||||
}
|
||||
|
||||
// For 'single' or 'future' modes, return updated event
|
||||
if (result) {
|
||||
res.json(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// result is null but mode wasn't 'all' - event not found or was deleted
|
||||
res.status(204).send();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default behavior: delete completely
|
||||
const deleted = await this.eventService.delete(
|
||||
req.params.id,
|
||||
req.user!.userId,
|
||||
|
||||
Reference in New Issue
Block a user