fixed the reccurring event delete bug
This commit is contained in:
@@ -419,19 +419,40 @@ router.delete('/:id', authMiddleware, async (req, res) => {
|
||||
if (!event) return res.status(404).json({ error: 'Not found' });
|
||||
const itm = await isToolManagerFn(req.schema, req.user);
|
||||
if (!itm && event.created_by !== req.user.id) return res.status(403).json({ error: 'Access denied' });
|
||||
const { recurringScope } = req.body || {};
|
||||
if (recurringScope === 'future' && event.recurrence_rule) {
|
||||
// Delete this event and all future occurrences with same creator/title
|
||||
await exec(req.schema, `
|
||||
DELETE FROM events WHERE created_by=$1 AND recurrence_rule IS NOT NULL
|
||||
AND title=$2 AND start_at >= $3
|
||||
`, [event.created_by, event.title, event.start_at]);
|
||||
} else if (recurringScope === 'all' && event.recurrence_rule) {
|
||||
// Delete present and future occurrences only — preserve past records
|
||||
await exec(req.schema, `
|
||||
DELETE FROM events WHERE created_by=$1 AND recurrence_rule IS NOT NULL AND title=$2 AND end_at >= NOW()
|
||||
`, [event.created_by, event.title]);
|
||||
const { recurringScope, occurrenceStart } = req.body || {};
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
|
||||
if (event.recurrence_rule && recurringScope === 'all') {
|
||||
// Delete the single base row — all virtual occurrences disappear with it
|
||||
await exec(req.schema, 'DELETE FROM events WHERE id=$1', [req.params.id]);
|
||||
|
||||
} else if (event.recurrence_rule && recurringScope === 'future') {
|
||||
// Truncate the series so it ends before this occurrence
|
||||
const occDate = new Date(occurrenceStart || event.start_at);
|
||||
if (occDate <= new Date(event.start_at)) {
|
||||
// Occurrence is at or before the base start — delete the whole series
|
||||
await exec(req.schema, 'DELETE FROM events WHERE id=$1', [req.params.id]);
|
||||
} else {
|
||||
const endBefore = new Date(occDate);
|
||||
endBefore.setDate(endBefore.getDate() - 1);
|
||||
const rule = { ...event.recurrence_rule };
|
||||
rule.ends = 'on';
|
||||
rule.endDate = `${endBefore.getFullYear()}-${pad(endBefore.getMonth()+1)}-${pad(endBefore.getDate())}`;
|
||||
delete rule.endCount;
|
||||
await exec(req.schema, 'UPDATE events SET recurrence_rule=$1 WHERE id=$2', [JSON.stringify(rule), req.params.id]);
|
||||
}
|
||||
|
||||
} else if (event.recurrence_rule && recurringScope === 'this') {
|
||||
// Add occurrence date to exceptions — base row and other occurrences are untouched
|
||||
const occDate = new Date(occurrenceStart || event.start_at);
|
||||
const occDateStr = `${occDate.getFullYear()}-${pad(occDate.getMonth()+1)}-${pad(occDate.getDate())}`;
|
||||
const rule = { ...event.recurrence_rule };
|
||||
const existing = Array.isArray(rule.exceptions) ? rule.exceptions : [];
|
||||
rule.exceptions = [...existing.filter(d => d !== occDateStr), occDateStr];
|
||||
await exec(req.schema, 'UPDATE events SET recurrence_rule=$1 WHERE id=$2', [JSON.stringify(rule), req.params.id]);
|
||||
|
||||
} else {
|
||||
// Non-recurring single delete
|
||||
await exec(req.schema, 'DELETE FROM events WHERE id=$1', [req.params.id]);
|
||||
}
|
||||
res.json({ success: true });
|
||||
|
||||
@@ -1060,6 +1060,9 @@ function expandRecurringEvent(ev, rangeStart, rangeEnd) {
|
||||
// Determine end condition
|
||||
const endDate = rule.ends === 'on' && rule.endDate ? new Date(rule.endDate + 'T23:59:59') : null;
|
||||
const endCount = rule.ends === 'after' ? (rule.endCount || 13) : null;
|
||||
const exceptions = new Set(rule.exceptions || []);
|
||||
const _pad = n => String(n).padStart(2, '0');
|
||||
const _toDateStr = d => `${d.getFullYear()}-${_pad(d.getMonth()+1)}-${_pad(d.getDate())}`;
|
||||
|
||||
// totalOcc counts ALL occurrences from origStart regardless of range,
|
||||
// so endCount is respected even when rangeStart is after the event's start.
|
||||
@@ -1083,6 +1086,7 @@ function expandRecurringEvent(ev, rangeStart, rangeEnd) {
|
||||
occ.setDate(weekStart.getDate() + dayNum);
|
||||
occ.setHours(origStart.getHours(), origStart.getMinutes(), origStart.getSeconds());
|
||||
if (!endDate || occ <= endDate) {
|
||||
if (!exceptions.has(_toDateStr(occ))) {
|
||||
totalOcc++;
|
||||
if (occ >= rangeStart && occ <= rangeEnd) {
|
||||
const occEnd = new Date(occ.getTime() + durMs);
|
||||
@@ -1090,13 +1094,16 @@ function expandRecurringEvent(ev, rangeStart, rangeEnd) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cur = step(cur);
|
||||
} else {
|
||||
if (!exceptions.has(_toDateStr(cur))) {
|
||||
totalOcc++;
|
||||
if (cur >= rangeStart && cur <= rangeEnd) {
|
||||
const occEnd = new Date(cur.getTime() + durMs);
|
||||
occurrences.push({...ev, start_at: cur.toISOString(), end_at: occEnd.toISOString(), _virtual: cur.toISOString() !== ev.start_at});
|
||||
}
|
||||
}
|
||||
cur = step(cur);
|
||||
}
|
||||
count++;
|
||||
@@ -1619,7 +1626,7 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
|
||||
// Virtual recurring occurrences carry their own start/end dates — overlay them so
|
||||
// the modal shows the correct occurrence time and isPast evaluates against the
|
||||
// occurrence's end_at, not the base event's first-occurrence end_at.
|
||||
if (e._virtual) { event.start_at = e.start_at; event.end_at = e.end_at; }
|
||||
if (e._virtual) { event.start_at = e.start_at; event.end_at = e.end_at; event._virtual = true; }
|
||||
setDetailEvent(event);
|
||||
} catch { toast('Failed to load event','error'); }
|
||||
};
|
||||
@@ -1631,7 +1638,7 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
|
||||
const e = deleteTarget;
|
||||
setDeleteTarget(null);
|
||||
try {
|
||||
await api.deleteEvent(e.id, scope);
|
||||
await api.deleteEvent(e.id, scope, e._virtual ? e.start_at : null);
|
||||
toast('Deleted','success');
|
||||
setPanel('calendar');
|
||||
setEditingEvent(null);
|
||||
|
||||
@@ -118,7 +118,7 @@ export const api = {
|
||||
getEvent: (id) => req('GET', `/schedule/${id}`),
|
||||
createEvent: (body) => req('POST', '/schedule', body), // body may include recurrenceRule: {freq,interval,byDay,ends,endDate,endCount}
|
||||
updateEvent: (id, body) => req('PATCH', `/schedule/${id}`, body),
|
||||
deleteEvent: (id, scope = 'this') => req('DELETE', `/schedule/${id}`, { recurringScope: scope }),
|
||||
deleteEvent: (id, scope = 'this', occurrenceStart = null) => req('DELETE', `/schedule/${id}`, { recurringScope: scope, occurrenceStart }),
|
||||
setAvailability: (id, response, note) => req('PUT', `/schedule/${id}/availability`, { response, note }),
|
||||
setAvailabilityNote: (id, note) => req('PATCH', `/schedule/${id}/availability/note`, { note }),
|
||||
deleteAvailability: (id) => req('DELETE', `/schedule/${id}/availability`),
|
||||
|
||||
Reference in New Issue
Block a user