diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index e69de29..0000000 diff --git a/IMPLEMENTATION_CHECKLIST.md b/IMPLEMENTATION_CHECKLIST.md deleted file mode 100644 index 266337e..0000000 --- a/IMPLEMENTATION_CHECKLIST.md +++ /dev/null @@ -1,352 +0,0 @@ -# โœ… Recurring Events Implementation Checklist - -## ๐Ÿ“‹ Implementation Status - -### โœ… COMPLETED (Ready to Use) - -#### Frontend Components -- [x] **RecurrenceEditor.tsx** - Complete UI component - - [x] Simple mode (Daily/Weekly/Monthly/Yearly) - - [x] Custom mode with full controls - - [x] DAILY: Interval support - - [x] WEEKLY: Day selection + interval - - [x] MONTHLY: Day of month (1-31) - - [x] YEARLY: Month + day selection - - [x] End conditions (Never/Until/Count) - - [x] Date validation with warnings - -#### Styles -- [x] **RecurrenceEditor.scss** - Complete styles - - [x] BEM methodology - - [x] Weekday buttons - - [x] Warning messages - - [x] Responsive layout - - [x] Integrated with design system - -#### Translations -- [x] **translations.json** - All languages - - [x] English (en) - - [x] French (fr) - - [x] Dutch (nl) - - [x] All UI strings - - [x] Month names - - [x] Validation warnings - -#### Tests -- [x] **RecurrenceEditor.test.tsx** - Full test suite - - [x] 15+ test cases - - [x] Component rendering - - [x] User interactions - - [x] All frequency types - - [x] Date validation - - [x] End conditions - - [x] Edge cases - -#### Documentation -- [x] **README_RECURRENCE.md** - Main entry point -- [x] **RECURRENCE_SUMMARY.md** - Quick reference -- [x] **RECURRENCE_IMPLEMENTATION.md** - Technical guide -- [x] **SCHEDULER_RECURRENCE_INTEGRATION.md** - Integration steps -- [x] **RECURRENCE_EXAMPLES.md** - Real-world examples -- [x] **IMPLEMENTATION_CHECKLIST.md** - This file - ---- - -## ๐Ÿ”„ PENDING (Needs Integration) - -### Integration Tasks - -- [ ] **Add RecurrenceEditor to EventModal** - - [ ] Import component in Scheduler.tsx - - [ ] Add recurrence state - - [ ] Add toggle button - - [ ] Reset state in useEffect - - [ ] Include in IcsEvent save - - [ ] Test in browser - -### Steps to Complete Integration - -Follow [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md): - -1. Import RecurrenceEditor -2. Add state management -3. Add UI toggle button -4. Include recurrence in save -5. Test end-to-end - -**Estimated time:** 30-45 minutes - ---- - -## ๐Ÿš€ Future Enhancements (Optional) - -### Not Required for MVP - -- [ ] **Advanced Patterns** - - [ ] BYSETPOS support ("1st Monday", "Last Friday") - - [ ] Position-based recurrence - - [ ] Complex patterns UI - -- [ ] **UI Improvements** - - [ ] Visual calendar preview of pattern - - [ ] Natural language summary ("Every 2 weeks on Monday") - - [ ] Recurring event icon in calendar view - -- [ ] **Editing Features** - - [ ] Edit single instance vs series UI - - [ ] Delete options (this/future/all) - - [ ] Exception handling UI - - [ ] RECURRENCE-ID support in UI - -- [ ] **Time Zone** - - [ ] Better time zone handling for UNTIL - - [ ] Time zone selector - - [ ] DST handling - ---- - -## ๐Ÿ“Š Feature Coverage - -### Supported โœ… - -| Feature | Status | Notes | -|---------|--------|-------| -| DAILY recurrence | โœ… | With interval | -| WEEKLY recurrence | โœ… | Multiple days | -| MONTHLY recurrence | โœ… | Day 1-31 | -| YEARLY recurrence | โœ… | Month + day | -| Never-ending | โœ… | No UNTIL or COUNT | -| Until date | โœ… | UNTIL parameter | -| After N occurrences | โœ… | COUNT parameter | -| Interval (every X) | โœ… | All frequencies | -| Date validation | โœ… | Feb 29, month lengths | -| Warning messages | โœ… | Invalid dates | -| Translations | โœ… | EN, FR, NL | -| Tests | โœ… | 15+ cases | -| Documentation | โœ… | Complete | - -### Not Supported (Yet) โŒ - -| Feature | Status | Reason | -|---------|--------|--------| -| nth occurrence | โŒ | Needs BYSETPOS UI | -| Last occurrence | โŒ | Needs BYSETPOS=-1 UI | -| Edit single instance | โŒ | Needs RECURRENCE-ID UI | -| Multiple months | โŒ | UI not implemented | -| Complex patterns | โŒ | Advanced use case | - ---- - -## ๐ŸŽฏ RFC 5545 Compliance - -### Implemented RRULE Parameters - -- [x] `FREQ` - Frequency (DAILY/WEEKLY/MONTHLY/YEARLY) -- [x] `INTERVAL` - Recurrence interval (every X periods) -- [x] `BYDAY` - Days of week (for WEEKLY) -- [x] `BYMONTHDAY` - Day of month (1-31) -- [x] `BYMONTH` - Month (1-12) -- [x] `COUNT` - Number of occurrences -- [x] `UNTIL` - End date - -### Not Implemented - -- [ ] `BYSETPOS` - Position in set (1st, 2nd, last) -- [ ] `BYYEARDAY` - Day of year -- [ ] `BYWEEKNO` - Week number -- [ ] `BYHOUR` - Hour (not applicable for calendar events) -- [ ] `BYMINUTE` - Minute (not applicable) -- [ ] `BYSECOND` - Second (not applicable) -- [ ] `WKST` - Week start (using default) - ---- - -## ๐Ÿงช Test Coverage - -### Unit Tests โœ… - -```bash -npm test RecurrenceEditor -``` - -**Coverage:** -- Component rendering: โœ… -- Simple mode selection: โœ… -- Custom mode UI: โœ… -- Weekly day toggles: โœ… -- Monthly day input: โœ… -- Yearly month/day: โœ… -- End conditions: โœ… -- Date validation: โœ… -- Warning messages: โœ… - -### Integration Tests โณ - -- [ ] Create recurring event in Scheduler -- [ ] Edit recurring event -- [ ] Delete recurring event -- [ ] View recurring instances in calendar -- [ ] Sync with CalDAV server -- [ ] Email invitations for recurring events - -### Manual Testing Checklist - -See [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md#testing) for full checklist. - -**Priority test cases:** -- [ ] Daily with interval 3 -- [ ] Weekly on Mon/Wed/Fri -- [ ] Monthly on 31st (edge case) -- [ ] Yearly on Feb 29 (leap year) -- [ ] Until date -- [ ] Count 10 occurrences -- [ ] Edit existing recurring event - ---- - -## ๐Ÿ“ฆ Files Summary - -### New Files Created (9) - -#### Code Files (3) -``` -src/frontend/apps/calendars/src/features/calendar/components/ -โ”œโ”€โ”€ RecurrenceEditor.tsx โœ… 377 lines -โ”œโ”€โ”€ RecurrenceEditor.scss โœ… 58 lines -โ””โ”€โ”€ __tests__/ - โ””โ”€โ”€ RecurrenceEditor.test.tsx โœ… 300+ lines -``` - -#### Documentation Files (6) -``` -(project root) -โ”œโ”€โ”€ README_RECURRENCE.md โœ… Main README -โ”œโ”€โ”€ RECURRENCE_SUMMARY.md โœ… Quick reference -โ”œโ”€โ”€ RECURRENCE_IMPLEMENTATION.md โœ… Technical docs -โ”œโ”€โ”€ SCHEDULER_RECURRENCE_INTEGRATION.md โœ… Integration guide -โ”œโ”€โ”€ RECURRENCE_EXAMPLES.md โœ… Usage examples -โ””โ”€โ”€ IMPLEMENTATION_CHECKLIST.md โœ… This file -``` - -### Modified Files (1) - -``` -src/frontend/apps/calendars/src/features/i18n/ -โ””โ”€โ”€ translations.json โœ… Added recurrence keys -``` - -**Total lines of code:** ~750+ -**Total documentation:** ~3000+ lines - ---- - -## ๐ŸŽ“ Knowledge Resources - -### Internal Documentation -1. [README_RECURRENCE.md](./README_RECURRENCE.md) - Start here -2. [RECURRENCE_SUMMARY.md](./RECURRENCE_SUMMARY.md) - Quick reference -3. [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md) - Deep dive -4. [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) - How to integrate -5. [RECURRENCE_EXAMPLES.md](./RECURRENCE_EXAMPLES.md) - Real examples - -### External Resources -- [RFC 5545 - iCalendar](https://datatracker.ietf.org/doc/html/rfc5545) -- [RRULE Specification](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) -- [ts-ics Documentation](https://github.com/Neuvernetzung/ts-ics) -- [Sabre/dav Documentation](https://sabre.io/dav/) - ---- - -## ๐Ÿšฆ Current Status - -### โœ… Ready for Integration - -**The RecurrenceEditor component is complete and production-ready!** - -All you need to do: -1. Follow [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) -2. Add 5 simple changes to Scheduler.tsx -3. Test in browser - -### ๐Ÿ“ˆ Progress - -``` -Implementation: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 100% COMPLETE -Integration: โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 0% PENDING -Testing: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 50% PARTIAL -Documentation: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 100% COMPLETE -``` - ---- - -## ๐ŸŽ‰ Next Steps - -### Immediate (Required) - -1. **Read integration guide** - โ†’ [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) - -2. **Integrate in Scheduler** - โ†’ Follow 5-step guide (30-45 min) - -3. **Test in browser** - โ†’ Create/edit recurring events - -### Soon (Recommended) - -1. **Run test suite** - ```bash - npm test RecurrenceEditor - ``` - -2. **Manual testing** - โ†’ Use [testing checklist](./RECURRENCE_IMPLEMENTATION.md#testing) - -3. **User feedback** - โ†’ Gather feedback from team - -### Later (Optional) - -1. **Consider enhancements** - โ†’ BYSETPOS patterns, edit single instance - -2. **Add visual preview** - โ†’ Calendar preview of recurrence pattern - -3. **Natural language summary** - โ†’ "Every 2 weeks on Monday and Friday" - ---- - -## ๐Ÿ“ž Support - -If you encounter issues during integration: - -1. Check [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) troubleshooting section -2. Review [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md) -3. Check browser console for errors -4. Verify ts-ics is correctly serializing RRULE - ---- - -## โœจ Summary - -**โœ… COMPLETE: Implementation** -- RecurrenceEditor component -- Styles & translations -- Tests & documentation - -**โณ PENDING: Integration** -- Add to Scheduler modal -- Test end-to-end - -**๐Ÿš€ READY: To Use** -- All patterns supported -- All validations working -- All documentation complete - -**Total effort to complete:** ~30-45 minutes of integration work - ---- - -**Let's integrate it! Start here:** [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) diff --git a/README_RECURRENCE.md b/README_RECURRENCE.md deleted file mode 100644 index 19d782f..0000000 --- a/README_RECURRENCE.md +++ /dev/null @@ -1,364 +0,0 @@ -# ๐Ÿ”„ Recurring Events Implementation - -**Complete implementation of recurring events for your CalDAV calendar application** - -## ๐Ÿš€ Quick Start - -The RecurrenceEditor component is **ready to use** in your application! - -```tsx -import { RecurrenceEditor } from '@/features/calendar/components/RecurrenceEditor'; - -function MyForm() { - const [recurrence, setRecurrence] = useState(); - - return ( - - ); -} -``` - -## ๐Ÿ“ฆ What's Included - -### โœ… Complete Implementation - -1. **RecurrenceEditor Component** - - Full UI for all recurrence types - - Date validation - - Multi-language support (EN/FR/NL) - -2. **Styles (SCSS)** - - BEM methodology - - Responsive design - - Integrated with your design system - -3. **Tests** - - 15+ test cases - - Full coverage - -4. **Documentation** - - Implementation guide - - Integration guide - - Examples - - Troubleshooting - -## ๐Ÿ“š Documentation Files - -| File | Purpose | -|------|---------| -| **[README_RECURRENCE.md](./README_RECURRENCE.md)** | **๐Ÿ‘ˆ START HERE** - This file | -| [RECURRENCE_SUMMARY.md](./RECURRENCE_SUMMARY.md) | Quick reference & overview | -| [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md) | Complete technical docs | -| [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) | How to integrate in Scheduler | -| [RECURRENCE_EXAMPLES.md](./RECURRENCE_EXAMPLES.md) | Real-world usage examples | - -## ๐ŸŽฏ Supported Recurrence Patterns - -| Type | Example | Status | -|------|---------|--------| -| **Daily** | Every day, every 3 days | โœ… Implemented | -| **Weekly** | Monday & Friday, every 2 weeks | โœ… Implemented | -| **Monthly** | 15th of each month | โœ… Implemented | -| **Yearly** | March 15th every year | โœ… Implemented | -| **End Conditions** | Never / Until date / After N times | โœ… Implemented | -| **Date Validation** | Feb 29, month lengths | โœ… Implemented | - -## ๐Ÿ“ Project Structure - -``` -src/frontend/apps/calendars/src/features/calendar/components/ -โ”œโ”€โ”€ RecurrenceEditor.tsx # Main component -โ”œโ”€โ”€ RecurrenceEditor.scss # Styles -โ””โ”€โ”€ __tests__/ - โ””โ”€โ”€ RecurrenceEditor.test.tsx # Tests - -src/frontend/apps/calendars/src/features/i18n/ -โ””โ”€โ”€ translations.json # Translations (EN/FR/NL) - -Documentation (project root): -โ”œโ”€โ”€ README_RECURRENCE.md # This file -โ”œโ”€โ”€ RECURRENCE_SUMMARY.md # Quick reference -โ”œโ”€โ”€ RECURRENCE_IMPLEMENTATION.md # Technical docs -โ”œโ”€โ”€ SCHEDULER_RECURRENCE_INTEGRATION.md # Integration guide -โ””โ”€โ”€ RECURRENCE_EXAMPLES.md # Usage examples -``` - -## ๐Ÿ”ง Integration Steps - -### Step 1: Use the Component - -The component is already created! Just import and use it: - -```tsx -import { RecurrenceEditor } from '@/features/calendar/components/RecurrenceEditor'; -import type { IcsRecurrenceRule } from 'ts-ics'; - -const [recurrence, setRecurrence] = useState(); - - -``` - -### Step 2: Include in IcsEvent - -```tsx -const event: IcsEvent = { - uid: crypto.randomUUID(), - summary: "Team Meeting", - start: { date: new Date() }, - end: { date: new Date() }, - recurrenceRule: recurrence, // โ† Add this -}; -``` - -### Step 3: That's It! - -CalDAV handles everything else: -- โœ… Stores RRULE in .ics file -- โœ… Expands recurring instances -- โœ… Syncs with other calendar apps - -## ๐ŸŽจ UI Preview - -### Simple Mode -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Repeat: [Daily โ–ผ] โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Custom Mode - Weekly -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Repeat every [2] [weeks โ–ผ] โ”‚ -โ”‚ โ”‚ -โ”‚ Repeat on: โ”‚ -โ”‚ [M] [T] [W] [T] [F] [S] [S] โ”‚ -โ”‚ โœ“ โœ“ โ”‚ -โ”‚ โ”‚ -โ”‚ Ends: โ”‚ -โ”‚ โ—‹ Never โ”‚ -โ”‚ โ—‹ On [2025-12-31] โ”‚ -โ”‚ โฆฟ After [10] occurrences โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Custom Mode - Monthly with Warning -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Repeat every [1] [months โ–ผ] โ”‚ -โ”‚ โ”‚ -โ”‚ Repeat on day: โ”‚ -โ”‚ Day [30] โ”‚ -โ”‚ โ”‚ -โ”‚ โš ๏ธ This month has at most 30 days โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## ๐ŸŒ Internationalization - -Fully translated in: -- ๐Ÿ‡ฌ๐Ÿ‡ง English -- ๐Ÿ‡ซ๐Ÿ‡ท French -- ๐Ÿ‡ณ๐Ÿ‡ฑ Dutch - -All UI strings, month names, and warning messages. - -## ๐Ÿงช Testing - -Run the test suite: - -```bash -npm test RecurrenceEditor -``` - -Test coverage: -- โœ… Component rendering -- โœ… User interactions -- โœ… All frequency types -- โœ… Date validation -- โœ… End conditions -- โœ… Edge cases - -## ๐Ÿ“– Common Use Cases - -### 1. Daily Standup (Every Weekday) - -```typescript -{ - frequency: 'WEEKLY', - byDay: [ - { day: 'MO' }, - { day: 'TU' }, - { day: 'WE' }, - { day: 'TH' }, - { day: 'FR' } - ] -} -``` - -**RRULE:** `FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR` - -### 2. Bi-Weekly Sprint Planning - -```typescript -{ - frequency: 'WEEKLY', - interval: 2, - byDay: [{ day: 'MO' }], - count: 10 -} -``` - -**RRULE:** `FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;COUNT=10` - -### 3. Monthly Team Meeting (15th) - -```typescript -{ - frequency: 'MONTHLY', - byMonthDay: [15] -} -``` - -**RRULE:** `FREQ=MONTHLY;BYMONTHDAY=15` - -### 4. Annual Birthday - -```typescript -{ - frequency: 'YEARLY', - byMonth: [3], - byMonthDay: [15] -} -``` - -**RRULE:** `FREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15` - -See [RECURRENCE_EXAMPLES.md](./RECURRENCE_EXAMPLES.md) for 10+ detailed examples! - -## ๐Ÿ” Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ RecurrenceEditor Component (React) โ”‚ -โ”‚ โ†“ โ”‚ -โ”‚ IcsRecurrenceRule (ts-ics) โ”‚ -โ”‚ โ†“ โ”‚ -โ”‚ RRULE string (RFC 5545) โ”‚ -โ”‚ โ†“ โ”‚ -โ”‚ .ics file (CalDAV) โ”‚ -โ”‚ โ†“ โ”‚ -โ”‚ Sabre/dav Server (PHP) โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**No backend changes needed!** Everything is handled by CalDAV standard. - -## ๐ŸŽฏ Next Steps - -### To Use in Your App - -1. Read [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) -2. Follow the 5-step integration guide -3. Test with your event modal - -### To Learn More - -1. Browse [RECURRENCE_EXAMPLES.md](./RECURRENCE_EXAMPLES.md) for real-world scenarios -2. Check [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md) for deep dive -3. Review [RECURRENCE_SUMMARY.md](./RECURRENCE_SUMMARY.md) for quick reference - -## โ“ FAQ - -### Q: Does this work with existing CalDAV events? - -**A:** Yes! The component uses standard RRULE format compatible with all CalDAV clients (Apple Calendar, Google Calendar, Outlook, etc.). - -### Q: Can users edit existing recurring events? - -**A:** Yes! The component loads existing recurrence rules from events and allows editing the entire series. - -### Q: What about editing single instances? - -**A:** Not yet implemented in UI. CalDAV supports it via RECURRENCE-ID, but the UI for "Edit this occurrence" vs "Edit series" is a future enhancement. - -### Q: Do recurring events sync with other calendar apps? - -**A:** Yes! All patterns are standard RFC 5545 RRULE format. - -### Q: Can I create "First Monday of month" patterns? - -**A:** Not yet. That requires BYSETPOS which is a future enhancement. - -### Q: What happens with February 30th? - -**A:** The UI shows a warning, and CalDAV will skip occurrences on invalid dates. - -## ๐Ÿ› Troubleshooting - -### Events not appearing as recurring - -1. Check browser console for errors -2. Verify `recurrenceRule` is in IcsEvent object -3. Check CalDAV server supports RRULE -4. Inspect .ics file in network tab - -### Translations not showing - -1. Verify translations.json includes new keys -2. Check i18n is initialized -3. Reload page after adding translations - -### Styles not applying - -1. Ensure RecurrenceEditor.scss is imported in globals.scss -2. Check for CSS conflicts -3. Verify BEM class names - -See [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md#troubleshooting) for more help. - -## ๐Ÿ“Š Feature Matrix - -| Feature | Status | Notes | -|---------|--------|-------| -| Daily recurrence | โœ… | With interval | -| Weekly recurrence | โœ… | Multiple days | -| Monthly recurrence | โœ… | Day 1-31 | -| Yearly recurrence | โœ… | Month + day | -| Never ends | โœ… | | -| Until date | โœ… | | -| After N times | โœ… | | -| Date validation | โœ… | Feb 29, month lengths | -| Translations | โœ… | EN, FR, NL | -| Tests | โœ… | 15+ cases | -| nth occurrence | โŒ | Future (BYSETPOS) | -| Edit single instance | โŒ | Future (RECURRENCE-ID UI) | - -## ๐ŸŽ“ Resources - -- [RFC 5545 - iCalendar](https://datatracker.ietf.org/doc/html/rfc5545) -- [RRULE Spec](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) -- [ts-ics Library](https://github.com/Neuvernetzung/ts-ics) -- [Sabre/dav Docs](https://sabre.io/dav/) - -## ๐Ÿ™ Credits - -Implementation follows RFC 5545 (iCalendar) standard and integrates with: -- ts-ics for ICS generation -- tsdav for CalDAV client -- @event-calendar/core for calendar UI -- Sabre/dav for CalDAV server - -## ๐Ÿ“ License - -Part of the calendars application. - ---- - -## ๐Ÿš€ Ready to Get Started? - -1. **Quick integration:** Read [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) -2. **See examples:** Check [RECURRENCE_EXAMPLES.md](./RECURRENCE_EXAMPLES.md) -3. **Deep dive:** Read [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md) - -**The RecurrenceEditor is production-ready and waiting for you to integrate it!** ๐ŸŽ‰ diff --git a/RECURRENCE_EXAMPLES.md b/RECURRENCE_EXAMPLES.md deleted file mode 100644 index 210cd2c..0000000 --- a/RECURRENCE_EXAMPLES.md +++ /dev/null @@ -1,523 +0,0 @@ -# Recurring Events - Usage Examples - -## Real-World Scenarios - -This document provides concrete examples of how to use the RecurrenceEditor for common recurring event patterns. - ---- - -## ๐Ÿ“… Example 1: Daily Standup Meeting - -**Requirement:** Team standup every weekday (Monday-Friday) at 9:00 AM - -### Configuration - -```typescript -const recurrence: IcsRecurrenceRule = { - frequency: 'WEEKLY', - interval: 1, - byDay: [ - { day: 'MO' }, - { day: 'TU' }, - { day: 'WE' }, - { day: 'TH' }, - { day: 'FR' } - ] -}; -``` - -### UI Steps -1. Select "Custom..." -2. Choose "weeks" frequency -3. Click all weekday buttons: M T W T F -4. Leave interval at 1 -5. Select "Never" for end condition - -### Generated RRULE -``` -RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR -``` - ---- - -## ๐Ÿ“… Example 2: Bi-Weekly Sprint Planning - -**Requirement:** Sprint planning every 2 weeks on Monday at 10:00 AM for 10 sprints - -### Configuration - -```typescript -const recurrence: IcsRecurrenceRule = { - frequency: 'WEEKLY', - interval: 2, - byDay: [{ day: 'MO' }], - count: 10 -}; -``` - -### UI Steps -1. Select "Custom..." -2. Set interval to "2" -3. Choose "weeks" frequency -4. Click "M" (Monday) -5. Select "After" and enter "10" occurrences - -### Generated RRULE -``` -RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;COUNT=10 -``` - -### Resulting Dates (starting Jan 6, 2025) -- Jan 6, 2025 -- Jan 20, 2025 -- Feb 3, 2025 -- Feb 17, 2025 -- Mar 3, 2025 -- Mar 17, 2025 -- Mar 31, 2025 -- Apr 14, 2025 -- Apr 28, 2025 -- May 12, 2025 (last occurrence) - ---- - -## ๐Ÿ“… Example 3: Monthly All-Hands Meeting - -**Requirement:** First Monday of each month at 2:00 PM - -โš ๏ธ **Note:** "First Monday" pattern requires BYSETPOS (not yet implemented). -**Workaround:** Use specific date if consistent, or create manually each month. - -**Alternative - Specific Day of Month:** - -### Configuration - -```typescript -const recurrence: IcsRecurrenceRule = { - frequency: 'MONTHLY', - interval: 1, - byMonthDay: [5] // 5th of every month -}; -``` - -### UI Steps -1. Select "Custom..." -2. Choose "months" frequency -3. Enter "5" for day of month -4. Select "Never" - -### Generated RRULE -``` -RRULE:FREQ=MONTHLY;BYMONTHDAY=5 -``` - ---- - -## ๐Ÿ“… Example 4: Quarterly Business Review - -**Requirement:** Last day of March, June, September, December at 3:00 PM - -โš ๏ธ **Current Implementation:** Set up as 4 separate yearly events. - -**Future Implementation:** Would use BYMONTH with multiple months. - -### Configuration (Workaround) - -Create 4 separate yearly events: - -**Q1 (March 31):** -```typescript -{ - frequency: 'YEARLY', - interval: 1, - byMonth: [3], - byMonthDay: [31] -} -``` - -**Q2 (June 30):** -```typescript -{ - frequency: 'YEARLY', - interval: 1, - byMonth: [6], - byMonthDay: [30] -} -``` - -**Q3 (September 30):** -```typescript -{ - frequency: 'YEARLY', - interval: 1, - byMonth: [9], - byMonthDay: [30] -} -``` - -**Q4 (December 31):** -```typescript -{ - frequency: 'YEARLY', - interval: 1, - byMonth: [12], - byMonthDay: [31] -} -``` - ---- - -## ๐Ÿ“… Example 5: Birthday Reminder - -**Requirement:** Annual reminder on March 15th - -### Configuration - -```typescript -const recurrence: IcsRecurrenceRule = { - frequency: 'YEARLY', - interval: 1, - byMonth: [3], - byMonthDay: [15] -}; -``` - -### UI Steps -1. Select "Custom..." -2. Choose "years" frequency -3. Select "March" from month dropdown -4. Enter "15" for day -5. Select "Never" - -### Generated RRULE -``` -RRULE:FREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15 -``` - ---- - -## ๐Ÿ“… Example 6: Payroll Processing - -**Requirement:** 1st and 15th of every month - -โš ๏ธ **Current Implementation:** Create as 2 separate events: - -**First event (1st):** -```typescript -{ - frequency: 'MONTHLY', - interval: 1, - byMonthDay: [1] -} -``` - -**Second event (15th):** -```typescript -{ - frequency: 'MONTHLY', - interval: 1, - byMonthDay: [15] -} -``` - -### UI Steps (for each) -1. Select "Custom..." -2. Choose "months" -3. Enter day (1 or 15) -4. Select "Never" - ---- - -## ๐Ÿ“… Example 7: Project Deadline (Fixed End Date) - -**Requirement:** Daily check-ins until project ends on December 31, 2025 - -### Configuration - -```typescript -const recurrence: IcsRecurrenceRule = { - frequency: 'DAILY', - interval: 1, - until: { - type: 'DATE', - date: new Date('2025-12-31') - } -}; -``` - -### UI Steps -1. Select "Custom..." -2. Choose "days" frequency -3. Keep interval at 1 -4. Select "On" -5. Choose date: 2025-12-31 - -### Generated RRULE -``` -RRULE:FREQ=DAILY;UNTIL=20251231T235959Z -``` - ---- - -## ๐Ÿ“… Example 8: Gym Schedule (Mon/Wed/Fri) - -**Requirement:** Gym sessions 3 times per week - -### Configuration - -```typescript -const recurrence: IcsRecurrenceRule = { - frequency: 'WEEKLY', - interval: 1, - byDay: [ - { day: 'MO' }, - { day: 'WE' }, - { day: 'FR' } - ] -}; -``` - -### UI Steps -1. Select "Custom..." -2. Choose "weeks" -3. Click M, W, F buttons -4. Select "Never" - -### Generated RRULE -``` -RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR -``` - ---- - -## ๐Ÿ“… Example 9: Leap Year Celebration - -**Requirement:** February 29th celebration (only on leap years) - -### Configuration - -```typescript -const recurrence: IcsRecurrenceRule = { - frequency: 'YEARLY', - interval: 1, - byMonth: [2], - byMonthDay: [29] -}; -``` - -### UI Steps -1. Select "Custom..." -2. Choose "years" -3. Select "February" -4. Enter "29" -5. โš ๏ธ Warning appears: "This date (Feb 29) only exists in leap years" -6. Select "Never" - -### Generated RRULE -``` -RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=29 -``` - -### Occurrences -- Feb 29, 2024 โœ… -- Feb 29, 2028 โœ… -- Feb 29, 2032 โœ… -- (Skips 2025, 2026, 2027, 2029, 2030, 2031) - ---- - -## ๐Ÿ“… Example 10: Seasonal Team Offsite - -**Requirement:** First day of each season (March, June, September, December) - -Create 4 separate yearly events or use the pattern: - -### Configuration (One event, workaround) - -**For March 1:** -```typescript -{ - frequency: 'YEARLY', - byMonth: [3], - byMonthDay: [1] -} -``` - -Repeat for months 6, 9, 12 as separate events. - -**Better approach when BYMONTH allows multiple values:** -```typescript -// Future implementation -{ - frequency: 'YEARLY', - byMonth: [3, 6, 9, 12], // Not yet supported in UI - byMonthDay: [1] -} -``` - ---- - -## ๐ŸŽฏ Complex Patterns Comparison - -| Pattern | Status | Implementation | -|---------|--------|----------------| -| "Every day" | โœ… Supported | `FREQ=DAILY` | -| "Every weekday" | โœ… Supported | `FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR` | -| "Every Monday" | โœ… Supported | `FREQ=WEEKLY;BYDAY=MO` | -| "1st of every month" | โœ… Supported | `FREQ=MONTHLY;BYMONTHDAY=1` | -| "Last day of month" | โœ… Supported (with caveat) | `FREQ=MONTHLY;BYMONTHDAY=31` | -| "1st Monday of month" | โŒ Future | Needs BYDAY + BYSETPOS | -| "Last Friday of month" | โŒ Future | Needs BYDAY + BYSETPOS=-1 | -| "Every 2 hours" | โŒ Not applicable | Events, not intraday recurrence | - ---- - -## ๐Ÿงช Testing Patterns - -### Test Case 1: Edge Case - February 30th - -```typescript -// User selects: -{ - frequency: 'YEARLY', - byMonth: [2], - byMonthDay: [30] -} -``` - -**Expected:** โš ๏ธ Warning: "February has at most 29 days" -**Behavior:** Event will never occur (no year has Feb 30) - -### Test Case 2: Month Overflow - April 31st - -```typescript -// User selects: -{ - frequency: 'MONTHLY', - byMonthDay: [31] -} -``` - -**Occurrences:** -- January 31 โœ… -- February 31 โŒ (skipped) -- March 31 โœ… -- April 31 โŒ (skipped - only 30 days) -- May 31 โœ… -- June 31 โŒ (skipped - only 30 days) -- July 31 โœ… - -**Warning shown for months with 30 days when setting up yearly recurrence** - ---- - -## ๐Ÿ“‹ Quick Reference - -### Frequency Types - -```typescript -frequency: 'DAILY' // Every day -frequency: 'WEEKLY' // Every week (specify days) -frequency: 'MONTHLY' // Every month (specify day 1-31) -frequency: 'YEARLY' // Every year (specify month + day) -``` - -### Intervals - -```typescript -interval: 1 // Every [frequency] -interval: 2 // Every 2 [frequency] -interval: 3 // Every 3 [frequency] -// etc. -``` - -### Days of Week (WEEKLY) - -```typescript -byDay: [ - { day: 'MO' }, // Monday - { day: 'TU' }, // Tuesday - { day: 'WE' }, // Wednesday - { day: 'TH' }, // Thursday - { day: 'FR' }, // Friday - { day: 'SA' }, // Saturday - { day: 'SU' } // Sunday -] -``` - -### Day of Month (MONTHLY, YEARLY) - -```typescript -byMonthDay: [15] // 15th of month -byMonthDay: [1] // 1st of month -byMonthDay: [31] // 31st of month (with caveats) -``` - -### Month (YEARLY) - -```typescript -byMonth: [1] // January -byMonth: [2] // February -// ... -byMonth: [12] // December -``` - -### End Conditions - -```typescript -// Never ends -(no count or until) - -// Ends on date -until: { - type: 'DATE', - date: new Date('2025-12-31') -} - -// Ends after N occurrences -count: 10 -``` - ---- - -## ๐Ÿ’ก Tips & Best Practices - -### 1. Use Simple Mode for Common Patterns - -Simple mode is sufficient for: -- Daily recurrence (every day) -- Weekly recurrence (every week, same days) -- Monthly recurrence (same date each month) -- Yearly recurrence (same date each year) - -### 2. Use Custom Mode for Advanced Patterns - -Custom mode is needed for: -- Intervals > 1 (every 2 weeks, every 3 months) -- Multiple days per week -- End dates or occurrence counts -- Specific validation - -### 3. Date Validation - -Always check for warnings when selecting: -- February dates (29, 30, 31) -- Month-end dates for monthly recurrence -- Day 31 for months with 30 days - -### 4. CalDAV Compatibility - -All patterns generated by RecurrenceEditor are standard RRULE format compatible with: -- Apple Calendar -- Google Calendar -- Microsoft Outlook -- Mozilla Thunderbird -- Any RFC 5545 compliant calendar - ---- - -## ๐Ÿ”— Related Documentation - -- [RECURRENCE_IMPLEMENTATION.md](./RECURRENCE_IMPLEMENTATION.md) - Technical implementation -- [SCHEDULER_RECURRENCE_INTEGRATION.md](./SCHEDULER_RECURRENCE_INTEGRATION.md) - Integration guide -- [RECURRENCE_SUMMARY.md](./RECURRENCE_SUMMARY.md) - Quick reference diff --git a/RECURRENCE_IMPLEMENTATION.md b/RECURRENCE_IMPLEMENTATION.md deleted file mode 100644 index 7f2e6c5..0000000 --- a/RECURRENCE_IMPLEMENTATION.md +++ /dev/null @@ -1,406 +0,0 @@ -# Recurring Events Implementation Guide - -## Overview - -This document describes the complete implementation of recurring events in the CalDAV calendar application. The implementation follows the iCalendar RFC 5545 standard for RRULE (Recurrence Rule). - -## Architecture - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Frontend (Next.js) โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ RecurrenceEditor Component โ”‚ -โ”‚ โ”œโ”€ UI for frequency selection (DAILY/WEEKLY/MONTHLY/YEARLY) โ”‚ -โ”‚ โ”œโ”€ Interval input โ”‚ -โ”‚ โ”œโ”€ Day/Month/Date selection โ”‚ -โ”‚ โ””โ”€ End conditions (never/until/count) โ”‚ -โ”‚ โ”‚ -โ”‚ EventCalendarAdapter โ”‚ -โ”‚ โ”œโ”€ Converts IcsRecurrenceRule to RRULE string โ”‚ -โ”‚ โ””โ”€ Parses RRULE to IcsRecurrenceRule โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ ts-ics Library โ”‚ -โ”‚ IcsRecurrenceRule interface โ”‚ -โ”‚ โ”œโ”€ frequency: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY' โ”‚ -โ”‚ โ”œโ”€ interval?: number โ”‚ -โ”‚ โ”œโ”€ byDay?: IcsWeekDay[] โ”‚ -โ”‚ โ”œโ”€ byMonthDay?: number[] โ”‚ -โ”‚ โ”œโ”€ byMonth?: number[] โ”‚ -โ”‚ โ”œโ”€ count?: number โ”‚ -โ”‚ โ””โ”€ until?: IcsDateObject โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ CalDAV Protocol โ”‚ -โ”‚ RRULE property in VEVENT โ”‚ -โ”‚ Example: RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,FR โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Sabre/dav Server (PHP) โ”‚ -โ”‚ Stores and serves iCalendar (.ics) files โ”‚ -โ”‚ Handles recurring event expansion โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## Component Structure - -### RecurrenceEditor Component - -Location: `src/features/calendar/components/RecurrenceEditor.tsx` - -**Props:** -```typescript -interface RecurrenceEditorProps { - value?: IcsRecurrenceRule; - onChange: (rule: IcsRecurrenceRule | undefined) => void; -} -``` - -**Features:** -- โœ… Simple mode: Quick selection (None, Daily, Weekly, Monthly, Yearly) -- โœ… Custom mode: Full control over all recurrence parameters -- โœ… DAILY: Interval support (every X days) -- โœ… WEEKLY: Interval + day selection (MO, TU, WE, TH, FR, SA, SU) -- โœ… MONTHLY: Day of month (1-31) with validation -- โœ… YEARLY: Month + day selection with leap year support -- โœ… End conditions: Never / Until date / After N occurrences -- โœ… Date validation warnings (Feb 30th, Feb 29th leap year, etc.) - -### Example Usage - -```tsx -import { RecurrenceEditor } from '@/features/calendar/components/RecurrenceEditor'; -import { useState } from 'react'; -import type { IcsRecurrenceRule } from 'ts-ics'; - -function EventForm() { - const [recurrence, setRecurrence] = useState(); - - return ( -
- {/* Other event fields */} - - - - {/* Save button */} - - ); -} -``` - -## Integration with Scheduler - -To integrate the RecurrenceEditor into the existing Scheduler modal, add the following: - -### 1. Add recurrence state - -```typescript -// In EventModal component -const [recurrence, setRecurrence] = useState( - event?.recurrenceRule -); -``` - -### 2. Add RecurrenceEditor to the form - -```tsx -import { RecurrenceEditor } from '../RecurrenceEditor'; - -// In the modal JSX, after location/description fields - -``` - -### 3. Include recurrence in event save - -```typescript -const icsEvent: IcsEvent = { - // ... existing fields - recurrenceRule: recurrence, -}; -``` - -### 4. Reset recurrence when modal opens - -```typescript -useEffect(() => { - // ... existing resets - setRecurrence(event?.recurrenceRule); -}, [event]); -``` - -## RRULE Examples - -### Daily Recurrence - -**Every day:** -``` -RRULE:FREQ=DAILY;INTERVAL=1 -``` - -**Every 3 days:** -``` -RRULE:FREQ=DAILY;INTERVAL=3 -``` - -**Daily for 10 occurrences:** -``` -RRULE:FREQ=DAILY;COUNT=10 -``` - -**Daily until Dec 31, 2025:** -``` -RRULE:FREQ=DAILY;UNTIL=20251231T235959Z -``` - -### Weekly Recurrence - -**Every week on Monday:** -``` -RRULE:FREQ=WEEKLY;BYDAY=MO -``` - -**Every 2 weeks on Monday and Friday:** -``` -RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,FR -``` - -**Weekly on weekdays (Mon-Fri):** -``` -RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR -``` - -### Monthly Recurrence - -**Every month on the 15th:** -``` -RRULE:FREQ=MONTHLY;BYMONTHDAY=15 -``` - -**Every 3 months on the 1st:** -``` -RRULE:FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=1 -``` - -**Monthly on the last day (31st with fallback):** -``` -RRULE:FREQ=MONTHLY;BYMONTHDAY=31 -``` -Note: For months with fewer than 31 days, most implementations skip that occurrence. - -### Yearly Recurrence - -**Every year on March 15th:** -``` -RRULE:FREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15 -``` - -**Every year on February 29th (leap years only):** -``` -RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=29 -``` - -**Every 2 years on December 25th:** -``` -RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=12;BYMONTHDAY=25 -``` - -## Date Validation - -The RecurrenceEditor includes smart validation for invalid dates: - -### February 30th/31st -**Warning:** "February has at most 29 days" - -### February 29th -**Warning:** "This date (Feb 29) only exists in leap years" - -### April 31st, June 31st, etc. -**Warning:** "This month has at most 30 days" - -### Day > 31 -**Warning:** "Day must be between 1 and 31" - -## IcsRecurrenceRule Interface (ts-ics) - -```typescript -interface IcsRecurrenceRule { - frequency: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY'; - interval?: number; // Default: 1 - count?: number; // Number of occurrences - until?: IcsDateObject; // End date - byDay?: IcsWeekDay[]; // Days of week (WEEKLY) - byMonthDay?: number[]; // Days of month (MONTHLY, YEARLY) - byMonth?: number[]; // Months (YEARLY) - bySetPos?: number[]; // Position (e.g., 1st Monday) - weekStart?: IcsWeekDay; // Week start day -} - -type IcsWeekDay = 'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU'; -``` - -## Backend Considerations - -The Django backend **does not need modifications** for recurring events. CalDAV handles recurrence natively: - -1. **Storage:** RRULE is stored as a property in the VEVENT within the .ics file -2. **Expansion:** Sabre/dav handles recurring event expansion when clients query date ranges -3. **Modifications:** Individual instances can be modified by creating exception events with RECURRENCE-ID - -### Example .ics file with recurrence - -```ics -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:-//CalDavService//NONSGML v1.0//EN -METHOD:PUBLISH -BEGIN:VEVENT -UID:abc-123-def-456 -DTSTART:20260125T140000Z -DTEND:20260125T150000Z -SUMMARY:Weekly Team Meeting -RRULE:FREQ=WEEKLY;BYDAY=MO;UNTIL=20261231T235959Z -ORGANIZER;CN=Alice:mailto:alice@example.com -ATTENDEE;CN=Bob;PARTSTAT=NEEDS-ACTION:mailto:bob@example.com -END:VEVENT -END:VCALENDAR -``` - -## Testing - -### Manual Testing Checklist - -- [ ] Daily recurrence with interval 1, 3, 7 -- [ ] Weekly recurrence with single day (Monday) -- [ ] Weekly recurrence with multiple days (Mon, Wed, Fri) -- [ ] Weekly recurrence with interval 2 -- [ ] Monthly recurrence on day 1, 15, 31 -- [ ] Monthly recurrence with February validation -- [ ] Yearly recurrence on Jan 1, Dec 25 -- [ ] Yearly recurrence on Feb 29 with leap year warning -- [ ] Never-ending recurrence -- [ ] Until date recurrence -- [ ] Count-based recurrence (10 occurrences) -- [ ] Edit recurring event -- [ ] Delete recurring event - -### Test Cases - -```typescript -// Test: Weekly on Monday and Friday -const rule: IcsRecurrenceRule = { - frequency: 'WEEKLY', - interval: 1, - byDay: [{ day: 'MO' }, { day: 'FR' }], -}; -// Expected RRULE: FREQ=WEEKLY;BYDAY=MO,FR - -// Test: Monthly on 31st (handles months with fewer days) -const rule: IcsRecurrenceRule = { - frequency: 'MONTHLY', - interval: 1, - byMonthDay: [31], -}; -// Expected RRULE: FREQ=MONTHLY;BYMONTHDAY=31 - -// Test: Yearly on Feb 29 -const rule: IcsRecurrenceRule = { - frequency: 'YEARLY', - interval: 1, - byMonth: [2], - byMonthDay: [29], - count: 10, -}; -// Expected RRULE: FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=29;COUNT=10 -``` - -## Translations - -All UI strings are internationalized (i18n) with support for: -- ๐Ÿ‡ฌ๐Ÿ‡ง English -- ๐Ÿ‡ซ๐Ÿ‡ท French -- ๐Ÿ‡ณ๐Ÿ‡ฑ Dutch - -Translation keys are defined in `src/features/i18n/translations.json`: - -```json -{ - "calendar": { - "recurrence": { - "label": "Repeat", - "daily": "Daily", - "weekly": "Weekly", - "monthly": "Monthly", - "yearly": "Yearly", - "repeatOnDay": "Repeat on day", - "repeatOnDate": "Repeat on date", - "dayOfMonth": "Day", - "months": { - "january": "January", - "february": "February", - ... - }, - "warnings": { - "februaryMax": "February has at most 29 days", - "leapYear": "This date (Feb 29) only exists in leap years", - ... - } - } - } -} -``` - -## Styling - -Styles are in `RecurrenceEditor.scss` using BEM methodology: - -```scss -.recurrence-editor { - &__label { ... } - &__weekday-button { ... } - &__weekday-button--selected { ... } - &__warning { ... } -} - -.recurrence-editor-layout { - &--row { ... } - &--gap-1rem { ... } - &--flex-wrap { ... } -} -``` - -## Known Limitations - -1. **No BYDAY with position** (e.g., "2nd Tuesday of month") - - Future enhancement - - Requires UI for "1st/2nd/3rd/4th/last" + weekday selection - -2. **No BYSETPOS** (complex patterns) - - e.g., "Last Friday of every month" - - Requires advanced UI - -3. **Time zone handling** - - UNTIL dates are converted to UTC - - Local time events use floating time - -4. **Recurring event modifications** - - Editing single instance creates exception (RECURRENCE-ID) - - Not yet implemented in UI (future work) - -## Future Enhancements - -- [ ] Visual calendar preview of recurrence pattern -- [ ] Natural language summary ("Every 2 weeks on Monday and Friday") -- [ ] Support for BYSETPOS (nth occurrence patterns) -- [ ] Exception handling UI for editing single instances -- [ ] Recurring event series deletion options (this only / this and future / all) - -## References - -- [RFC 5545 - iCalendar](https://datatracker.ietf.org/doc/html/rfc5545) -- [RRULE Specification](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) -- [ts-ics Documentation](https://github.com/Neuvernetzung/ts-ics) -- [Sabre/dav Documentation](https://sabre.io/dav/) diff --git a/RECURRENCE_SUMMARY.md b/RECURRENCE_SUMMARY.md deleted file mode 100644 index db634b1..0000000 --- a/RECURRENCE_SUMMARY.md +++ /dev/null @@ -1,369 +0,0 @@ -# Recurring Events Implementation - Summary - -## ๐ŸŽฏ What Was Implemented - -A complete recurring events system following the iCalendar RFC 5545 standard (RRULE). - -### โœ… Features Completed - -1. **RecurrenceEditor Component** (`RecurrenceEditor.tsx`) - - โœ… DAILY recurrence with interval support - - โœ… WEEKLY recurrence with day selection (Mon-Sun) - - โœ… MONTHLY recurrence with day of month (1-31) - - โœ… YEARLY recurrence with month + day selection - - โœ… End conditions: Never / Until date / After N occurrences - - โœ… Smart date validation (Feb 29th, month lengths) - - โœ… Visual warnings for invalid dates - - โœ… Simple and Custom modes - -2. **Styles** (`RecurrenceEditor.scss`) - - โœ… BEM methodology - - โœ… Responsive layout - - โœ… Weekday button selection - - โœ… Warning messages styling - - โœ… Integrated with existing design system - -3. **Translations** (`translations.json`) - - โœ… English (en) - - โœ… French (fr) - - โœ… Dutch (nl) - - โœ… All UI strings - - โœ… Month names - - โœ… Validation warnings - -4. **Tests** (`RecurrenceEditor.test.tsx`) - - โœ… 15+ test cases - - โœ… All recurrence types - - โœ… Date validation - - โœ… End conditions - - โœ… User interactions - -5. **Documentation** - - โœ… Complete implementation guide - - โœ… Scheduler integration guide - - โœ… RRULE examples - - โœ… Testing checklist - - โœ… Troubleshooting guide - -## ๐Ÿ“ Files Created/Modified - -### New Files -``` -src/frontend/apps/calendars/src/features/calendar/components/ -โ”œโ”€โ”€ RecurrenceEditor.tsx โœ… Complete component -โ”œโ”€โ”€ RecurrenceEditor.scss โœ… Styles -โ””โ”€โ”€ __tests__/ - โ””โ”€โ”€ RecurrenceEditor.test.tsx โœ… Test suite - -Documentation: -โ”œโ”€โ”€ RECURRENCE_IMPLEMENTATION.md โœ… Full implementation guide -โ”œโ”€โ”€ SCHEDULER_RECURRENCE_INTEGRATION.md โœ… Integration guide -โ””โ”€โ”€ RECURRENCE_SUMMARY.md โœ… This file -``` - -### Modified Files -``` -src/frontend/apps/calendars/src/features/i18n/ -โ””โ”€โ”€ translations.json โœ… Added recurrence translations (EN/FR/NL) - -src/frontend/apps/calendars/src/styles/ -โ””โ”€โ”€ globals.scss โœ… RecurrenceEditor.scss already imported -``` - -## ๐Ÿš€ Quick Start - -### 1. Use RecurrenceEditor in a Form - -```tsx -import { RecurrenceEditor } from '@/features/calendar/components/RecurrenceEditor'; -import { useState } from 'react'; -import type { IcsRecurrenceRule } from 'ts-ics'; - -function MyEventForm() { - const [recurrence, setRecurrence] = useState(); - - return ( -
- - - - - ); -} -``` - -### 2. Include in IcsEvent - -```typescript -const event: IcsEvent = { - uid: crypto.randomUUID(), - summary: "Team Meeting", - start: { date: new Date() }, - end: { date: new Date() }, - recurrenceRule: recurrence, // From RecurrenceEditor -}; -``` - -### 3. CalDAV Automatically Handles It - -No backend changes needed! The RRULE is stored in the .ics file: - -```ics -BEGIN:VEVENT -UID:abc-123 -SUMMARY:Team Meeting -DTSTART:20260125T140000Z -RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=20 -END:VEVENT -``` - -## ๐Ÿ“Š Supported Patterns - -| Pattern | Example | RRULE | -|---------|---------|-------| -| **Daily** | Every day | `FREQ=DAILY` | -| | Every 3 days | `FREQ=DAILY;INTERVAL=3` | -| **Weekly** | Every Monday | `FREQ=WEEKLY;BYDAY=MO` | -| | Mon, Wed, Fri | `FREQ=WEEKLY;BYDAY=MO,WE,FR` | -| | Every 2 weeks on Thu | `FREQ=WEEKLY;INTERVAL=2;BYDAY=TH` | -| **Monthly** | 15th of each month | `FREQ=MONTHLY;BYMONTHDAY=15` | -| | Last day (31st) | `FREQ=MONTHLY;BYMONTHDAY=31` | -| **Yearly** | March 15th | `FREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15` | -| | Feb 29 (leap years) | `FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=29` | -| **End** | Never | (no UNTIL or COUNT) | -| | Until Dec 31, 2025 | `UNTIL=20251231T235959Z` | -| | 10 times | `COUNT=10` | - -## ๐Ÿ”ง Integration with Scheduler - -To integrate into your EventModal in Scheduler.tsx, follow these 5 steps: - -1. **Import:** `import { RecurrenceEditor } from '../RecurrenceEditor';` -2. **State:** `const [recurrence, setRecurrence] = useState();` -3. **UI:** Add button + `` -4. **Save:** Include `recurrenceRule: recurrence` in IcsEvent -5. **Reset:** Add recurrence reset in useEffect - -See `SCHEDULER_RECURRENCE_INTEGRATION.md` for complete code. - -## ๐ŸŽจ UI Features - -### Simple Mode -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Repeat: [Dropdown: Daily โ–ผ] โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -Dropdown options: -- No -- Daily -- Weekly -- Monthly -- Yearly -- Custom... - -### Custom Mode - Weekly Example -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Repeat every [2] [weeks โ–ผ] โ”‚ -โ”‚ โ”‚ -โ”‚ Repeat on: โ”‚ -โ”‚ [M] [T] [W] [T] [F] [S] [S] โ† Toggle buttons โ”‚ -โ”‚ โœ“ โœ“ โ† Selected โ”‚ -โ”‚ โ”‚ -โ”‚ Ends: โ”‚ -โ”‚ โ—‹ Never โ”‚ -โ”‚ โ—‹ On [2025-12-31] โ”‚ -โ”‚ โฆฟ After [10] occurrences โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Validation Warnings -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Repeat every [1] [years โ–ผ] โ”‚ -โ”‚ โ”‚ -โ”‚ Repeat on date: โ”‚ -โ”‚ [February โ–ผ] [30] โ”‚ -โ”‚ โ”‚ -โ”‚ โš ๏ธ February has at most 29 days โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -## ๐Ÿงช Testing - -Run tests: -```bash -npm test RecurrenceEditor -``` - -Manual testing checklist: -- [ ] Daily with intervals 1, 3, 7 -- [ ] Weekly single day (Monday) -- [ ] Weekly multiple days (Mon, Wed, Fri) -- [ ] Weekly with interval 2 -- [ ] Monthly on 1st, 15th, 31st -- [ ] Monthly February validation -- [ ] Yearly Jan 1, Dec 25 -- [ ] Yearly Feb 29 leap year warning -- [ ] Never-ending -- [ ] Until date -- [ ] Count-based (10 occurrences) - -## ๐Ÿ“š Documentation Files - -1. **RECURRENCE_IMPLEMENTATION.md** - - Complete technical documentation - - Architecture overview - - Component structure - - RRULE examples - - Backend considerations - - Testing guide - -2. **SCHEDULER_RECURRENCE_INTEGRATION.md** - - Step-by-step integration guide - - Code snippets for each step - - Complete example - - Troubleshooting - -3. **RECURRENCE_SUMMARY.md** (this file) - - Quick reference - - Files overview - - Quick start guide - -## ๐Ÿ”ฎ Future Enhancements - -### Not Yet Implemented (Optional) - -1. **Advanced Patterns** - - BYSETPOS (e.g., "2nd Tuesday of month") - - Position-based recurrence ("Last Friday") - -2. **UI Enhancements** - - Visual calendar preview - - Natural language summary ("Every 2 weeks on Monday") - - Recurrence icon in calendar - -3. **Editing Features** - - Edit single instance vs series - - Delete this/future/all options - - Exception handling UI - -4. **Time Zone** - - Better time zone handling for UNTIL - - Time zone selector for events - -## โœ… What Works Now - -- โœ… Create recurring events -- โœ… Edit recurring events (entire series) -- โœ… Delete recurring events -- โœ… View recurring event instances in calendar -- โœ… CalDAV sync with other clients (Outlook, Apple Calendar, etc.) -- โœ… Email invitations for recurring events -- โœ… Attendees on recurring events -- โœ… All recurrence patterns (DAILY/WEEKLY/MONTHLY/YEARLY) -- โœ… All end conditions (never/until/count) -- โœ… Date validation - -## ๐Ÿ› Known Limitations - -1. **Single Instance Editing** - - Editing modifies entire series - - No UI for "Edit this occurrence only" - - (CalDAV supports via RECURRENCE-ID, but UI not implemented) - -2. **Advanced Patterns** - - No "nth occurrence" (e.g., "2nd Tuesday") - - No "last occurrence" (e.g., "last Friday") - -3. **Visual Feedback** - - No recurring event icon in calendar view - - No summary text showing recurrence pattern - -## ๐Ÿ’ก Usage Tips - -### Leap Year Events (Feb 29) - -When creating yearly event on Feb 29: -```typescript -{ - frequency: 'YEARLY', - byMonth: [2], - byMonthDay: [29] -} -``` - -โš ๏ธ UI shows: "This date (Feb 29) only exists in leap years" - -Event will only occur in: -- 2024 โœ… -- 2025 โŒ -- 2026 โŒ -- 2027 โŒ -- 2028 โœ… - -### Month-End Events (31st) - -When creating monthly event on 31st: -```typescript -{ - frequency: 'MONTHLY', - byMonthDay: [31] -} -``` - -Event occurs on: -- January 31 โœ… -- February 31 โŒ (skipped) -- March 31 โœ… -- April 31 โŒ (skipped, only 30 days) -- May 31 โœ… - -### Weekday Selection - -For "every weekday" (Mon-Fri): -```typescript -{ - frequency: 'WEEKLY', - byDay: [ - { day: 'MO' }, - { day: 'TU' }, - { day: 'WE' }, - { day: 'TH' }, - { day: 'FR' } - ] -} -``` - -## ๐ŸŽ“ Learning Resources - -- [RFC 5545 - iCalendar Specification](https://datatracker.ietf.org/doc/html/rfc5545) -- [RRULE Documentation](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) -- [ts-ics Library](https://github.com/Neuvernetzung/ts-ics) -- [Sabre/dav](https://sabre.io/dav/) - -## ๐Ÿ“ž Support - -If you encounter issues: - -1. Check `RECURRENCE_IMPLEMENTATION.md` for detailed docs -2. Check `SCHEDULER_RECURRENCE_INTEGRATION.md` for integration help -3. Run tests: `npm test RecurrenceEditor` -4. Check browser console for errors -5. Inspect network tab for CalDAV requests - -## ๐ŸŽ‰ Summary - -You now have a **complete, production-ready** recurring events system that: - -- โœ… Supports all common recurrence patterns -- โœ… Validates user input with helpful warnings -- โœ… Integrates seamlessly with CalDAV -- โœ… Works with ts-ics and @event-calendar -- โœ… Is fully translated (EN/FR/NL) -- โœ… Is well-tested and documented -- โœ… Follows RFC 5545 standard - -**Next step:** Integrate into Scheduler using `SCHEDULER_RECURRENCE_INTEGRATION.md`! ๐Ÿš€ diff --git a/SCHEDULER_RECURRENCE_INTEGRATION.md b/SCHEDULER_RECURRENCE_INTEGRATION.md deleted file mode 100644 index 809bd16..0000000 --- a/SCHEDULER_RECURRENCE_INTEGRATION.md +++ /dev/null @@ -1,368 +0,0 @@ -# Scheduler Recurrence Integration Guide - -## How to Add Recurrence Support to EventModal in Scheduler.tsx - -This guide shows exactly how to integrate the RecurrenceEditor component into the existing Scheduler event modal. - -## Step 1: Import RecurrenceEditor - -Add to imports at the top of `Scheduler.tsx`: - -```typescript -import { RecurrenceEditor } from "../RecurrenceEditor"; -import type { IcsRecurrenceRule } from "ts-ics"; -``` - -## Step 2: Add Recurrence State - -In the `EventModal` component, add recurrence state after the existing useState declarations: - -```typescript -// Around line 110, after: -const [attendees, setAttendees] = useState([]); -const [showAttendees, setShowAttendees] = useState(false); - -// Add: -const [recurrence, setRecurrence] = useState( - event?.recurrenceRule -); -const [showRecurrence, setShowRecurrence] = useState(() => { - return !!event?.recurrenceRule; -}); -``` - -## Step 3: Reset Recurrence When Event Changes - -In the `useEffect` that resets form state, add recurrence reset: - -```typescript -// Around line 121-161, in the useEffect(() => { ... }, [event, calendarUrl]) -useEffect(() => { - setTitle(event?.summary || ""); - setDescription(event?.description || ""); - setLocation(event?.location || ""); - setSelectedCalendarUrl(calendarUrl); - - // Initialize attendees from event - if (event?.attendees && event.attendees.length > 0) { - setAttendees(event.attendees); - setShowAttendees(true); - } else { - setAttendees([]); - setShowAttendees(false); - } - - // ADD THIS: Initialize recurrence from event - if (event?.recurrenceRule) { - setRecurrence(event.recurrenceRule); - setShowRecurrence(true); - } else { - setRecurrence(undefined); - setShowRecurrence(false); - } - - // ... rest of the useEffect -}, [event, calendarUrl]); -``` - -## Step 4: Include Recurrence in Save - -In the `handleSave` function, add recurrence to the IcsEvent object: - -```typescript -// Around line 200-227, when creating the icsEvent -const icsEvent: IcsEvent = { - ...eventWithoutDuration, - uid: event?.uid || crypto.randomUUID(), - summary: title, - description: description || undefined, - location: location || undefined, - start: { - date: fakeUtcStart, - local: { - timezone: BROWSER_TIMEZONE, - tzoffset: adapter.getTimezoneOffset(startDate, BROWSER_TIMEZONE), - }, - }, - end: { - date: fakeUtcEnd, - local: { - timezone: BROWSER_TIMEZONE, - tzoffset: adapter.getTimezoneOffset(endDate, BROWSER_TIMEZONE), - }, - }, - organizer: organizer, - attendees: attendees.length > 0 ? attendees : undefined, - recurrenceRule: recurrence, // ADD THIS LINE -}; -``` - -## Step 5: Add RecurrenceEditor to UI - -In the modal JSX, add a button to show/hide recurrence and the RecurrenceEditor component. - -### Add Feature Button (like the attendees button) - -Around line 350-360, after the attendees button: - -```tsx -{/* Existing code */} - - -{/* ADD THIS: Recurrence button */} - -``` - -### Add RecurrenceEditor Component - -Around line 370, after the AttendeesInput: - -```tsx -{/* Existing attendees input */} -{showAttendees && ( -
- -
-)} - -{/* ADD THIS: Recurrence editor */} -{showRecurrence && ( -
- -
-)} -``` - -## Step 6: Add CSS for Recurrence Section - -In `Scheduler.scss`, add styling for the recurrence section: - -```scss -.event-modal { - // ... existing styles - - &__recurrence-editor { - padding: 1rem; - background-color: #f8f9fa; - border-radius: 4px; - margin-top: 1rem; - } - - // Ensure feature tags wrap properly - &__features { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; // Add this if not present - } -} -``` - -## Complete EventModal Component Structure - -Here's the complete structure with recurrence integrated: - -```typescript -const EventModal = ({ - isOpen, - mode, - event, - calendarUrl, - calendars, - adapter, - onSave, - onDelete, - onClose, -}: EventModalProps) => { - const { t } = useTranslation(); - const { user } = useAuth(); - - // Form state - const [title, setTitle] = useState(event?.summary || ""); - const [description, setDescription] = useState(event?.description || ""); - const [location, setLocation] = useState(event?.location || ""); - const [startDateTime, setStartDateTime] = useState(""); - const [endDateTime, setEndDateTime] = useState(""); - const [selectedCalendarUrl, setSelectedCalendarUrl] = useState(calendarUrl); - const [isLoading, setIsLoading] = useState(false); - - // Features state - const [attendees, setAttendees] = useState([]); - const [showAttendees, setShowAttendees] = useState(false); - const [recurrence, setRecurrence] = useState(); - const [showRecurrence, setShowRecurrence] = useState(false); - - // Calculate organizer - const organizer: IcsOrganizer | undefined = event?.organizer || ...; - - // Reset form when event changes - useEffect(() => { - // Reset basic fields - setTitle(event?.summary || ""); - setDescription(event?.description || ""); - setLocation(event?.location || ""); - setSelectedCalendarUrl(calendarUrl); - - // Reset attendees - if (event?.attendees && event.attendees.length > 0) { - setAttendees(event.attendees); - setShowAttendees(true); - } else { - setAttendees([]); - setShowAttendees(false); - } - - // Reset recurrence - if (event?.recurrenceRule) { - setRecurrence(event.recurrenceRule); - setShowRecurrence(true); - } else { - setRecurrence(undefined); - setShowRecurrence(false); - } - - // Reset dates - // ... existing date reset logic - }, [event, calendarUrl]); - - const handleSave = async () => { - // ... create icsEvent with recurrence - const icsEvent: IcsEvent = { - // ... all fields - recurrenceRule: recurrence, - }; - - await onSave(icsEvent, selectedCalendarUrl); - }; - - return ( - - {/* Title, Calendar selector, Dates */} - - {/* Feature tags */} -
- - - -
- - {/* Location, Description */} - - {/* Attendees section */} - {showAttendees && ( - - )} - - {/* Recurrence section */} - {showRecurrence && ( - - )} - - {/* Save/Cancel buttons */} -
- ); -}; -``` - -## Material Icons - -The recurrence button uses the `repeat` Material icon. Make sure Material Icons are loaded: - -```html - - -``` - -## Testing the Integration - -1. **Create new recurring event:** - - Click "Create" in calendar - - Click "Repeat" button (๐Ÿ” icon) - - Select "Weekly", check "Monday" and "Wednesday" - - Save - -2. **Edit recurring event:** - - Click on a recurring event - - Modal should show recurrence with "Repeat" button active - - Modify recurrence pattern - - Save - -3. **Remove recurrence:** - - Open recurring event - - Click "Repeat" button to expand - - Select "None" from dropdown - - Save - -## Expected Behavior - -- โœ… Recurrence button toggles RecurrenceEditor visibility -- โœ… Active button shows blue background (like attendees) -- โœ… RecurrenceEditor state persists when toggling visibility -- โœ… Saving event includes recurrence in IcsEvent -- โœ… Opening existing recurring event loads recurrence correctly -- โœ… Calendar displays recurring event instances - -## Troubleshooting - -### Recurrence not saving -- Check that `recurrenceRule: recurrence` is in the icsEvent object -- Verify ts-ics is correctly serializing the RRULE - -### Recurrence not loading when editing -- Check the useEffect includes recurrence reset -- Verify event?.recurrenceRule is being passed from EventCalendarAdapter - -### UI not showing properly -- Ensure RecurrenceEditor.scss is imported in globals.scss -- Check that Material Icons font is loaded - -### Events not appearing as recurring -- Verify CalDAV server supports RRULE -- Check browser console for errors -- Inspect .ics file content in network tab - -## Next Steps - -After integration, consider: -1. Adding recurrence summary text (e.g., "Repeats weekly on Monday") -2. Handle editing single instance vs series -3. Add "Delete series" vs "Delete this occurrence" options -4. Show recurrence icon in calendar event display diff --git a/docs/PR_SPLIT_PLAN.md b/docs/PR_SPLIT_PLAN.md deleted file mode 100644 index 10f4395..0000000 --- a/docs/PR_SPLIT_PLAN.md +++ /dev/null @@ -1,196 +0,0 @@ -# Plan : Dรฉcoupage en plusieurs PRs - -## Contexte - -- **24 commits** depuis `main` -- **158 fichiers** modifiรฉs (+16,943 / -10,848 lignes) -- Travail accumulรฉ sans dรฉcoupage en PRs - ---- - -## Stratรฉgie - -Crรฉer **5-6 branches** depuis `main`, chacune avec des commits logiques, -puis crรฉer une PR pour chaque branche. - -**Approche technique :** -1. Rester sur `poc/event-calendar` (branche actuelle de travail) -2. Pour chaque PR : crรฉer une nouvelle branche depuis `main`, copier les - fichiers pertinents depuis `poc/event-calendar`, commiter - ---- - -## Dรฉcoupage proposรฉ (6 PRs) - -### PR 1 : Backend - Invitations CalDAV avec emails -**Branche** : `feat/caldav-invitations` - -**Fichiers :** -- `docker/sabredav/src/AttendeeNormalizerPlugin.php` -- `docker/sabredav/src/HttpCallbackIMipPlugin.php` -- `docker/sabredav/server.php` -- `docker/sabredav/sql/pgsql.calendars.sql` -- `src/backend/core/services/calendar_invitation_service.py` -- `src/backend/core/api/viewsets_caldav.py` -- `src/backend/core/templates/emails/calendar_invitation*.html/txt` -- `src/backend/calendars/settings.py` -- `env.d/development/backend.defaults` -- `env.d/development/caldav.defaults` -- `compose.yaml` - -**Description PR** : Ajout du scheduling CalDAV (iTIP) avec envoi d'emails -pour les invitations, mises ร  jour et annulations. - ---- - -### PR 2 : Frontend - Refactoring CalDavService et helpers -**Branche** : `refactor/caldav-service` - -**Fichiers :** -- `features/calendar/services/dav/CalDavService.ts` -- `features/calendar/services/dav/EventCalendarAdapter.ts` -- `features/calendar/services/dav/caldav-helpers.ts` -- `features/calendar/services/dav/helpers/*.ts` -- `features/calendar/services/dav/types/*.ts` -- `features/calendar/services/dav/constants.ts` -- `features/calendar/services/dav/__tests__/*.ts` - -**Description PR** : Refactoring du service CalDAV avec extraction des -helpers, meilleure gestion des types et ajout de tests. - ---- - -### PR 3 : Frontend - Composant Scheduler (EventModal, handlers) -**Branche** : `feat/scheduler-component` - -**Dรฉpend de** : PR 2 - -**Fichiers :** -- `features/calendar/components/scheduler/*` -- `features/calendar/components/RecurrenceEditor.tsx` -- `features/calendar/components/RecurrenceEditor.scss` -- `features/calendar/components/AttendeesInput.tsx` -- `features/calendar/components/AttendeesInput.scss` -- `features/calendar/contexts/CalendarContext.tsx` -- `pages/calendar.tsx` -- `pages/calendar.scss` - -**Description PR** : Nouveau composant Scheduler avec EventModal pour -la crรฉation/รฉdition d'รฉvรฉnements, gestion des rรฉcurrences et des invitรฉs. - ---- - -### PR 4 : Frontend - Refactoring CalendarList modulaire -**Branche** : `refactor/calendar-list` - -**Fichiers :** -- `features/calendar/components/calendar-list/*` -- `features/calendar/components/LeftPanel.tsx` -- `features/calendar/components/MiniCalendar.tsx` -- `features/calendar/components/MiniCalendar.scss` -- `features/calendar/components/CreateCalendarModal.tsx` -- `features/calendar/components/CalendarList.scss` -- `features/calendar/components/index.ts` - -**Description PR** : Refactoring de CalendarList en composants modulaires -(CalendarItemMenu, CalendarListItem, CalendarModal, DeleteConfirmModal). - ---- - -### PR 5 : Frontend - Support i18n et locales -**Branche** : `feat/calendar-i18n` - -**Fichiers :** -- `features/calendar/hooks/useCalendarLocale.ts` -- `features/i18n/*` (si modifiรฉ) -- `src/frontend/apps/e2e/__tests__/calendar-locale.test.ts` - -**Description PR** : Ajout du support des locales pour le calendrier -avec tests e2e. - ---- - -### PR 6 : Frontend - Nettoyage code mort -**Branche** : `chore/remove-dead-code` - -**Fichiers supprimรฉs :** -- `features/ui/components/breadcrumbs/` -- `features/ui/components/circular-progress/` -- `features/ui/components/infinite-scroll/` -- `features/ui/components/info/` -- `features/ui/components/responsive/` -- `features/forms/components/RhfInput.tsx` -- `hooks/useCopyToClipboard.tsx` -- `utils/useLayout.tsx` -- `features/calendar/components/EventModalDeprecated.tsx` -- `features/calendar/components/EventModalAdapter.tsx` -- `features/calendar/hooks/useEventModal.tsx` -- `features/calendar/hooks/useCreateEventModal.tsx` -- `src/frontend/packages/open-calendar/` (package entier) - -**Description PR** : Suppression du code mort et des composants inutilisรฉs. - ---- - -## Ordre de merge recommandรฉ - -``` -1. PR 1 (Backend invitations) - indรฉpendante -2. PR 2 (CalDavService) - indรฉpendante -3. PR 6 (Dead code) - indรฉpendante -4. PR 5 (i18n) - indรฉpendante -5. PR 4 (CalendarList) - aprรจs PR 6 -6. PR 3 (Scheduler) - aprรจs PR 2, PR 4 -``` - ---- - -## ร‰tapes d'exรฉcution - -Pour chaque PR : - -```bash -# 1. Crรฉer la branche depuis main -git checkout main -git pull origin main -git checkout -b - -# 2. Copier les fichiers depuis poc/event-calendar -git checkout poc/event-calendar -- - -# 3. Vรฉrifier et commiter -git add . -git commit -m "..." - -# 4. Pousser et crรฉer la PR -git push -u origin -gh pr create --title "..." --body "..." -``` - ---- - -## Fichiers ร  exclure des PRs - -- `CLAUDE.md` (fichier local) -- `IMPLEMENTATION_CHECKLIST.md`, `README_RECURRENCE.md`, etc. - (documentation temporaire ร  supprimer ou consolider) - ---- - -## Vรฉrification - -Avant chaque PR : -```bash -cd src/frontend/apps/calendars -yarn tsc --noEmit # Types OK -yarn lint # Lint OK -yarn test # Tests OK -``` - ---- - -## Faisabilitรฉ - -**Oui, c'est tout ร  fait possible.** La stratรฉgie `git checkout -- ` -permet de rรฉcupรฉrer des fichiers spรฉcifiques d'une branche sans perdre -l'historique de travail. Chaque PR sera autonome et reviewable indรฉpendamment. diff --git a/docs/invitations.md b/docs/invitations.md new file mode 100644 index 0000000..d2a3d67 --- /dev/null +++ b/docs/invitations.md @@ -0,0 +1,195 @@ +# Invitations + +How event invitations work end-to-end: creating, sending, responding, +updating, and cancelling. + +## Architecture + +``` +Frontend (EventModal) + โ†’ CalDAV proxy (Django) + โ†’ SabreDAV (stores event, detects attendees) + โ†’ HttpCallbackIMipPlugin (HTTP POST to Django) + โ†’ CalendarInvitationService (sends email) + โ†’ Attendee receives email + โ†’ RSVP link or iTIP client response + โ†’ RSVPView (Django) or CalDAV REPLY + โ†’ PARTSTAT updated in event + โ†’ Organizer notified +``` + +## Creating an event with attendees + +1. User adds attendees via `AttendeesSection` in EventModal +2. `useEventForm.toIcsEvent()` serializes the event with `ATTENDEE` + and `ORGANIZER` properties +3. `CalDavService.createEvent()` sends a PUT to CalDAV through the + Django proxy +4. The proxy (`CalDAVProxyView`) injects an + `X-CalDAV-Callback-URL` header pointing back to Django + +The resulting `.ics` contains: + +```ics +BEGIN:VEVENT +UID:abc-123 +SUMMARY:Team Meeting +DTSTART:20260301T140000Z +DTEND:20260301T150000Z +ORGANIZER;CN=Alice:mailto:alice@example.com +ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:bob@example.com +SEQUENCE:0 +END:VEVENT +``` + +## SabreDAV processing + +When SabreDAV receives the event, three plugins run in order: + +1. **CalendarSanitizerPlugin** (priority 85) โ€” strips inline binary + attachments (Outlook signatures), truncates oversized fields, + enforces max resource size (1 MB default) +2. **AttendeeNormalizerPlugin** (priority 90) โ€” lowercases emails, + deduplicates attendees keeping the highest-priority PARTSTAT + (ACCEPTED > TENTATIVE > DECLINED > NEEDS-ACTION) +3. **iMip scheduling** โ€” detects attendees and creates a REQUEST + message for each one + +The scheduling message is routed by **HttpCallbackIMipPlugin**, which +POSTs to Django: + +``` +POST /api/v1.0/caldav-scheduling-callback/ +X-Api-Key: +X-CalDAV-Sender: alice@example.com +X-CalDAV-Recipient: bob@example.com +X-CalDAV-Method: REQUEST +Content-Type: text/calendar + + +``` + +## Sending invitation emails + +`CalDAVSchedulingCallbackView` receives the callback and delegates to +`CalendarInvitationService.send_invitation()`. + +Steps: + +1. **Parse** โ€” `ICalendarParser.parse()` extracts UID, summary, + dates, organizer, attendee, location, description, sequence number +2. **Template selection** based on method and sequence: + | Method | Sequence | Template | + |--------|----------|----------| + | REQUEST | 0 | `calendar_invitation.html` | + | REQUEST | >0 | `calendar_invitation_update.html` | + | CANCEL | any | `calendar_invitation_cancel.html` | + | REPLY | any | `calendar_invitation_reply.html` | +3. **RSVP tokens** โ€” for REQUEST emails, generates signed URLs: + ``` + /rsvp/?token=&action=accepted + /rsvp/?token=&action=tentative + /rsvp/?token=&action=declined + ``` + Tokens are signed with `django.core.signing.Signer(salt="rsvp")` + and contain `{uid, email, organizer}`. +4. **ICS attachment** โ€” if `CALENDAR_ITIP_ENABLED=True`, the + attachment includes `METHOD:REQUEST` for iTIP-aware clients + (Outlook, Apple Mail). If False (default), the METHOD is stripped + and web RSVP links are used instead. +5. **Send** โ€” multipart email with HTML + plain text + ICS attachment. + Reply-To is set to the organizer's email. + +## Responding to invitations + +Two paths: + +### Web RSVP (default) + +Attendee clicks Accept / Maybe / Decline link in the email. + +`RSVPView` handles `GET /rsvp/?token=...&action=accepted`: + +1. Unsigns the token (salt="rsvp") +2. Finds the event in the organizer's CalDAV calendar by UID +3. Checks the event is not in the past (recurring events are never + considered past) +4. Updates the attendee's `PARTSTAT` to ACCEPTED / TENTATIVE / DECLINED +5. PUTs the updated event back to CalDAV +6. Renders a confirmation page + +The PUT triggers SabreDAV to generate a REPLY message, which flows +back through HttpCallbackIMipPlugin โ†’ Django โ†’ organizer email. + +### iTIP client response + +When `CALENDAR_ITIP_ENABLED=True`, email clients like Outlook or +Apple Calendar show native Accept/Decline buttons. The client sends +an iTIP REPLY directly to the CalDAV server, which triggers the same +callback flow. + +## Updating an event + +When an event with attendees is modified: + +1. `CalDavService.updateEvent()` increments the `SEQUENCE` number +2. SabreDAV detects the change and creates REQUEST messages with the + updated sequence +3. Attendees receive an update email + (`calendar_invitation_update.html`) + +## Cancelling an event + +When an event with attendees is deleted: + +1. SabreDAV creates CANCEL messages for each attendee +2. Attendees receive a cancellation email + (`calendar_invitation_cancel.html`) + +## Configuration + +| Setting | Default | Description | +|---------|---------|-------------| +| `CALDAV_URL` | `http://caldav:80` | Internal CalDAV server URL | +| `CALDAV_INBOUND_API_KEY` | None | API key for callbacks from CalDAV | +| `CALDAV_OUTBOUND_API_KEY` | None | API key for requests to CalDAV | +| `CALDAV_CALLBACK_BASE_URL` | None | Internal URL for CalDAVโ†’Django (Docker: `http://backend:8000`) | +| `CALENDAR_ITIP_ENABLED` | False | Use iTIP METHOD headers in ICS attachments | +| `CALENDAR_INVITATION_FROM_EMAIL` | `DEFAULT_FROM_EMAIL` | Sender address for invitation emails | +| `APP_URL` | `""` | Base URL for RSVP links in emails | + +## Key files + +| Area | Path | +|------|------| +| Attendee UI | `src/frontend/.../event-modal-sections/AttendeesSection.tsx` | +| Event form | `src/frontend/.../scheduler/hooks/useEventForm.ts` | +| CalDAV client | `src/frontend/.../services/dav/CalDavService.ts` | +| CalDAV proxy | `src/backend/core/api/viewsets_caldav.py` | +| Scheduling callback | `src/backend/core/api/viewsets_caldav.py` (`CalDAVSchedulingCallbackView`) | +| RSVP handler | `src/backend/core/api/viewsets_rsvp.py` | +| Email service | `src/backend/core/services/calendar_invitation_service.py` | +| ICS parser | `src/backend/core/services/calendar_invitation_service.py` (`ICalendarParser`) | +| Email templates | `src/backend/core/templates/emails/calendar_invitation*.html` | +| SabreDAV sanitizer | `docker/sabredav/src/CalendarSanitizerPlugin.php` | +| SabreDAV attendee dedup | `docker/sabredav/src/AttendeeNormalizerPlugin.php` | +| SabreDAV callback plugin | `docker/sabredav/src/HttpCallbackIMipPlugin.php` | + +## Future: Messages mail client integration + +La Suite includes a Messages mail client (based on an open-source +webmail). Future integration would allow: + +- **Inline RSVP** โ€” render Accept/Decline buttons directly in the + Messages UI when an email contains a `text/calendar` attachment with + `METHOD:REQUEST` +- **Calendar preview** โ€” show event details (date, time, location) + extracted from the ICS attachment without opening the full calendar +- **Auto-add to calendar** โ€” accepted events automatically appear in + the user's Calendars calendar via a shared CalDAV backend +- **Status sync** โ€” PARTSTAT changes in Messages propagate to + Calendars and vice versa + +This requires Messages to support iTIP processing +(`CALENDAR_ITIP_ENABLED=True`) and share the same CalDAV/auth +infrastructure. diff --git a/docs/recurrence.md b/docs/recurrence.md new file mode 100644 index 0000000..2faaecf --- /dev/null +++ b/docs/recurrence.md @@ -0,0 +1,107 @@ +# Recurring Events + +Recurring events follow the iCalendar RFC 5545 RRULE standard. No backend +changes are needed โ€” CalDAV (SabreDAV) handles recurrence natively. + +## Architecture + +``` +RecurrenceEditor (React) + -> IcsRecurrenceRule (ts-ics) + -> RRULE string (RFC 5545) + -> .ics file (CalDAV) + -> SabreDAV server +``` + +## RecurrenceEditor Component + +Located at +`src/frontend/apps/calendars/src/features/calendar/components/RecurrenceEditor.tsx` + +```tsx +import { RecurrenceEditor } from '@/features/calendar/components/RecurrenceEditor'; + +const [recurrence, setRecurrence] = useState(); + + +``` + +Include in the event object: + +```typescript +const event: IcsEvent = { + // ...other fields + recurrenceRule: recurrence, +}; +``` + +### Supported patterns + +| Pattern | RRULE | +|---------|-------| +| Every day | `FREQ=DAILY` | +| Every 3 days | `FREQ=DAILY;INTERVAL=3` | +| Every Monday | `FREQ=WEEKLY;BYDAY=MO` | +| Mon/Wed/Fri | `FREQ=WEEKLY;BYDAY=MO,WE,FR` | +| Every 2 weeks on Thu | `FREQ=WEEKLY;INTERVAL=2;BYDAY=TH` | +| 15th of each month | `FREQ=MONTHLY;BYMONTHDAY=15` | +| March 15 yearly | `FREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15` | +| 10 occurrences | append `;COUNT=10` | +| Until a date | append `;UNTIL=20251231T235959Z` | + +### Not yet supported + +- `BYSETPOS` (e.g. "1st Monday of month", "last Friday") +- Edit single instance vs series (needs RECURRENCE-ID UI) +- Visual preview of recurrence pattern + +### Date validation + +The component warns about edge cases: +- Feb 30/31 โ€” "February has at most 29 days" +- Feb 29 โ€” "Only exists in leap years" +- Day 31 on 30-day months โ€” shown as warning + +### Translations + +Supported: English, French, Dutch. Keys are in +`src/frontend/apps/calendars/src/features/i18n/translations.json` +under `calendar.recurrence.*`. + +## IcsRecurrenceRule interface (ts-ics) + +```typescript +interface IcsRecurrenceRule { + frequency: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY'; + interval?: number; + count?: number; + until?: IcsDateObject; + byDay?: { day: 'MO'|'TU'|'WE'|'TH'|'FR'|'SA'|'SU' }[]; + byMonthDay?: number[]; + byMonth?: number[]; +} +``` + +## How CalDAV handles it + +1. RRULE is stored as a property in the VEVENT inside the `.ics` file +2. SabreDAV expands recurring instances when clients query date ranges +3. Individual instance modifications use RECURRENCE-ID (not yet in UI) + +Example `.ics`: + +```ics +BEGIN:VEVENT +UID:abc-123 +SUMMARY:Weekly Team Meeting +DTSTART:20260125T140000Z +RRULE:FREQ=WEEKLY;BYDAY=MO;UNTIL=20261231T235959Z +END:VEVENT +``` + +## References + +- [RFC 5545 โ€” iCalendar](https://datatracker.ietf.org/doc/html/rfc5545) +- [RRULE spec](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) +- [ts-ics](https://github.com/Neuvernetzung/ts-ics) +- [SabreDAV](https://sabre.io/dav/)