Files
calendar/styles.css
Connor Johnstone d945c46e5a Implement real-time event refresh functionality
- Backend: Add GET /api/calendar/events/:uid endpoint for single event refresh
- Backend: Implement fetch_event_by_uid method to retrieve updated events from CalDAV
- Frontend: Add event click callback system to trigger refresh on interaction
- Frontend: Display loading state with orange pulsing animation during refresh
- Frontend: Smart event data updates without full calendar reload
- Frontend: Graceful error handling with fallback to cached data
- CSS: Add refreshing animation for visual feedback during updates

Events now automatically refresh from CalDAV server when clicked, ensuring
users always see the most current event data including changes made in
other calendar applications.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 17:51:30 -04:00

589 lines
10 KiB
CSS

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f8f9fa;
color: #333;
line-height: 1.6;
}
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.app-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.app-header h1 {
margin: 0;
font-size: 1.8rem;
}
.app-header nav {
display: flex;
gap: 1rem;
align-items: center;
}
.app-header nav a {
color: white;
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 4px;
transition: background-color 0.2s;
}
.app-header nav a:hover {
background-color: rgba(255,255,255,0.2);
}
.app-main {
flex: 1;
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
/* Authentication Forms */
.login-container, .register-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
}
.login-form, .register-form {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
width: 100%;
max-width: 400px;
}
.login-form h2, .register-form h2 {
text-align: center;
margin-bottom: 2rem;
color: #333;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #555;
}
.form-group input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
transition: border-color 0.2s;
}
.form-group input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
}
.form-group input:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
.login-button, .register-button {
width: 100%;
padding: 0.75rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.login-button:hover, .register-button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.login-button:disabled, .register-button:disabled {
background: #ccc;
transform: none;
cursor: not-allowed;
}
.error-message {
background-color: #f8d7da;
color: #721c24;
padding: 0.75rem;
border-radius: 4px;
margin-bottom: 1rem;
border: 1px solid #f5c6cb;
}
.auth-links {
text-align: center;
margin-top: 2rem;
color: #666;
}
.auth-links a {
color: #667eea;
text-decoration: none;
}
.auth-links a:hover {
text-decoration: underline;
}
.logout-button {
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
color: white;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.logout-button:hover {
background: rgba(255,255,255,0.3);
}
/* Calendar View */
.calendar-view {
height: calc(100vh - 140px); /* Full height minus header and padding */
display: flex;
flex-direction: column;
}
.calendar-loading, .calendar-error {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background: white;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}
.calendar-loading p {
font-size: 1.2rem;
color: #666;
}
.calendar-error p {
font-size: 1.2rem;
color: #d32f2f;
}
/* Calendar Component */
.calendar {
background: white;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
}
.calendar-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.month-year {
font-size: 1.8rem;
font-weight: 600;
margin: 0;
}
.nav-button {
background: rgba(255,255,255,0.2);
border: none;
color: white;
font-size: 1.5rem;
font-weight: bold;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
}
.nav-button:hover {
background: rgba(255,255,255,0.3);
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
flex: 1;
background: white;
}
.weekday-header {
background: #f8f9fa;
padding: 1rem;
text-align: center;
font-weight: 600;
color: #666;
border-bottom: 1px solid #e9ecef;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.calendar-day {
border: 1px solid #f0f0f0;
padding: 0.75rem;
min-height: 100px;
display: flex;
flex-direction: column;
cursor: pointer;
transition: background-color 0.2s;
position: relative;
}
.calendar-day:hover {
background-color: #f8f9ff;
}
.calendar-day.current-month {
background: white;
}
.calendar-day.prev-month,
.calendar-day.next-month {
background: #fafafa;
color: #ccc;
}
.calendar-day.today {
background: #e3f2fd;
border: 2px solid #2196f3;
}
.calendar-day.has-events {
background: #fff3e0;
}
.calendar-day.today.has-events {
background: #e1f5fe;
}
.calendar-day.selected {
background: #e8f5e8;
border: 2px solid #4caf50;
box-shadow: 0 0 8px rgba(76, 175, 80, 0.3);
}
.calendar-day.selected.has-events {
background: #f1f8e9;
}
.calendar-day.selected.today {
background: #e0f2f1;
border: 2px solid #4caf50;
}
.calendar-day.selected .day-number {
color: #2e7d32;
font-weight: 700;
}
.day-number {
font-weight: 600;
font-size: 1.1rem;
margin-bottom: 0.5rem;
}
.calendar-day.today .day-number {
color: #1976d2;
}
.event-indicators {
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.event-box {
background: #2196f3;
color: white;
padding: 2px 4px;
border-radius: 3px;
font-size: 0.7rem;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
transition: background-color 0.2s;
}
.event-box:hover {
background: #1976d2;
}
.event-box.refreshing {
background: #ff9800;
animation: pulse 1.5s ease-in-out infinite alternate;
}
@keyframes pulse {
0% {
opacity: 0.7;
}
100% {
opacity: 1;
}
}
.event-dot {
background: #ff9800;
height: 6px;
border-radius: 3px;
margin-bottom: 1px;
}
.more-events {
font-size: 0.7rem;
color: #666;
margin-top: 2px;
font-weight: 500;
}
/* Event Modal Styles */
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(2px);
}
.modal-content {
background: white;
border-radius: 12px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
position: relative;
animation: modalAppear 0.2s ease-out;
}
@keyframes modalAppear {
from {
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem 2rem 1rem;
border-bottom: 1px solid #e9ecef;
}
.modal-header h3 {
margin: 0;
color: #333;
font-size: 1.4rem;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
font-size: 1.8rem;
color: #999;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
line-height: 1;
}
.modal-close:hover {
background: #f8f9fa;
color: #666;
transform: scale(1.1);
}
.modal-body {
padding: 1.5rem 2rem 2rem;
}
.event-detail {
display: grid;
grid-template-columns: 100px 1fr;
gap: 1rem;
margin-bottom: 1rem;
align-items: start;
}
.event-detail strong {
color: #555;
font-weight: 600;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.event-detail span {
color: #333;
font-size: 1rem;
line-height: 1.5;
word-break: break-word;
}
/* Responsive Design */
@media (max-width: 768px) {
.calendar-header {
padding: 1rem;
}
.month-year {
font-size: 1.4rem;
}
.nav-button {
width: 35px;
height: 35px;
font-size: 1.2rem;
}
.weekday-header {
padding: 0.5rem;
font-size: 0.8rem;
}
.calendar-day {
min-height: 70px;
padding: 0.5rem;
}
.day-number {
font-size: 1rem;
}
.app-main {
padding: 1rem;
}
.calendar-view {
height: calc(100vh - 120px);
}
}
@media (max-width: 480px) {
.calendar-day {
min-height: 60px;
padding: 0.25rem;
}
.weekday-header {
padding: 0.5rem 0.25rem;
}
.day-number {
font-size: 0.9rem;
}
}
/* Mobile adjustments for modal */
@media (max-width: 768px) {
.modal-content {
margin: 1rem;
width: calc(100% - 2rem);
}
.modal-header, .modal-body {
padding: 1rem 1.5rem;
}
.event-detail {
grid-template-columns: 80px 1fr;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.event-detail strong {
font-size: 0.8rem;
}
.event-detail span {
font-size: 0.9rem;
}
.app-header {
flex-direction: column;
text-align: center;
padding: 1rem;
}
.app-header nav {
margin-top: 1rem;
}
.app-main {
padding: 1rem;
}
.login-form, .register-form {
padding: 1.5rem;
}
}