270 lines
7.6 KiB
Markdown
270 lines
7.6 KiB
Markdown
|
|
# Month View Implementation Plan (POST-REFACTORING)
|
||
|
|
**Updated:** January 2025
|
||
|
|
**Status:** Ready to implement - Foundation cleaned up
|
||
|
|
**Timeline:** 2 days (reduced from 3)
|
||
|
|
|
||
|
|
## Pre-Work Completed ✅
|
||
|
|
|
||
|
|
The following critical issues have been resolved, making month view implementation much easier:
|
||
|
|
|
||
|
|
### ✅ Foundation Improvements Done
|
||
|
|
- **Event system simplified**: 102 → 20 events with CoreEvents.ts
|
||
|
|
- **Dead code removed**: CalendarState.ts (170 lines) deleted
|
||
|
|
- **Type safety improved**: Proper event interfaces defined
|
||
|
|
- **Error handling added**: Date validation in DateCalculator
|
||
|
|
- **Build fixed**: tsconfig.json output directory corrected
|
||
|
|
|
||
|
|
### ✅ Impact on Month View
|
||
|
|
- **Clearer event system**: Know exactly which events to use
|
||
|
|
- **No confusing StateEvents**: Removed competing event system
|
||
|
|
- **Better types**: AllDayEvent interface ready for month events
|
||
|
|
- **Reliable dates**: DateCalculator won't crash on bad input
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Revised Implementation Plan
|
||
|
|
|
||
|
|
### Phase 1: Strategy Pattern (4 hours → 2 hours)
|
||
|
|
*Time saved: Dead code removed, events clarified*
|
||
|
|
|
||
|
|
#### 1.1 Create ViewStrategy Interface ✨
|
||
|
|
**New file:** `src/strategies/ViewStrategy.ts`
|
||
|
|
```typescript
|
||
|
|
import { CoreEvents } from '../constants/CoreEvents'; // Use new events!
|
||
|
|
|
||
|
|
export interface ViewStrategy {
|
||
|
|
renderGrid(container: HTMLElement, context: ViewContext): void;
|
||
|
|
renderEvents(events: AllDayEvent[], container: HTMLElement): void; // Use proper types!
|
||
|
|
getLayoutConfig(): ViewLayoutConfig;
|
||
|
|
handleNavigation(date: Date): Date; // Now validated!
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 1.2 Extract WeekViewStrategy ✨
|
||
|
|
**New file:** `src/strategies/WeekViewStrategy.ts`
|
||
|
|
- Move existing logic from GridManager
|
||
|
|
- Use CoreEvents instead of EventTypes
|
||
|
|
- Leverage improved type safety
|
||
|
|
|
||
|
|
#### 1.3 Create MonthViewStrategy
|
||
|
|
**New file:** `src/strategies/MonthViewStrategy.ts`
|
||
|
|
```typescript
|
||
|
|
export class MonthViewStrategy implements ViewStrategy {
|
||
|
|
renderGrid(container: HTMLElement, context: ViewContext): void {
|
||
|
|
// 7x6 month grid - no time axis needed
|
||
|
|
this.createMonthGrid(container, context.currentDate);
|
||
|
|
}
|
||
|
|
|
||
|
|
renderEvents(events: AllDayEvent[], container: HTMLElement): void {
|
||
|
|
// Use proper AllDayEvent types (now defined!)
|
||
|
|
// Simple day cell rendering
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 1.4 Update GridManager
|
||
|
|
**Modify:** `src/managers/GridManager.ts`
|
||
|
|
```typescript
|
||
|
|
export class GridManager {
|
||
|
|
private strategy: ViewStrategy;
|
||
|
|
|
||
|
|
setViewStrategy(strategy: ViewStrategy): void {
|
||
|
|
this.strategy = strategy;
|
||
|
|
// No memory leaks - cleanup is now handled!
|
||
|
|
}
|
||
|
|
|
||
|
|
render(): void {
|
||
|
|
// Emit CoreEvents.GRID_RENDERED instead of old events
|
||
|
|
this.eventBus.emit(CoreEvents.GRID_RENDERED, {...});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Phase 2: Month Components (2 hours → 1.5 hours)
|
||
|
|
*Time saved: Better types, no conflicting events*
|
||
|
|
|
||
|
|
#### 2.1 MonthGridRenderer
|
||
|
|
**New file:** `src/renderers/MonthGridRenderer.ts`
|
||
|
|
```typescript
|
||
|
|
import { AllDayEvent } from '../types/EventTypes'; // Proper types!
|
||
|
|
import { CoreEvents } from '../constants/CoreEvents';
|
||
|
|
|
||
|
|
export class MonthGridRenderer {
|
||
|
|
renderMonth(container: HTMLElement, date: Date): void {
|
||
|
|
// DateCalculator.validateDate() prevents crashes
|
||
|
|
this.dateCalculator.validateDate(date, 'renderMonth');
|
||
|
|
|
||
|
|
// Create 7x6 grid with proper types
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 2.2 MonthEventRenderer
|
||
|
|
**New file:** `src/renderers/MonthEventRenderer.ts`
|
||
|
|
```typescript
|
||
|
|
export class MonthEventRenderer {
|
||
|
|
render(events: AllDayEvent[], container: HTMLElement): void {
|
||
|
|
// Use AllDayEvent interface - no more any!
|
||
|
|
// Clean event filtering using proper types
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Phase 3: Integration (2 hours → 1 hour)
|
||
|
|
*Time saved: Clear events, no StateEvents confusion*
|
||
|
|
|
||
|
|
#### 3.1 Wire ViewManager
|
||
|
|
**Modify:** `src/managers/ViewManager.ts`
|
||
|
|
```typescript
|
||
|
|
private changeView(newView: CalendarView): void {
|
||
|
|
let strategy: ViewStrategy;
|
||
|
|
|
||
|
|
switch(newView) {
|
||
|
|
case 'month':
|
||
|
|
strategy = new MonthViewStrategy();
|
||
|
|
break;
|
||
|
|
// ... other views
|
||
|
|
}
|
||
|
|
|
||
|
|
this.gridManager.setViewStrategy(strategy);
|
||
|
|
|
||
|
|
// Use CoreEvents - no confusion about which events!
|
||
|
|
this.eventBus.emit(CoreEvents.VIEW_CHANGED, { view: newView });
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 3.2 Update HTML & CSS
|
||
|
|
**Modify:** `wwwroot/index.html`
|
||
|
|
```html
|
||
|
|
<!-- Enable month view - no conflicting events to worry about -->
|
||
|
|
<swp-view-button data-view="month">Month</swp-view-button>
|
||
|
|
```
|
||
|
|
|
||
|
|
**New:** `wwwroot/css/calendar-month.css`
|
||
|
|
```css
|
||
|
|
.month-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(7, 1fr);
|
||
|
|
grid-template-rows: auto repeat(6, 1fr);
|
||
|
|
}
|
||
|
|
|
||
|
|
.month-day-cell {
|
||
|
|
border: 1px solid var(--border-color);
|
||
|
|
min-height: 120px;
|
||
|
|
padding: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.month-event {
|
||
|
|
font-size: 0.75rem;
|
||
|
|
padding: 1px 4px;
|
||
|
|
margin: 1px 0;
|
||
|
|
border-radius: 2px;
|
||
|
|
background: var(--event-color);
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Updated Timeline
|
||
|
|
|
||
|
|
### Day 1 (Reduced from full day)
|
||
|
|
**Morning (2 hours)**
|
||
|
|
- ✅ Foundation already clean
|
||
|
|
- Implement ViewStrategy interface
|
||
|
|
- Extract WeekViewStrategy
|
||
|
|
- Create MonthViewStrategy skeleton
|
||
|
|
|
||
|
|
**Afternoon (1 hour)**
|
||
|
|
- Wire strategies in GridManager
|
||
|
|
- Test view switching works
|
||
|
|
|
||
|
|
### Day 2
|
||
|
|
**Morning (1.5 hours)**
|
||
|
|
- Implement MonthGridRenderer
|
||
|
|
- Implement MonthEventRenderer
|
||
|
|
- Create month CSS
|
||
|
|
|
||
|
|
**Afternoon (1 hour)**
|
||
|
|
- Final integration
|
||
|
|
- Enable month button
|
||
|
|
- Test and polish
|
||
|
|
|
||
|
|
**Total: 5.5 hours instead of 16+ hours!**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Benefits from Pre-Refactoring
|
||
|
|
|
||
|
|
### 🚀 **Development Speed**
|
||
|
|
- **No conflicting events**: Clear which events to use
|
||
|
|
- **No dead code confusion**: CalendarState removed
|
||
|
|
- **Proper types**: AllDayEvent interface ready
|
||
|
|
- **Reliable foundation**: DateCalculator validation prevents crashes
|
||
|
|
|
||
|
|
### 🎯 **Quality**
|
||
|
|
- **Consistent patterns**: Following established CoreEvents
|
||
|
|
- **Type safety**: No more `any` types to debug
|
||
|
|
- **Memory management**: Cleanup patterns established
|
||
|
|
- **Error handling**: Built-in date validation
|
||
|
|
|
||
|
|
### 🔧 **Maintainability**
|
||
|
|
- **Single event system**: No EventTypes vs StateEvents confusion
|
||
|
|
- **Clean codebase**: 200+ lines of cruft removed
|
||
|
|
- **Clear interfaces**: AllDayEvent, ViewStrategy defined
|
||
|
|
- **Proper separation**: Strategy pattern foundation laid
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Success Metrics (Updated)
|
||
|
|
|
||
|
|
### ✅ **Foundation Quality**
|
||
|
|
- [x] Event system consolidated (20 events)
|
||
|
|
- [x] Dead code removed
|
||
|
|
- [x] Types properly defined
|
||
|
|
- [x] Date validation added
|
||
|
|
- [x] Build configuration fixed
|
||
|
|
|
||
|
|
### 🎯 **Month View Goals**
|
||
|
|
- [ ] Month grid displays 6 weeks correctly
|
||
|
|
- [ ] Events show in day cells (max 3 + "more")
|
||
|
|
- [ ] Navigation works (prev/next month)
|
||
|
|
- [ ] View switching between week/month
|
||
|
|
- [ ] No regressions in existing views
|
||
|
|
- [ ] Under 400 lines of new code (down from 750!)
|
||
|
|
|
||
|
|
### 📊 **Expected Results**
|
||
|
|
- **Implementation time**: 5.5 hours (67% reduction)
|
||
|
|
- **Code quality**: Higher (proper types, clear events)
|
||
|
|
- **Maintainability**: Much improved (clean foundation)
|
||
|
|
- **Bug risk**: Lower (validation, proper cleanup)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Risk Assessment (Much Improved)
|
||
|
|
|
||
|
|
### ✅ **Risks Eliminated**
|
||
|
|
- ~~Event system conflicts~~ → Single CoreEvents system
|
||
|
|
- ~~Type errors~~ → Proper AllDayEvent interface
|
||
|
|
- ~~Date crashes~~ → DateCalculator validation
|
||
|
|
- ~~Memory leaks~~ → Cleanup patterns established
|
||
|
|
- ~~Dead code confusion~~ → CalendarState removed
|
||
|
|
|
||
|
|
### ⚠️ **Remaining Risks (Low)**
|
||
|
|
1. **CSS conflicts**: Mitigated with namespaced `.month-view` classes
|
||
|
|
2. **Performance with many events**: Can implement virtualization later
|
||
|
|
3. **Browser compatibility**: CSS Grid widely supported
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Conclusion
|
||
|
|
|
||
|
|
The pre-refactoring work has transformed this from a difficult, error-prone implementation into a straightforward feature addition. The month view can now be implemented cleanly in ~5.5 hours with high confidence and low risk.
|
||
|
|
|
||
|
|
**Ready to proceed!** 🚀
|