- Add new context menu callback for singleton events to avoid series pipeline
- Implement complete RRULE construction with INTERVAL, COUNT, and UNTIL parameters
- Update frontend service methods to pass recurrence parameters correctly
- Add missing recurrence fields to backend UpdateEventRequest model
- Fix parameter ordering in frontend method calls
- Ensure singleton→series conversion uses single event pipeline initially
This resolves issues where converting singleton events to recurring series
would not respect recurrence interval (every N days), count (N occurrences),
or until date parameters.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix double timezone conversion in drag-and-drop that caused 4-hour time shifts
- Frontend now sends local times instead of UTC to backend for proper conversion
- Add missing timezone parameter to update_series method to fix recurring event updates
- Update both event modal and drag-and-drop paths to include timezone information
- Maintain RFC 5545 compliance with proper timezone conversion in backend
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add hidden print-preview-copy div at app level for clean print isolation
- Use @media print rules to show only print copy (960x720) in landscape
- Auto-sync print copy with preview content on modal render via use_effect
- Copy CSS variables and data attributes for proper hour filtering
- Set explicit dimensions matching print-preview-paper content area
- Force landscape orientation with @page rule for better calendar printing
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enable clicking external calendar color icons to open color picker dropdown
- Implement backend API integration for updating external calendar colors
- Add conditional hover effects to prevent interference with color picker
- Use extremely high z-index (999999) to ensure dropdown appears above all elements
- Match existing CalDAV calendar color picker behavior and styling
- Support real-time color updates with immediate visual feedback
- Maintain color consistency across sidebar and calendar events
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace separate Create Calendar and External Calendar modals with unified tabbed interface
- Redesign modal styling with less rounded corners and cleaner appearance
- Significantly increase padding throughout modal components for better spacing
- Fix CSS variable self-references (control-padding, standard-transition)
- Improve button styling with better padding (0.875rem 2rem) and colors
- Enhance form elements with generous padding (1rem) and improved focus states
- Redesign tab bar with segmented control appearance and proper active states
- Update context menus with modern glassmorphism styling and smooth animations
- Consolidate calendar management functionality into single reusable component
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
CSS Improvements:
- Remove all mobile responsive CSS (@media queries) - 348+ lines removed
- Add comprehensive CSS variables for glass effects, control dimensions, transitions
- Consolidate duplicate patterns (43+ transition, 37+ border-radius, 61+ padding instances)
- Remove legacy week grid CSS section
- Reduce total CSS from 4,197 to 3,828 lines (8.8% reduction)
Sidebar Enhancements:
- Remove unused sidebar-nav div and navigation link
- Standardize all dropdown controls to consistent 40px height and styling
- Reduce calendar item padding from 0.75rem to 0.5rem for more compact display
- Unify theme-selector and style-selector styling with view-selector
Mobile Detection:
- Add MobileWarningModal component with device detection
- Show helpful popup directing mobile users to native CalDAV apps
- Add Navigator and DomTokenList web-sys features
- Desktop-focused experience with appropriate mobile guidance
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add refresh_calendar_data function to replace window.location.reload()
- Implement dynamic event re-fetching without full page refresh
- Add last_updated timestamp to UserInfo to force component re-renders
- Fix WASM compatibility by using js_sys::Date::now() instead of SystemTime
- Remove debug logging from refresh operations
- Maintain same user experience with improved performance
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Backend: Enhance CalDAV discovery to require at least one valid 207 response
- Backend: Fail authentication if no valid CalDAV endpoints are found
- Frontend: Add token verification on app startup to validate stored tokens
- Frontend: Clear invalid tokens when login fails or token verification fails
- Frontend: Prevent users with invalid tokens from accessing calendar page
This resolves the issue where invalid servers (like google.com) were incorrectly
accepted as valid CalDAV servers, and ensures proper authentication flow.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add UID-based deduplication to prefer recurring events over single events with same UID
- Implement RRULE-generated instance detection to filter duplicate occurrences
- Add title normalization for case-insensitive matching and consolidation
- Fix external calendar refresh button with proper error handling and loading states
- Update context menu for external events to show only "View Event Details" option
- Add comprehensive multi-pass deduplication: UID → title consolidation → RRULE filtering
This resolves issues where Outlook calendars showed duplicate events with same UID
but different RRULE states (e.g., "Dragster Stand Up" appearing both as recurring
and single events).
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The root cause was that drag operations sent naive local time to the backend,
which the backend interpreted using the SERVER's local timezone rather than
the USER's timezone. This caused different behavior between development and
production servers in different timezones.
**Frontend Changes:**
- Convert naive datetime from drag operations to UTC before sending to backend
- Use client-side Local timezone to properly convert user's intended times
- Handle DST transition edge cases with fallback logic
**Backend Changes:**
- Update parse_event_datetime to treat incoming times as UTC (no server timezone conversion)
- Update series handlers to expect UTC times from frontend
- Remove server-side Local timezone dependency for event parsing
**Result:**
- Consistent behavior across all server environments regardless of server timezone
- Drag operations now correctly preserve user's intended local times
- Fixes "4 hours too early" issue in production drag-and-drop operations
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
When users add a new external calendar, events now appear instantly instead
of waiting for the next 5-minute auto-refresh cycle or manual refresh.
Changes:
- Modified ExternalCalendarModal to return newly created calendar ID
- Enhanced on_success callback to immediately fetch events for new calendar
- Added proper visibility checking and error handling
- Removed unused imports to clean up compilation warnings
User experience improvement:
- Before: Add calendar → wait 5 minutes → see events
- After: Add calendar → events appear immediately
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements server-side database caching with 5-minute refresh intervals to
dramatically improve external calendar performance while keeping data fresh.
Backend changes:
- New external_calendar_cache table with ICS data storage
- Smart cache logic: serves from cache if < 5min old, fetches fresh otherwise
- Cache repository methods for get/update/clear operations
- Migration script for cache table creation
Frontend changes:
- 5-minute auto-refresh interval for background updates
- Manual refresh button (🔄) for each external calendar
- Last updated timestamps showing when each calendar was refreshed
- Centralized refresh function with proper cleanup on logout
Performance improvements:
- Initial load: instant from cache vs slow external HTTP requests
- Background updates: fresh data without user waiting
- Reduced external API calls: only when cache is stale
- Scalable: handles multiple external calendars efficiently
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Users can now toggle visibility of CalDAV calendars using checkboxes in
the sidebar, matching the behavior of external calendars. Events from
hidden calendars are automatically filtered out of the calendar view.
Changes:
- Add is_visible field to CalendarInfo (frontend & backend)
- Add visibility checkboxes to CalDAV calendar list items
- Implement real-time event filtering based on calendar visibility
- Add CSS styling matching external calendar checkboxes
- Default new calendars to visible state
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Users can now right-click on external calendar items in the sidebar
to access a context menu with a "Delete Calendar" option. The delete
action removes the calendar from both the server and local state,
including all associated events from the calendar display.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive Windows timezone support for global external calendars
- Map Windows timezone names (e.g. "Mountain Standard Time") to IANA zones (e.g. "America/Denver")
- Support 60+ timezone mappings across North America, Europe, Asia, Asia Pacific, Africa, South America
- Add chrono-tz dependency for proper timezone handling
- Fix external calendar event colors by setting calendar_path for color lookup
- Add visual distinction for external calendar events with dashed borders and calendar emoji
- Update timezone parsing to extract TZID parameters from iCalendar DTSTART/DTEND properties
- Pass external calendar data through component hierarchy for color matching
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Database: Add external_calendars table with user relationships and CRUD operations
- Backend: Implement REST API endpoints for external calendar management and ICS fetching
- Frontend: Add external calendar modal, sidebar section with visibility toggles
- Calendar integration: Merge external events with regular events in unified view
- ICS parsing: Support multiple datetime formats, recurring events, and timezone handling
- Authentication: Integrate with existing JWT token system for user-specific calendars
- UI: Visual distinction with 📅 indicator and separate management section
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Backend now updates RRULE when recurrence_count or recurrence_end_date parameters are provided
- Fixed update_entire_series() to modify COUNT/UNTIL instead of preserving original RRULE
- Added comprehensive RRULE parsing functions to extract existing frequency, interval, count, until, and BYDAY components
- Fixed frontend parameter mapping to pass recurrence parameters through update_series calls
- Resolves issue where changing recurring event from 5 to 7 occurrences kept original COUNT=5
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Events created with specific occurrence counts (like "repeat 5 times") were
repeating forever instead of stopping after the specified number.
Root cause: Frontend form collected recurrence_count and recurrence_until
values correctly, but these weren't being passed through the event creation
pipeline to the backend, which was hardcoding None values.
Fix implemented across entire creation flow:
1. **Enhanced Parameter Conversion**:
- Added recurrence_count and recurrence_until to to_create_event_params() tuple
- Properly extracts values from form: recurrence_count, recurrence_until.map()
2. **Updated Backend Method Signature**:
- Added recurrence_count: Option<u32> and recurrence_until: Option<String>
- to create_event() method parameters
3. **Fixed Backend Implementation**:
- Replace hardcoded None values with actual form parameters
- "recurrence_end_date": recurrence_until, "recurrence_count": recurrence_count
4. **Updated Call Sites**:
- Modified app.rs to pass params.18 (recurrence_count) and params.19 (recurrence_until)
- Proper parameter indexing after tuple expansion
Result: Complete recurrence control now works correctly:
- ✅ Events with COUNT=5 stop after exactly 5 occurrences
- ✅ Events with UNTIL date stop on specified date
- ✅ Events with "repeat forever" continue indefinitely
- ✅ Proper iCalendar RRULE generation with COUNT/UNTIL parameters
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed multiple issues with recurring event editing via modal that were causing
events to be created instead of updated, and API parameter mismatches.
Key fixes:
1. **Restore Update Flow**:
- Added original_uid tracking to EventCreationData to distinguish create vs update
- Modal now routes to update endpoints when editing existing events instead of always creating new ones
- Implemented dual-path logic in on_event_create callback to handle both operations
2. **Fix "This and Future" Updates**:
- Added occurrence_date field to EventCreationData for recurring event context
- Backend now receives required occurrence_date parameter for this_and_future scope
- Populated occurrence_date from event start date in modal conversion
3. **Fix Update Scope Parameters**:
- Corrected scope parameter mapping to match backend API expectations:
* EditAll: "entire_series" → "all_in_series"
* EditFuture: "this_and_future" (correct)
* EditThis: "this_event_only" → "this_only"
4. **Enhanced Backend Integration**:
- Proper routing between update_event() and update_series() based on event type
- Correct parameter handling for both single and recurring event updates
- Added missing parameters (exception_dates, update_action, until_date)
Result: All recurring event edit operations now work correctly:
- ✅ "Edit all events in series" updates existing series instead of creating new
- ✅ "Edit this and future events" properly handles occurrence dates
- ✅ "Edit this event only" works for single instance modifications
- ✅ No more duplicate events created during editing
- ✅ Proper CalDAV server synchronization maintained
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Events dragged to 4am-7am were appearing at 8am-11am due to double
timezone conversion. The issue was:
1. Frontend converted local time to UTC before sending to backend
2. Backend (after previous fix) converted "local time" (actually UTC) to UTC again
3. Result: double conversion causing 4+ hour shift in wrong direction
Solution: Remove frontend UTC conversion in drag-and-drop callback.
Let backend handle the local-to-UTC conversion consistently.
- Remove .and_local_timezone(chrono::Local).unwrap().to_utc() conversion
- Send NaiveDateTime directly as local time strings to backend
- Backend parse_event_datetime() now properly handles local-to-UTC conversion
Now drag-and-drop works correctly: drag to 4am shows 4am.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add database migration for last_used_calendar field in user preferences
- Update backend models and handlers to support last_used_calendar persistence
- Modify frontend preferences service with update_last_used_calendar() method
- Implement automatic saving of selected calendar on event creation
- Add localStorage fallback for offline usage and immediate UI response
- Update create event modal to default to last used calendar for new events
- Clean up unused imports from event form components
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove the original create_event_modal.rs and rename create_event_modal_v2.rs
to complete the modal migration started earlier. This eliminates duplicate code
and consolidates to a single, clean event modal implementation.
Changes:
- Remove original create_event_modal.rs (2,300+ lines)
- Rename create_event_modal_v2.rs → create_event_modal.rs
- Update component/function names: CreateEventModalV2 → CreateEventModal
- Fix all imports in app.rs and calendar.rs
- Add missing to_create_event_params() method to EventCreationData
- Resolve EditAction type conflicts between modules
- Clean up duplicate types and unused imports
- Maintain backwards compatibility with EventCreationData export
Result: -2440 lines, +160 lines - massive code cleanup with zero functionality loss.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit adds a complete style system alongside the existing theme system, allowing users to switch between different UI styles while maintaining theme color variations.
**Core Features:**
- Style enum (Default, Google Calendar) separate from Theme enum
- Hot-swappable stylesheets with dynamic loading
- Style preference persistence (localStorage + database)
- Style picker UI in sidebar below theme picker
**Frontend Implementation:**
- Add Style enum to sidebar.rs with value/display methods
- Implement dynamic stylesheet loading in app.rs
- Add style picker dropdown with proper styling
- Handle style state management and persistence
- Add web-sys features for HtmlLinkElement support
**Backend Integration:**
- Add calendar_style column to user_preferences table
- Update all database operations (insert/update/select)
- Extend API models for style preference
- Add migration for existing users
**Google Calendar Style:**
- Clean Material Design-inspired interface
- White sidebar with proper contrast
- Enhanced calendar grid with subtle shadows
- Improved event styling with hover effects
- Google Sans typography throughout
- Professional color scheme and spacing
**Technical Details:**
- Trunk asset management for stylesheet copying
- High CSS specificity to override theme styles
- Modular CSS architecture for easy extensibility
- Comprehensive text contrast fixes
- Enhanced calendar cells and navigation
Users can now choose between the original gradient design (Default) and a clean Google Calendar-inspired interface (Google Calendar), with full preference persistence across sessions.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moved event fetching logic from CalendarView to Calendar component to properly
use the visible date range instead of hardcoded current month. The Calendar
component already tracks the current visible date through navigation, so events
now load correctly for August and other months when navigating.
Changes:
- Calendar component now manages its own events state and fetching
- Event fetching responds to current_date changes from navigation
- CalendarView simplified to just render Calendar component
- Fixed cargo fmt/clippy formatting across codebase
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The modal update flow was calling the regular event update endpoint
instead of the series endpoint, preventing proper handling of the
three edit types (this event, this and future, all events).
## Changes:
- Add logic to detect when edit_scope is set for recurring events
- Route to update_series() when edit_scope is present and event has RRULE
- Map EditAction enum to backend update_scope strings:
- EditThis → "this_only" (creates exception + EXDATE)
- EditFuture → "this_and_future" (new series + UNTIL on original)
- EditAll → "all_in_series" (update existing series)
- Pass occurrence date for single/future edits using original event date
- Fall back to regular update_event() for non-recurring events
Now the modal properly leverages the existing robust series endpoint
that handles RFC 5545 compliant recurring event modifications.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Frontend Changes:
- Add EditAction enum (EditThis, EditFuture, EditAll) to event context menu
- Update context menu to show 3 edit options for recurring events
- Enhance EventCreationData with edit_scope and changed_fields tracking
- Update app component to handle EditAction types and pass to modal
- Add field change tracking infrastructure to CreateEventModal
## Backend Changes:
- Add changed_fields parameter to UpdateEventSeriesRequest for optimization
- Existing series endpoint already supports the three update types:
- "this_only" - creates exception with EXDATE
- "this_and_future" - creates new series with UNTIL on original
- "all_in_series" - updates existing series in-place
## Implementation Details:
- Event context menu shows single edit option for non-recurring events
- Recurring events get three options: "Edit This Event", "Edit This and Future Events", "Edit All Events in Series"
- Modal tracks which fields user actually changed for efficient updates
- Backend series endpoint already has the logic for all three update scenarios
- Full RFC 5545 compliance with proper EXDATE and UNTIL handling
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add multi-stage Dockerfile with dependency caching for both frontend and backend
- Implement Docker Compose configuration with separate frontend/backend services
- Configure Caddy as reverse proxy with proper WASM and static file serving
- Add volume mounting for frontend assets shared between containers
- Optimize build process with staged compilation and workspace handling
- Add debug logging and WASM initialization tracking for production deployment
- Update README with project motivation and "vibe coded" disclaimer
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed unused functions and variables identified after RRULE parameter fix:
- Remove unused build_series_rrule function from backend series handler
- Remove unused RecurrenceType::from_rrule and helper functions from frontend
- Prefix unused state variables with underscores to suppress warnings
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit resolves the "Failed to fetch" errors when updating recurring
event series through drag operations by implementing proper request
sequencing and fixing time parameter handling.
Key fixes:
- Eliminate HTTP request cancellation by sequencing operations properly
- Add global mutex to prevent CalDAV HTTP race conditions
- Implement complete RFC 5545-compliant series splitting for "this_and_future"
- Fix frontend to pass dragged times instead of original times
- Add comprehensive error handling and request timing logs
- Backend now handles both UPDATE (add UNTIL) and CREATE (new series) in single request
Technical changes:
- Frontend: Remove concurrent CREATE request, pass dragged times to backend
- Backend: Implement full this_and_future logic with sequential operations
- CalDAV: Add mutex serialization and detailed error tracking
- Series: Create new series with occurrence date + dragged times
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit resolves multiple issues with event deletion:
Backend fixes:
- Fix CalDAV URL construction for DELETE requests (missing slash separator)
- Improve event lookup by href with exact matching and fallback to UID extraction
- Add support for both RFC3339 and simple YYYY-MM-DD date formats in occurrence parsing
- Implement proper logic to distinguish recurring vs non-recurring events in delete_this action
- For non-recurring events: delete entire event from CalDAV server
- For recurring events: add EXDATE to exclude specific occurrences
- Add comprehensive debug logging for troubleshooting deletion issues
Frontend fixes:
- Update callback signatures to support series endpoint parameters (7-parameter tuples)
- Add update_series method to CalendarService for series-specific operations
- Route single occurrence modifications through series endpoint with proper scoping
- Fix all component prop definitions to use new callback signature
- Update all emit calls to pass correct number of parameters
The deletion process now works correctly:
- Single events are completely removed from the calendar
- Recurring event occurrences are properly excluded via EXDATE
- Debug logging helps identify and resolve CalDAV communication issues
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Created calendar-models/ shared library with RFC 5545-compliant VEvent structures
- Migrated backend to use shared VEvent with proper field mappings (dtstart/dtend, rrule, exdate, etc.)
- Converted CalDAV client to parse into VEvent structures with structured types
- Updated all CRUD handlers to use VEvent with CalendarUser, Attendee, VAlarm types
- Restructured project as Cargo workspace with frontend/, backend/, calendar-models/
- Updated Trunk configuration for new directory structure
- Fixed all compilation errors and field references throughout codebase
- Updated documentation and build instructions for workspace structure
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>