Internal
Public Access
Add custom recurrence patterns (day-of-week, nth-weekday, day-of-month)
Recurring tasks were locked to fixed daily/weekly/biweekly/monthly/yearly intervals. The `custom` recurrence type and `recurrence_rule` field already existed in the model and API docs, but RRULE evaluation was a TODO stub that silently fell back to weekly, and no UI exposed the option. Implements real RRULE parsing via dateutil.rrule, and adds a builder UI (day-of-week checkboxes for weekly, day-of-month or Nth-weekday for monthly) so users can express patterns like "every other Wednesday" or "every second Tuesday" without hand-writing RRULE strings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
4d88d10382
commit
70dcc1f001
@@ -162,6 +162,74 @@ function closeDetail() {
|
||||
closeMobileMenus();
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Custom Recurrence Builder
|
||||
============================================ */
|
||||
|
||||
function toggleCustomRecurrenceBuilder(selectEl) {
|
||||
const form = selectEl.closest('form');
|
||||
const builder = form.querySelector('.custom-recurrence-builder');
|
||||
if (builder) {
|
||||
builder.classList.toggle('hidden', selectEl.value !== 'custom');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCustomFreqPanel(radioEl) {
|
||||
const builder = radioEl.closest('.custom-recurrence-builder');
|
||||
const weeklyPanel = builder.querySelector('.custom-weekly-panel');
|
||||
const monthlyPanel = builder.querySelector('.custom-monthly-panel');
|
||||
weeklyPanel.classList.toggle('hidden', radioEl.value !== 'weekly');
|
||||
monthlyPanel.classList.toggle('hidden', radioEl.value !== 'monthly');
|
||||
}
|
||||
|
||||
function assembleCustomRecurrenceRule(formEl) {
|
||||
const recurrenceSelect = formEl.querySelector('[name="recurrence"]');
|
||||
const ruleInput = formEl.querySelector('[name="recurrence_rule"]');
|
||||
if (!recurrenceSelect || !ruleInput) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (recurrenceSelect.value !== 'custom') {
|
||||
ruleInput.value = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
const builder = formEl.querySelector('.custom-recurrence-builder');
|
||||
if (!builder) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const freqMode = builder.querySelector('.custom-freq-mode:checked');
|
||||
const freq = freqMode ? freqMode.value : 'weekly';
|
||||
|
||||
if (freq === 'weekly') {
|
||||
const panel = builder.querySelector('.custom-weekly-panel');
|
||||
const interval = parseInt(panel.querySelector('.custom-interval').value, 10) || 1;
|
||||
const days = Array.from(panel.querySelectorAll('.custom-weekday:checked')).map(cb => cb.value);
|
||||
if (days.length === 0) {
|
||||
ruleInput.value = '';
|
||||
return true;
|
||||
}
|
||||
ruleInput.value = `FREQ=WEEKLY;INTERVAL=${interval};BYDAY=${days.join(',')}`;
|
||||
} else {
|
||||
const panel = builder.querySelector('.custom-monthly-panel');
|
||||
const interval = parseInt(panel.querySelector('.custom-interval').value, 10) || 1;
|
||||
const monthlyMode = panel.querySelector('.custom-monthly-mode:checked');
|
||||
const mode = monthlyMode ? monthlyMode.value : 'day';
|
||||
|
||||
if (mode === 'day') {
|
||||
const day = parseInt(panel.querySelector('.custom-bymonthday').value, 10) || 1;
|
||||
ruleInput.value = `FREQ=MONTHLY;INTERVAL=${interval};BYMONTHDAY=${day}`;
|
||||
} else {
|
||||
const ordinal = panel.querySelector('.custom-nth-ordinal').value;
|
||||
const weekday = panel.querySelector('.custom-nth-weekday').value;
|
||||
ruleInput.value = `FREQ=MONTHLY;INTERVAL=${interval};BYDAY=${ordinal}${weekday}`;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Mobile Navigation
|
||||
============================================ */
|
||||
|
||||
Reference in New Issue
Block a user