Fix notes popup positioning, reposition task actions to avoid overlap, and improve Apple Calendar sync
This commit is contained in:
parent
368928c016
commit
279d1a4c49
@ -967,35 +967,45 @@ h3 {
|
|||||||
color: var(--weekly-text);
|
color: var(--weekly-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Action Buttons Styling */
|
/* Action Buttons Styling - Float above task on hover to avoid overlap */
|
||||||
.task-actions {
|
.task-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: -26px; /* Position above the task item */
|
||||||
bottom: 0;
|
background: var(--weekly-bg, white);
|
||||||
z-index: 10;
|
border: 1px solid var(--weekly-border, #e0e0e0);
|
||||||
padding-left: 8px;
|
border-radius: 6px;
|
||||||
padding-right: 4px;
|
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
|
||||||
|
z-index: 100;
|
||||||
|
padding: 2px 4px;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Time-grid tasks: show actions below task text as a dropdown toolbar */
|
/* Time-grid tasks: show actions above task text as a floating toolbar on hover */
|
||||||
.time-slot-task > .task-actions {
|
.time-slot-task > .task-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: auto;
|
top: -28px; /* Float above the task */
|
||||||
bottom: auto;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
right: auto;
|
right: auto;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
background: var(--weekly-bg, white);
|
background: var(--weekly-bg, white);
|
||||||
border: 1px solid var(--weekly-border, #e0e0e0);
|
border: 1px solid var(--weekly-border, #e0e0e0);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
z-index: 20;
|
z-index: 100; /* Ensure it stays above everything */
|
||||||
|
display: flex !important; /* Force flex even if default is hidden */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure actions don't get clipped by the scrolling container */
|
||||||
|
.time-slots-container {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .time-slot-task > .task-actions {
|
.dark .time-slot-task > .task-actions {
|
||||||
|
|||||||
@ -331,8 +331,8 @@ export function GridTaskBlock({
|
|||||||
)}
|
)}
|
||||||
<button className="task-action-btn delete text-red-500 hover:text-red-700 hover:bg-red-100/50 dark:hover:bg-red-900/30 rounded" onClick={(e) => { e.stopPropagation(); deleteTask(task.id); }} title="Delete">
|
<button className="task-action-btn delete text-red-500 hover:text-red-700 hover:bg-red-100/50 dark:hover:bg-red-900/30 rounded" onClick={(e) => { e.stopPropagation(); deleteTask(task.id); }} title="Delete">
|
||||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<line x1="18" y1="6" x2="6" y2="18" />
|
<polyline points="3 6 5 6 21 6"></polyline>
|
||||||
<line x1="6" y1="6" x2="18" y2="18" />
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -5854,7 +5854,7 @@ function TaskItem({
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="task-actions absolute right-0 top-0 bottom-0 flex items-center bg-white/95 dark:bg-gray-800/95 pl-2 pr-1 shadow-[-12px_0_12px_-4px_rgba(255,255,255,0.95)] dark:shadow-[-12px_0_12px_-4px_rgba(31,41,55,0.95)] z-20">
|
<div className="task-actions z-20">
|
||||||
{/* Complete */}
|
{/* Complete */}
|
||||||
<button
|
<button
|
||||||
className={`task-action-btn ${task.completed ? "active text-green-600 dark:text-green-500" : ""}`}
|
className={`task-action-btn ${task.completed ? "active text-green-600 dark:text-green-500" : ""}`}
|
||||||
@ -6028,8 +6028,8 @@ function TaskItem({
|
|||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
>
|
>
|
||||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
<polyline points="3 6 5 6 21 6"></polyline>
|
||||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export interface AppleCalendarEvent {
|
|||||||
endDate: string;
|
endDate: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
location?: string;
|
location?: string;
|
||||||
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppleCalendar {
|
export interface AppleCalendar {
|
||||||
@ -165,7 +166,8 @@ export const getUpcomingEvents = async (
|
|||||||
startDate: exIsAllDay ? exStart.toISOString().slice(0, 10) : exStart.toISOString(),
|
startDate: exIsAllDay ? exStart.toISOString().slice(0, 10) : exStart.toISOString(),
|
||||||
endDate: exIsAllDay ? exEnd.toISOString().slice(0, 10) : exEnd.toISOString(),
|
endDate: exIsAllDay ? exEnd.toISOString().slice(0, 10) : exEnd.toISOString(),
|
||||||
description: exEvent.description,
|
description: exEvent.description,
|
||||||
location: exEvent.location
|
location: exEvent.location,
|
||||||
|
url: exVevent.getFirstPropertyValue('url')?.toString() || undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -197,7 +199,8 @@ export const getUpcomingEvents = async (
|
|||||||
startDate: isAllDayRecurring ? occStart.toISOString().slice(0, 10) : occStart.toISOString(),
|
startDate: isAllDayRecurring ? occStart.toISOString().slice(0, 10) : occStart.toISOString(),
|
||||||
endDate: isAllDayRecurring ? occEnd.toISOString().slice(0, 10) : occEnd.toISOString(),
|
endDate: isAllDayRecurring ? occEnd.toISOString().slice(0, 10) : occEnd.toISOString(),
|
||||||
description: event.description,
|
description: event.description,
|
||||||
location: event.location
|
location: event.location,
|
||||||
|
url: vevent.getFirstPropertyValue('url')?.toString() || undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (expandErr: any) {
|
} catch (expandErr: any) {
|
||||||
@ -217,7 +220,8 @@ export const getUpcomingEvents = async (
|
|||||||
startDate: isAllDay ? start.toISOString().slice(0, 10) : start.toISOString(),
|
startDate: isAllDay ? start.toISOString().slice(0, 10) : start.toISOString(),
|
||||||
endDate: isAllDay ? end.toISOString().slice(0, 10) : end.toISOString(),
|
endDate: isAllDay ? end.toISOString().slice(0, 10) : end.toISOString(),
|
||||||
description: event.description,
|
description: event.description,
|
||||||
location: event.location
|
location: event.location,
|
||||||
|
url: vevent.getFirstPropertyValue('url')?.toString() || undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -338,7 +342,8 @@ END:VCALENDAR`;
|
|||||||
startDate: eventData.start.dateTime || eventData.start.date || '',
|
startDate: eventData.start.dateTime || eventData.start.date || '',
|
||||||
endDate: eventData.end.dateTime || eventData.end.date || '',
|
endDate: eventData.end.dateTime || eventData.end.date || '',
|
||||||
description: eventData.description,
|
description: eventData.description,
|
||||||
location: eventData.location
|
location: eventData.location,
|
||||||
|
url: eventData.url
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[APPLE CALENDAR] Error creating event:', error);
|
console.error('[APPLE CALENDAR] Error creating event:', error);
|
||||||
@ -553,7 +558,8 @@ export const updateEvent = async (
|
|||||||
startDate: event.startDate.toString(),
|
startDate: event.startDate.toString(),
|
||||||
endDate: event.endDate.toString(),
|
endDate: event.endDate.toString(),
|
||||||
description: event.description,
|
description: event.description,
|
||||||
location: event.location
|
location: event.location,
|
||||||
|
url: vevent.getFirstPropertyValue('url')?.toString() || undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -56,9 +56,9 @@ function toOutlookRecurrence(recurrence?: string, startDate?: Date): any {
|
|||||||
case 'daily':
|
case 'daily':
|
||||||
return { pattern: { type: 'daily', interval: 1 }, range };
|
return { pattern: { type: 'daily', interval: 1 }, range };
|
||||||
case 'weekly':
|
case 'weekly':
|
||||||
return { pattern: { type: 'weekly', interval: 1, daysOfWeek: [['sunday','monday','tuesday','wednesday','thursday','friday','saturday'][start.getDay()]] }, range };
|
return { pattern: { type: 'weekly', interval: 1, daysOfWeek: [['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][start.getDay()]] }, range };
|
||||||
case 'biweekly':
|
case 'biweekly':
|
||||||
return { pattern: { type: 'weekly', interval: 2, daysOfWeek: [['sunday','monday','tuesday','wednesday','thursday','friday','saturday'][start.getDay()]] }, range };
|
return { pattern: { type: 'weekly', interval: 2, daysOfWeek: [['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][start.getDay()]] }, range };
|
||||||
case 'monthly':
|
case 'monthly':
|
||||||
return { pattern: { type: 'absoluteMonthly', interval: 1, dayOfMonth: start.getDate() }, range };
|
return { pattern: { type: 'absoluteMonthly', interval: 1, dayOfMonth: start.getDate() }, range };
|
||||||
case 'yearly':
|
case 'yearly':
|
||||||
@ -360,6 +360,7 @@ export const getCalendarEvents = async (
|
|||||||
date: startIsAllDay ? event.endDate : undefined,
|
date: startIsAllDay ? event.endDate : undefined,
|
||||||
},
|
},
|
||||||
location: event.location,
|
location: event.location,
|
||||||
|
url: event.url,
|
||||||
source: 'apple' as const,
|
source: 'apple' as const,
|
||||||
calendarId,
|
calendarId,
|
||||||
calendarTitle: calendars.find(c => c.id === calendarId)?.title || 'Apple Calendar',
|
calendarTitle: calendars.find(c => c.id === calendarId)?.title || 'Apple Calendar',
|
||||||
@ -751,6 +752,7 @@ export const updateCalendarEvent = async (
|
|||||||
start: { dateTime: updatedEvent.startDate },
|
start: { dateTime: updatedEvent.startDate },
|
||||||
end: { dateTime: updatedEvent.endDate },
|
end: { dateTime: updatedEvent.endDate },
|
||||||
location: updatedEvent.location,
|
location: updatedEvent.location,
|
||||||
|
url: updatedEvent.url,
|
||||||
source: 'apple',
|
source: 'apple',
|
||||||
calendarId,
|
calendarId,
|
||||||
calendarTitle: '',
|
calendarTitle: '',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user