diff --git a/internal/air/static/css/calendar-grid.css b/internal/air/static/css/calendar-grid.css index 036970b..af32338 100644 --- a/internal/air/static/css/calendar-grid.css +++ b/internal/air/static/css/calendar-grid.css @@ -178,23 +178,61 @@ background: white; } - .today-dot { + /* Pulsing today indicator */ + .today-indicator { position: absolute; - bottom: 8px; - left: 50%; - transform: translateX(-50%); - width: 6px; - height: 6px; + top: 8px; + right: 8px; + width: 8px; + height: 8px; background: var(--accent); border-radius: 50%; + animation: todayPulse 2s ease-in-out infinite; } - .event-dot { - width: 8px; - height: 8px; + @keyframes todayPulse { + 0%, 100% { + transform: scale(1); + opacity: 1; + box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.4); + } + 50% { + transform: scale(1.2); + opacity: 0.8; + box-shadow: 0 0 0 6px rgba(139, 92, 246, 0); + } + } + + /* Event count badge */ + .event-count-badge { + position: absolute; + bottom: 8px; + right: 8px; + min-width: 20px; + height: 20px; + padding: 0 6px; background: var(--accent); - border-radius: 50%; - margin-top: 4px; + border-radius: 10px; + font-size: 11px; + font-weight: 600; + color: white; + display: flex; + align-items: center; + justify-content: center; + } + + .calendar-day.selected .event-count-badge { + background: white; + color: var(--accent); + } + + /* Legacy support */ + .today-dot { + display: none; + } + + .event-dot { + display: none; } .calendar-events-panel { @@ -233,6 +271,7 @@ border: 1px solid var(--border); border-radius: 14px; transition: all 0.2s; + position: relative; } .event-card:hover { @@ -240,6 +279,35 @@ transform: translateY(-2px); } + /* Edit button - top right corner */ + .event-card .event-edit-btn { + position: absolute !important; + top: 8px; + right: 8px; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-surface); + border: 1px solid var(--border); + border-radius: 6px; + color: var(--text-muted); + cursor: pointer; + opacity: 0; + transition: all 0.2s; + } + + .event-card:hover .event-edit-btn { + opacity: 1; + } + + .event-edit-btn:hover { + background: var(--bg-hover); + color: var(--text-primary); + border-color: var(--border-light); + } + .event-card.focus-time { background: linear-gradient(135deg, rgba(34, 197, 94, 0.1) 0%, rgba(16, 185, 129, 0.05) 100%); border-color: rgba(34, 197, 94, 0.3); @@ -262,10 +330,46 @@ margin-bottom: 10px; } + .event-time-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; + } + .event-time { font-size: 12px; color: var(--text-muted); - margin-bottom: 6px; + } + + .event-relative-time { + font-size: 11px; + font-weight: 600; + padding: 2px 8px; + border-radius: 10px; + background: var(--bg-surface); + color: var(--text-muted); + } + + .event-relative-time.starting-now { + background: linear-gradient(135deg, #ef4444 0%, #f97316 100%); + color: white; + animation: urgentPulse 1s ease-in-out infinite; + } + + .event-relative-time.starting-soon { + background: linear-gradient(135deg, #f59e0b 0%, #eab308 100%); + color: white; + } + + .event-relative-time.upcoming { + background: rgba(139, 92, 246, 0.15); + color: var(--accent); + } + + @keyframes urgentPulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.7; } } .event-title { @@ -316,6 +420,31 @@ color: var(--text-muted); } + /* Join Meeting button */ + .join-meeting-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + background: linear-gradient(135deg, #4285f4 0%, #34a853 100%); + border-radius: 8px; + font-size: 13px; + font-weight: 600; + color: white; + text-decoration: none; + transition: all 0.2s; + box-shadow: 0 2px 8px rgba(66, 133, 244, 0.3); + } + + .join-meeting-btn:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(66, 133, 244, 0.4); + } + + .join-meeting-btn:active { + transform: translateY(0); + } + .event-actions { display: flex; gap: 8px; diff --git a/internal/air/static/js/calendar-data.js b/internal/air/static/js/calendar-data.js index 38a6b74..9995e89 100644 --- a/internal/air/static/js/calendar-data.js +++ b/internal/air/static/js/calendar-data.js @@ -21,7 +21,8 @@ async loadEvents() { const data = await AirAPI.getEvents({ start: Math.floor(start.getTime() / 1000), - end: Math.floor(end.getTime() / 1000) + end: Math.floor(end.getTime() / 1000), + limit: 200 // Fetch more events to cover the full month }); this.events = data.events || []; diff --git a/internal/air/static/js/calendar-ui.js b/internal/air/static/js/calendar-ui.js index 644f08a..504126f 100644 --- a/internal/air/static/js/calendar-ui.js +++ b/internal/air/static/js/calendar-ui.js @@ -45,21 +45,23 @@ renderCalendarGrid() { for (let day = 1; day <= daysInMonth; day++) { const isToday = isCurrentMonth && day === todayDate; const dateStr = `${year}-${month + 1}-${day}`; - const hasEvents = this.events.some(e => { + // Count events for this day + const dayEvents = this.events.filter(e => { const eventDate = new Date(e.start_time * 1000); return eventDate.getFullYear() === year && eventDate.getMonth() === month && eventDate.getDate() === day; }); + const eventCount = dayEvents.length; let classes = 'calendar-day'; if (isToday) classes += ' today'; - if (hasEvents) classes += ' has-event'; + if (eventCount > 0) classes += ' has-event'; html += `