From 2db711586e5378d82cb2fc5a3176a84a73358893 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Tue, 7 Oct 2025 07:19:06 +1100 Subject: [PATCH] analysis: AI analysis for issue #859 [Bug]: Multiple days task glitches in calendar view Generated by ai-issue-analyzer --- issue-analysis/issue-859.md | 236 ++++++++++++++++++ ...-859-bases-calendar-multi-day-task.test.ts | 167 +++++++++++++ 2 files changed, 403 insertions(+) create mode 100644 issue-analysis/issue-859.md create mode 100644 tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts diff --git a/issue-analysis/issue-859.md b/issue-analysis/issue-859.md new file mode 100644 index 00000000..81a8820b --- /dev/null +++ b/issue-analysis/issue-859.md @@ -0,0 +1,236 @@ +# Issue #859 Analysis: Multiple Days Task Glitches in Calendar View + +## Problem Understanding + +### Issue Description +When creating a multi-day task by dragging in the calendar view, the task duration behaves unexpectedly. Users report that when they drag to create a 2-day task, it spreads to an entire week, several weeks, or any random amount of days. This happens in both the base calendar view (Bases integration) and the old calendar view (AdvancedCalendarView). + +### Root Cause +The bug is **only in the Bases calendar view** (`src/bases/calendar-view.ts`), not in AdvancedCalendarView. The issue occurs because the `handleDateSelect` function in the Bases calendar view doesn't calculate the `timeEstimate` property for multi-day all-day task selections, unlike AdvancedCalendarView which does. + +When a user drags across multiple days: +1. The Bases calendar view only sets the `scheduled` date to the start date +2. It does NOT calculate or set the `timeEstimate` based on the number of days dragged +3. The TaskCreationModal then either uses a default time estimate or shows an incorrect duration +4. When the task is displayed back on the calendar, the end date is calculated from `start + timeEstimate`, which doesn't match the user's intended selection + +The AdvancedCalendarView (old calendar view) already has the fix implemented (see issue #564), but this fix was never ported to the Bases calendar view. + +### Evidence +Comparing the two implementations: + +**AdvancedCalendarView** (`src/views/AdvancedCalendarView.ts:1182-1225`): +- ✅ Calculates `daysDuration` for multi-day all-day selections +- ✅ Sets `timeEstimate = daysDuration * minutesPerDay` when `daysDuration > 1` +- ✅ Handles single-day selections correctly (no time estimate) +- ✅ Handles timed selections correctly + +**Bases Calendar View** (`src/bases/calendar-view.ts:108-139`): +- ❌ Does NOT calculate duration for multi-day selections +- ❌ Only sets `scheduled` date, ignoring the dragged duration +- ❌ Missing the entire multi-day calculation logic + +## Test File Location + +**Test File**: `tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts` + +**How to Run**: +```bash +npm test -- tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts +``` + +The test demonstrates: +1. The current buggy behavior (timeEstimate is undefined for multi-day selections) +2. The expected fixed behavior (timeEstimate should be set to `daysDuration * 1440` minutes) + +## Relevant Code Locations + +### Primary Bug Location +- **File**: `src/bases/calendar-view.ts` +- **Function**: `handleDateSelect` (lines 108-139) +- **Issue**: Missing multi-day duration calculation logic + +### Reference Implementation (Working) +- **File**: `src/views/AdvancedCalendarView.ts` +- **Function**: `handleTaskCreation` (lines 1182-1225) +- **Status**: Already fixed for issue #564 + +### Related Test Files +- **Existing test**: `tests/unit/issues/issue-564-multi-day-task-creation.test.ts` (shows correct behavior) +- **New test**: `tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts` (demonstrates bug) + +### Shared Calendar Core +- **File**: `src/bases/calendar-core.ts` +- **Relevance**: Contains shared event generation logic but not task creation logic + +## Proposed Solutions + +### Solution 1: Port the Fix from AdvancedCalendarView (Recommended) + +**Approach**: Copy the multi-day duration calculation logic from `AdvancedCalendarView.handleTaskCreation` to `calendar-view.ts:handleDateSelect`. + +**Implementation**: +```typescript +// In calendar-view.ts, replace lines 125-133 with: +const scheduledDate = allDay + ? format(start, "yyyy-MM-dd") + : format(start, "yyyy-MM-dd'T'HH:mm"); + +const durationMinutes = Math.round((end.getTime() - start.getTime()) / (1000 * 60)); + +// Convert slot duration setting to minutes for comparison +const slotDurationMinutes = 30; // Default from settings + +// Determine if this was a drag (intentional time selection) or just a click +const isDragOperation = !allDay && durationMinutes > slotDurationMinutes; + +const prePopulatedValues: any = { + scheduled: scheduledDate +}; + +// Calculate duration for multi-day all-day selections +if (allDay) { + const dayDurationMillis = 24 * 60 * 60 * 1000; + const daysDuration = Math.round((end.getTime() - start.getTime()) / dayDurationMillis); + + if (daysDuration > 1) { + const minutesPerDay = 60 * 24; + prePopulatedValues.timeEstimate = daysDuration * minutesPerDay; + } +} else if (isDragOperation) { + prePopulatedValues.timeEstimate = durationMinutes; +} + +const { TaskCreationModal } = require("../modals/TaskCreationModal"); +const modal = new TaskCreationModal(plugin.app, plugin, { + prePopulatedValues +}); +modal.open(); +``` + +**Pros**: +- ✅ Direct fix that matches the working implementation +- ✅ Consistent behavior across both calendar views +- ✅ Minimal code changes (only modifying `handleDateSelect`) +- ✅ Already proven to work in AdvancedCalendarView +- ✅ Easy to test and verify + +**Cons**: +- ⚠️ Requires accessing `plugin.settings.calendarViewSettings.slotDuration` which may need to be passed to the factory function +- ⚠️ Minor code duplication between two calendar views + +**Risk**: Low - This is a proven fix already working in production + +--- + +### Solution 2: Extract to Shared Function + +**Approach**: Create a shared function in `calendar-core.ts` for task creation logic, then use it in both calendar views. + +**Implementation**: +1. Create `calculateTaskCreationValues(start: Date, end: Date, allDay: boolean, slotDurationMinutes: number)` in `calendar-core.ts` +2. Use this function in both `AdvancedCalendarView.handleTaskCreation` and `calendar-view.ts:handleDateSelect` + +**Pros**: +- ✅ DRY principle - single source of truth +- ✅ Easier to maintain and test +- ✅ Any future fixes apply to both views automatically +- ✅ Better code organization + +**Cons**: +- ⚠️ Requires refactoring both calendar views +- ⚠️ More extensive testing needed +- ⚠️ Larger changeset with higher risk of regression + +**Risk**: Medium - More extensive changes but better long-term solution + +--- + +### Solution 3: Minimal Patch (Quick Fix) + +**Approach**: Add just the multi-day calculation without the full drag detection logic. + +**Implementation**: +```typescript +// In calendar-view.ts, after line 128: +const scheduledDate = allDay + ? format(start, "yyyy-MM-dd") + : format(start, "yyyy-MM-dd'T'HH:mm"); + +const prePopulatedValues: any = { + scheduled: scheduledDate +}; + +// Quick fix: add multi-day duration calculation +if (allDay) { + const daysDuration = Math.round((end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000)); + if (daysDuration > 1) { + prePopulatedValues.timeEstimate = daysDuration * 60 * 24; + } +} +``` + +**Pros**: +- ✅ Minimal code change +- ✅ Very low risk +- ✅ Fast to implement and test + +**Cons**: +- ❌ Doesn't handle timed task duration (week view drags) +- ❌ Missing drag vs. click detection logic +- ❌ Incomplete fix - only solves all-day multi-day tasks +- ❌ Still inconsistent with AdvancedCalendarView + +**Risk**: Low, but incomplete solution + +--- + +## Recommended Approach + +**Solution 1: Port the Fix from AdvancedCalendarView** + +This is the recommended approach because: + +1. **Proven Solution**: The fix already works in AdvancedCalendarView since issue #564 +2. **Complete Fix**: Handles all cases (multi-day, single-day, timed, clicks) +3. **Low Risk**: Minimal changes to a well-understood bug +4. **Fast Implementation**: Can be done in a single commit +5. **Easy to Test**: Can verify against existing test patterns + +### Implementation Steps + +1. Locate the `handleDateSelect` function in `src/bases/calendar-view.ts:108` +2. Replace lines 126-133 with the duration calculation logic from AdvancedCalendarView +3. Access the slot duration setting from plugin settings (may need to pass it to the factory) +4. Run the test suite to verify the fix +5. Manually test both calendar views to ensure consistency + +### Testing Strategy + +1. Run unit test: `npm test -- tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts` +2. Run existing test: `npm test -- tests/unit/issues/issue-564-multi-day-task-creation.test.ts` +3. Manual testing in Bases calendar view: + - Drag to create 2-day task → should have 2880 minutes time estimate + - Drag to create 7-day task → should have 10080 minutes time estimate + - Click single day → should use default time estimate + - Drag timed selection → should calculate duration in minutes +4. Verify AdvancedCalendarView still works correctly + +### Edge Cases to Consider + +- User drags across daylight saving time boundaries +- User drags across month/year boundaries +- Very long selections (30+ days) +- Selections that start/end at non-midnight times but are marked allDay + +--- + +## Alternative Considerations + +If **Solution 2 (Extract to Shared Function)** is preferred for long-term maintainability: + +1. Implement Solution 1 first as an immediate fix +2. Create a follow-up task to refactor both views to use shared logic +3. This allows the bug to be fixed quickly while planning for better architecture + +This two-phase approach balances speed of fix with code quality goals. diff --git a/tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts b/tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts new file mode 100644 index 00000000..a2f5e262 --- /dev/null +++ b/tests/unit/issues/issue-859-bases-calendar-multi-day-task.test.ts @@ -0,0 +1,167 @@ +/** + * Test for Issue #859: Multiple days task glitches in calendar view + * + * When creating a multi-day task by dragging in the Bases calendar view, + * the task duration should correctly reflect the number of days selected. + * Currently, the Bases calendar view (calendar-view.ts) doesn't calculate + * the time estimate for multi-day all-day tasks, unlike AdvancedCalendarView. + */ + +import { describe, it, expect } from '@jest/globals'; +import { format } from 'date-fns'; + +describe('Issue #859: Multi-day task creation in Bases calendar view', () => { + // Simulate the CURRENT (buggy) handleDateSelect logic from calendar-view.ts + function simulateBasesCalendarTaskCreation(start: Date, end: Date, allDay: boolean) { + const scheduledDate = allDay + ? format(start, "yyyy-MM-dd") + : format(start, "yyyy-MM-dd'T'HH:mm"); + + const prePopulatedValues: any = { + scheduled: scheduledDate + }; + + // BUG: calendar-view.ts doesn't calculate timeEstimate for multi-day selections + // It only sets the scheduled date, without considering the duration + + return prePopulatedValues; + } + + // Simulate the FIXED handleDateSelect logic (matching AdvancedCalendarView) + function simulateBasesCalendarTaskCreationFixed(start: Date, end: Date, allDay: boolean) { + const scheduledDate = allDay + ? format(start, "yyyy-MM-dd") + : format(start, "yyyy-MM-dd'T'HH:mm"); + + const durationMinutes = Math.round((end.getTime() - start.getTime()) / (1000 * 60)); + + // Convert slot duration setting to minutes for comparison + const slotDurationMinutes = 30; // Default 30 minutes + + // Determine if this was a drag (intentional time selection) or just a click + const isDragOperation = !allDay && durationMinutes > slotDurationMinutes; + + const prePopulatedValues: any = { + scheduled: scheduledDate + }; + + // FIX: Calculate duration for multi-day all-day selections + if (allDay) { + // For all-day events, calculate duration in days if multi-day selection + const dayDurationMillis = 24 * 60 * 60 * 1000; // milliseconds in a day + const daysDuration = Math.round((end.getTime() - start.getTime()) / dayDurationMillis); + + if (daysDuration > 1) { + // Multi-day selection: set time estimate based on days + const minutesPerDay = 60 * 24; + prePopulatedValues.timeEstimate = daysDuration * minutesPerDay; + } + // For single-day all-day events, let TaskCreationModal use the default setting + } else if (isDragOperation) { + // User dragged to select a specific duration, use that + prePopulatedValues.timeEstimate = durationMinutes; + } + + return prePopulatedValues; + } + + describe('Current buggy behavior', () => { + it('should FAIL: does not set time estimate for 2-day task', () => { + // User drags to create a 2-day task + const start = new Date('2025-01-15T00:00:00.000Z'); + const end = new Date('2025-01-17T00:00:00.000Z'); + + const result = simulateBasesCalendarTaskCreation(start, end, true); + + // BUG: timeEstimate is not set, so it will be undefined + expect(result.scheduled).toBe('2025-01-15'); + expect(result.timeEstimate).toBeUndefined(); // This demonstrates the bug + // Should be: expect(result.timeEstimate).toBe(2880); // 2 days = 2880 minutes + }); + + it('should FAIL: does not set time estimate for 7-day task', () => { + // User drags to create a week-long task + const start = new Date('2025-01-15T00:00:00.000Z'); + const end = new Date('2025-01-22T00:00:00.000Z'); + + const result = simulateBasesCalendarTaskCreation(start, end, true); + + // BUG: timeEstimate is not set + expect(result.scheduled).toBe('2025-01-15'); + expect(result.timeEstimate).toBeUndefined(); // This demonstrates the bug + // Should be: expect(result.timeEstimate).toBe(10080); // 7 days = 10080 minutes + }); + + it('should FAIL: does not set time estimate for random multi-day selection', () => { + // User drags to create a 5-day task + const start = new Date('2025-03-10T00:00:00.000Z'); + const end = new Date('2025-03-15T00:00:00.000Z'); + + const result = simulateBasesCalendarTaskCreation(start, end, true); + + // BUG: timeEstimate is not set, leading to unexpected behavior + expect(result.scheduled).toBe('2025-03-10'); + expect(result.timeEstimate).toBeUndefined(); // This demonstrates the bug + // Should be: expect(result.timeEstimate).toBe(7200); // 5 days = 7200 minutes + }); + }); + + describe('Fixed behavior', () => { + it('should set correct time estimate for 2-day all-day task', () => { + const start = new Date('2025-01-15T00:00:00.000Z'); + const end = new Date('2025-01-17T00:00:00.000Z'); + + const result = simulateBasesCalendarTaskCreationFixed(start, end, true); + + // 2 days = 2 * 24 * 60 = 2880 minutes + expect(result.scheduled).toBe('2025-01-15'); + expect(result.timeEstimate).toBe(2880); + }); + + it('should set correct time estimate for 7-day all-day task', () => { + const start = new Date('2025-01-15T00:00:00.000Z'); + const end = new Date('2025-01-22T00:00:00.000Z'); + + const result = simulateBasesCalendarTaskCreationFixed(start, end, true); + + // 7 days = 7 * 24 * 60 = 10080 minutes + expect(result.scheduled).toBe('2025-01-15'); + expect(result.timeEstimate).toBe(10080); + }); + + it('should set correct time estimate for single-day all-day task', () => { + const start = new Date('2025-01-15T00:00:00.000Z'); + const end = new Date('2025-01-16T00:00:00.000Z'); + + const result = simulateBasesCalendarTaskCreationFixed(start, end, true); + + // Single day all-day task should not have time estimate set (use default) + expect(result.scheduled).toBe('2025-01-15'); + expect(result.timeEstimate).toBeUndefined(); + }); + + it('should work correctly for timed tasks (week view behavior)', () => { + // 2 hour selection in week view (timed) + const start = new Date('2025-01-15T09:00:00.000Z'); + const end = new Date('2025-01-15T11:00:00.000Z'); + + const result = simulateBasesCalendarTaskCreationFixed(start, end, false); + + // 2 hours = 120 minutes + expect(result.scheduled).toBe('2025-01-15T09:00'); + expect(result.timeEstimate).toBe(120); + }); + + it('should not set time estimate for short timed selections (clicks)', () => { + // Short click in week view (less than slot duration) + const start = new Date('2025-01-15T09:00:00.000Z'); + const end = new Date('2025-01-15T09:15:00.000Z'); // 15 minutes < 30 minute slot + + const result = simulateBasesCalendarTaskCreationFixed(start, end, false); + + // Short selection should not set time estimate (use default) + expect(result.scheduled).toBe('2025-01-15T09:00'); + expect(result.timeEstimate).toBeUndefined(); + }); + }); +});