From 6583ed10637ff5a9e8c340b18c72ffc6937ffe4d Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Wed, 12 Nov 2025 21:17:20 +0100 Subject: [PATCH] Adds event description support in calendar UI Extends event model to include optional description field Enhances event rendering with: - New description getter/setter in base event element - Updated CSS grid layout for description display - Dynamic description handling in event rendering - Updated mock event data with descriptions Improves visual information density and event context in calendar view --- src/elements/SwpEventElement.ts | 27 ++++++++++++- src/types/CalendarTypes.ts | 1 + wwwroot/css/calendar-events-css.css | 63 +++++++++++++++++++++++++++-- wwwroot/data/mock-events.json | 27 +++++++++++++ 4 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/elements/SwpEventElement.ts b/src/elements/SwpEventElement.ts index 6acbbb5..9f288e3 100644 --- a/src/elements/SwpEventElement.ts +++ b/src/elements/SwpEventElement.ts @@ -60,6 +60,13 @@ export abstract class BaseSwpEventElement extends HTMLElement { this.dataset.title = value; } + get description(): string { + return this.dataset.description || ''; + } + set description(value: string) { + this.dataset.description = value; + } + get type(): string { return this.dataset.type || 'work'; } @@ -77,7 +84,7 @@ export class SwpEventElement extends BaseSwpEventElement { * Observed attributes - changes trigger attributeChangedCallback */ static get observedAttributes() { - return ['data-start', 'data-end', 'data-title', 'data-type']; + return ['data-start', 'data-end', 'data-title', 'data-description', 'data-type']; } /** @@ -199,6 +206,7 @@ export class SwpEventElement extends BaseSwpEventElement { this.innerHTML = ` ${timeRange} ${this.title} + ${this.description ? `${this.description}` : ''} `; } @@ -208,6 +216,7 @@ export class SwpEventElement extends BaseSwpEventElement { private updateDisplay(): void { const timeEl = this.querySelector('swp-event-time'); const titleEl = this.querySelector('swp-event-title'); + const descEl = this.querySelector('swp-event-description'); if (timeEl && this.dataset.start && this.dataset.end) { const start = new Date(this.dataset.start); @@ -223,6 +232,20 @@ export class SwpEventElement extends BaseSwpEventElement { if (titleEl && this.dataset.title) { titleEl.textContent = this.dataset.title; } + + if (this.dataset.description) { + if (descEl) { + descEl.textContent = this.dataset.description; + } else if (this.description) { + // Add description element if it doesn't exist + const newDescEl = document.createElement('swp-event-description'); + newDescEl.textContent = this.description; + this.appendChild(newDescEl); + } + } else if (descEl) { + // Remove description element if description is empty + descEl.remove(); + } } @@ -265,6 +288,7 @@ export class SwpEventElement extends BaseSwpEventElement { element.dataset.eventId = event.id; element.dataset.title = event.title; + element.dataset.description = event.description || ''; element.dataset.start = dateService.toUTC(event.start); element.dataset.end = dateService.toUTC(event.end); element.dataset.type = event.type; @@ -280,6 +304,7 @@ export class SwpEventElement extends BaseSwpEventElement { return { id: element.dataset.eventId || '', title: element.dataset.title || '', + description: element.dataset.description || undefined, start: new Date(element.dataset.start || ''), end: new Date(element.dataset.end || ''), type: element.dataset.type || 'work', diff --git a/src/types/CalendarTypes.ts b/src/types/CalendarTypes.ts index 77dbd8c..9c7cb50 100644 --- a/src/types/CalendarTypes.ts +++ b/src/types/CalendarTypes.ts @@ -17,6 +17,7 @@ export interface IRenderContext { export interface ICalendarEvent { id: string; title: string; + description?: string; start: Date; end: Date; type: string; // Flexible event type - can be any string value diff --git a/wwwroot/css/calendar-events-css.css b/wwwroot/css/calendar-events-css.css index 5feab37..927b485 100644 --- a/wwwroot/css/calendar-events-css.css +++ b/wwwroot/css/calendar-events-css.css @@ -12,7 +12,14 @@ swp-day-columns swp-event { right: 2px; color: var(--color-text); font-size: 12px; - padding: 2px 4px; + padding: 4px 6px; + + /* CSS Grid layout for time, title, and description */ + display: grid; + grid-template-columns: auto 1fr; + grid-template-rows: auto 1fr; + gap: 2px 4px; + align-items: start; /* Event types */ &[data-type="meeting"] { @@ -137,16 +144,66 @@ swp-resize-handle::before { } swp-day-columns swp-event-time { - display: block; + grid-column: 1; + grid-row: 1; font-size: 0.875rem; font-weight: 500; - margin-bottom: 4px; + white-space: nowrap; } swp-day-columns swp-event-title { + grid-column: 2; + grid-row: 1; + font-size: 0.875rem; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +swp-day-columns swp-event-description { + grid-column: 1 / -1; + grid-row: 2; display: block; font-size: 0.875rem; + opacity: 0.8; line-height: 1.3; + overflow: hidden; + word-wrap: break-word; +} + +/* Hide description if event is too short (<60px) */ +swp-day-columns swp-event[style*="height: 30px"] swp-event-description, +swp-day-columns swp-event[style*="height: 31px"] swp-event-description, +swp-day-columns swp-event[style*="height: 32px"] swp-event-description, +swp-day-columns swp-event[style*="height: 33px"] swp-event-description, +swp-day-columns swp-event[style*="height: 34px"] swp-event-description, +swp-day-columns swp-event[style*="height: 35px"] swp-event-description, +swp-day-columns swp-event[style*="height: 36px"] swp-event-description, +swp-day-columns swp-event[style*="height: 37px"] swp-event-description, +swp-day-columns swp-event[style*="height: 38px"] swp-event-description, +swp-day-columns swp-event[style*="height: 39px"] swp-event-description, +swp-day-columns swp-event[style*="height: 40px"] swp-event-description, +swp-day-columns swp-event[style*="height: 41px"] swp-event-description, +swp-day-columns swp-event[style*="height: 42px"] swp-event-description, +swp-day-columns swp-event[style*="height: 43px"] swp-event-description, +swp-day-columns swp-event[style*="height: 44px"] swp-event-description, +swp-day-columns swp-event[style*="height: 45px"] swp-event-description, +swp-day-columns swp-event[style*="height: 46px"] swp-event-description, +swp-day-columns swp-event[style*="height: 47px"] swp-event-description, +swp-day-columns swp-event[style*="height: 48px"] swp-event-description, +swp-day-columns swp-event[style*="height: 49px"] swp-event-description, +swp-day-columns swp-event[style*="height: 50px"] swp-event-description, +swp-day-columns swp-event[style*="height: 51px"] swp-event-description, +swp-day-columns swp-event[style*="height: 52px"] swp-event-description, +swp-day-columns swp-event[style*="height: 53px"] swp-event-description, +swp-day-columns swp-event[style*="height: 54px"] swp-event-description, +swp-day-columns swp-event[style*="height: 55px"] swp-event-description, +swp-day-columns swp-event[style*="height: 56px"] swp-event-description, +swp-day-columns swp-event[style*="height: 57px"] swp-event-description, +swp-day-columns swp-event[style*="height: 58px"] swp-event-description, +swp-day-columns swp-event[style*="height: 59px"] swp-event-description { + display: none; } /* Multi-day events */ diff --git a/wwwroot/data/mock-events.json b/wwwroot/data/mock-events.json index 9c5e552..ed8e4d4 100644 --- a/wwwroot/data/mock-events.json +++ b/wwwroot/data/mock-events.json @@ -3251,6 +3251,7 @@ { "id": "NOV10-001", "title": "Morgen Standup", + "description": "Daily team sync - status updates", "start": "2025-11-10T05:00:00Z", "end": "2025-11-10T05:30:00Z", "type": "meeting", @@ -3264,6 +3265,7 @@ { "id": "NOV10-002", "title": "Sprint Planning", + "description": "Plan backlog items and estimate story points", "start": "2025-11-10T06:00:00Z", "end": "2025-11-10T07:30:00Z", "type": "meeting", @@ -3277,6 +3279,7 @@ { "id": "NOV10-003", "title": "Udvikling af ny feature", + "description": "Implement user authentication module with OAuth2 support, JWT tokens, refresh token rotation, and secure password hashing using bcrypt. Include comprehensive unit tests and integration tests for all authentication flows.", "start": "2025-11-10T08:00:00Z", "end": "2025-11-10T11:00:00Z", "type": "work", @@ -3290,6 +3293,7 @@ { "id": "NOV10-004", "title": "Frokostmøde med klient", + "description": "Discuss project requirements and timeline", "start": "2025-11-10T08:00:00Z", "end": "2025-11-10T09:00:00Z", "type": "meal", @@ -3316,6 +3320,7 @@ { "id": "NOV11-001", "title": "Morgen Standup", + "description": "Quick sync on progress and blockers", "start": "2025-11-11T05:00:00Z", "end": "2025-11-11T05:30:00Z", "type": "meeting", @@ -3329,6 +3334,7 @@ { "id": "NOV11-002", "title": "Arkitektur Review", + "description": "Review system design and scalability", "start": "2025-11-11T07:00:00Z", "end": "2025-11-11T08:30:00Z", "type": "meeting", @@ -3342,6 +3348,7 @@ { "id": "NOV11-003", "title": "Code Review Session", + "description": "Review pull requests and provide feedback", "start": "2025-11-11T10:00:00Z", "end": "2025-11-11T11:30:00Z", "type": "work", @@ -3355,6 +3362,7 @@ { "id": "NOV11-004", "title": "Database Optimering", + "description": "Optimize queries and add indexes", "start": "2025-11-11T13:00:00Z", "end": "2025-11-11T15:00:00Z", "type": "work", @@ -3381,6 +3389,7 @@ { "id": "NOV12-001", "title": "Morgen Standup", + "description": "Team alignment and daily planning", "start": "2025-11-12T05:00:00Z", "end": "2025-11-12T05:30:00Z", "type": "meeting", @@ -3394,6 +3403,7 @@ { "id": "NOV12-002", "title": "Teknisk Workshop", + "description": "Learn new frameworks and best practices", "start": "2025-11-12T06:00:00Z", "end": "2025-11-12T08:00:00Z", "type": "meeting", @@ -3407,6 +3417,7 @@ { "id": "NOV12-003", "title": "API Udvikling", + "description": "Build REST endpoints for mobile app including user profile management, push notifications, real-time chat functionality, file upload with image compression, and comprehensive API documentation using OpenAPI specification. Implement rate limiting and caching strategies.", "start": "2025-11-12T09:00:00Z", "end": "2025-11-12T12:00:00Z", "type": "work", @@ -3420,6 +3431,7 @@ { "id": "NOV12-004", "title": "Klient Præsentation", + "description": "Demo new features and gather feedback", "start": "2025-11-12T13:00:00Z", "end": "2025-11-12T14:30:00Z", "type": "meeting", @@ -3433,6 +3445,7 @@ { "id": "NOV13-001", "title": "Morgen Standup", + "description": "Daily sync and impediment removal", "start": "2025-11-13T05:00:00Z", "end": "2025-11-13T05:30:00Z", "type": "meeting", @@ -3446,6 +3459,7 @@ { "id": "NOV13-002", "title": "Performance Testing", + "description": "Load testing and bottleneck analysis", "start": "2025-11-13T07:00:00Z", "end": "2025-11-13T09:00:00Z", "type": "work", @@ -3459,6 +3473,7 @@ { "id": "NOV13-003", "title": "Sikkerhedsgennemgang", + "description": "Security audit and vulnerability scan", "start": "2025-11-13T10:00:00Z", "end": "2025-11-13T11:30:00Z", "type": "meeting", @@ -3472,6 +3487,7 @@ { "id": "NOV13-004", "title": "Bug Fixing Session", + "description": "Fix critical bugs from production", "start": "2025-11-13T13:00:00Z", "end": "2025-11-13T15:00:00Z", "type": "work", @@ -3498,6 +3514,7 @@ { "id": "NOV14-001", "title": "Morgen Standup", + "description": "Sprint wrap-up and final status check", "start": "2025-11-14T05:00:00Z", "end": "2025-11-14T05:30:00Z", "type": "meeting", @@ -3511,6 +3528,7 @@ { "id": "NOV14-002", "title": "Sprint Review", + "description": "Demo completed work to stakeholders", "start": "2025-11-14T06:00:00Z", "end": "2025-11-14T07:00:00Z", "type": "meeting", @@ -3524,6 +3542,7 @@ { "id": "NOV14-003", "title": "Retrospektiv", + "description": "Reflect on sprint and identify improvements", "start": "2025-11-14T07:30:00Z", "end": "2025-11-14T08:30:00Z", "type": "meeting", @@ -3537,6 +3556,7 @@ { "id": "NOV14-004", "title": "Dokumentation", + "description": "Update technical documentation including architecture diagrams, API reference with request/response examples, deployment guides for production and staging environments, troubleshooting section with common issues and solutions, and developer onboarding documentation with setup instructions.", "start": "2025-11-14T10:00:00Z", "end": "2025-11-14T12:00:00Z", "type": "work", @@ -3550,6 +3570,7 @@ { "id": "NOV14-005", "title": "Deployment Planning", + "description": "Plan release strategy and rollback", "start": "2025-11-14T13:00:00Z", "end": "2025-11-14T14:00:00Z", "type": "meeting", @@ -3563,6 +3584,7 @@ { "id": "NOV15-001", "title": "Morgen Standup", + "description": "New sprint kickoff and goal setting", "start": "2025-11-15T05:00:00Z", "end": "2025-11-15T05:30:00Z", "type": "meeting", @@ -3576,6 +3598,7 @@ { "id": "NOV15-002", "title": "Feature Demo", + "description": "Showcase new functionality to team", "start": "2025-11-15T07:00:00Z", "end": "2025-11-15T08:00:00Z", "type": "meeting", @@ -3589,6 +3612,7 @@ { "id": "NOV15-003", "title": "Refactoring Session", + "description": "Clean up technical debt and improve code", "start": "2025-11-15T09:00:00Z", "end": "2025-11-15T11:00:00Z", "type": "work", @@ -3602,6 +3626,7 @@ { "id": "NOV15-004", "title": "Klient Opkald", + "description": "Weekly status update and next steps", "start": "2025-11-15T13:00:00Z", "end": "2025-11-15T14:00:00Z", "type": "meeting", @@ -3628,6 +3653,7 @@ { "id": "NOV16-001", "title": "Weekend Projekt", + "description": "Personal coding project and experimentation", "start": "2025-11-16T06:00:00Z", "end": "2025-11-16T10:00:00Z", "type": "work", @@ -3641,6 +3667,7 @@ { "id": "NOV16-002", "title": "Personlig Udvikling", + "description": "Learn new technologies and skills", "start": "2025-11-16T11:00:00Z", "end": "2025-11-16T13:00:00Z", "type": "work",