style: make CalendarEventModal more compact for mobile

Reduce padding, gaps, font sizes, and combine Status/Visibility into
a single row for better usability on smaller screens.

v1.36.1
This commit is contained in:
mARTin 2026-03-16 17:04:25 +01:00
parent b9c05571be
commit d991e7a65b
2 changed files with 220 additions and 232 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "my-weekly-todo-list", "name": "my-weekly-todo-list",
"version": "1.36.0", "version": "1.36.1",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -87,7 +87,6 @@ export default function CalendarEventModal({
); );
const [newAttachmentUrl, setNewAttachmentUrl] = useState(''); const [newAttachmentUrl, setNewAttachmentUrl] = useState('');
const [showMoreOptions, setShowMoreOptions] = useState( const [showMoreOptions, setShowMoreOptions] = useState(
// Auto-expand if event has these fields set
!!(event?.attendees?.length || event?.attachments?.length || (event?.busyStatus && event.busyStatus !== 'busy') || (event?.visibility && event.visibility !== 'default')) !!(event?.attendees?.length || event?.attachments?.length || (event?.busyStatus && event.busyStatus !== 'busy') || (event?.visibility && event.visibility !== 'default'))
); );
@ -143,7 +142,7 @@ export default function CalendarEventModal({
case 'google': return <FontAwesomeIcon icon={faGoogle} style={{ opacity: 0.8 }} />; case 'google': return <FontAwesomeIcon icon={faGoogle} style={{ opacity: 0.8 }} />;
case 'apple': return <FontAwesomeIcon icon={faApple} style={{ opacity: 0.8 }} />; case 'apple': return <FontAwesomeIcon icon={faApple} style={{ opacity: 0.8 }} />;
case 'outlook': return <FontAwesomeIcon icon={faMicrosoft} style={{ opacity: 0.8 }} />; case 'outlook': return <FontAwesomeIcon icon={faMicrosoft} style={{ opacity: 0.8 }} />;
case 'synology': return <FontAwesomeIcon icon={faServer} style={{ opacity: 0.8, fontSize: '0.8rem' }} />; case 'synology': return <FontAwesomeIcon icon={faServer} style={{ opacity: 0.8, fontSize: '0.75rem' }} />;
default: return null; default: return null;
} }
}; };
@ -165,7 +164,6 @@ export default function CalendarEventModal({
setIsSaving(true); setIsSaving(true);
setError(''); setError('');
try { try {
// Filter out "none" reminders
const activeReminders = reminders.filter(r => r.minutes >= 0); const activeReminders = reminders.filter(r => r.minutes >= 0);
await onSave({ await onSave({
@ -255,7 +253,6 @@ export default function CalendarEventModal({
const updateReminder = (index: number, minutes: number) => { const updateReminder = (index: number, minutes: number) => {
if (minutes === -1) { if (minutes === -1) {
// Remove this reminder
setReminders(reminders.filter((_, i) => i !== index)); setReminders(reminders.filter((_, i) => i !== index));
return; return;
} }
@ -270,30 +267,32 @@ export default function CalendarEventModal({
const rowStyle: React.CSSProperties = { const rowStyle: React.CSSProperties = {
display: 'flex', alignItems: 'center', justifyContent: 'space-between', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '0 10px', minHeight: '32px',
}; };
const labelStyle: React.CSSProperties = { const labelStyle: React.CSSProperties = {
fontSize: '0.9rem', color: 'var(--weekly-text-light)', fontWeight: 500, fontSize: '0.82rem', color: 'var(--weekly-text-light)', fontWeight: 500,
}; };
const selectStyle: React.CSSProperties = { const selectStyle: React.CSSProperties = {
background: 'transparent', border: 'none', textAlign: 'right' as const, background: 'transparent', border: 'none', textAlign: 'right' as const,
fontSize: '0.9rem', cursor: 'pointer', outline: 'none', color: 'var(--weekly-text)', fontSize: '0.82rem', cursor: 'pointer', outline: 'none', color: 'var(--weekly-text)',
}; };
return ( return (
<div className="weekly-modal-overlay" onClick={onClose}> <div className="weekly-modal-overlay" onClick={onClose}>
<div className="weekly-modal-content" onClick={e => e.stopPropagation()} style={{ <div className="weekly-modal-content" onClick={e => e.stopPropagation()} style={{
maxWidth: '450px', maxWidth: '400px',
padding: '20px', width: 'calc(100vw - 16px)',
borderRadius: '12px', padding: '12px',
borderRadius: '10px',
boxShadow: '0 10px 25px rgba(0,0,0,0.15)', boxShadow: '0 10px 25px rgba(0,0,0,0.15)',
border: '1px solid var(--weekly-border)', border: '1px solid var(--weekly-border)',
maxHeight: '90vh', maxHeight: '85vh',
overflowY: 'auto', overflowY: 'auto',
}}> }}>
{error && <div style={{ color: '#ef4444', marginBottom: '1rem', fontSize: '0.85rem', textAlign: 'center' }}>{error}</div>} {error && <div style={{ color: '#ef4444', marginBottom: '6px', fontSize: '0.8rem', textAlign: 'center' }}>{error}</div>}
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
{/* Title Input */} {/* Title */}
<input <input
type="text" type="text"
value={title} value={title}
@ -301,87 +300,74 @@ export default function CalendarEventModal({
placeholder="New Event" placeholder="New Event"
autoFocus autoFocus
style={{ style={{
width: '100%', width: '100%', padding: '6px 10px', fontSize: '1.05rem', fontWeight: 600,
padding: '12px 16px', border: 'none', borderBottom: '1px solid var(--weekly-border)',
fontSize: '1.25rem', borderRadius: '0', background: 'transparent', outline: 'none',
fontWeight: 600,
border: 'none',
borderBottom: '1px solid var(--weekly-border)',
borderRadius: '0',
background: 'transparent',
outline: 'none',
marginBottom: '4px'
}} }}
/> />
{/* Location Input */} {/* Location */}
<input <input
type="text" type="text"
value={location} value={location}
onChange={e => setLocation(e.target.value)} onChange={e => setLocation(e.target.value)}
placeholder="Location or Video Call" placeholder="Location or Video Call"
style={{ style={{
width: '100%', width: '100%', padding: '5px 10px', border: 'none',
padding: '8px 16px',
border: 'none',
borderBottom: '1px solid var(--weekly-border)', borderBottom: '1px solid var(--weekly-border)',
borderRadius: '0', borderRadius: '0', background: 'transparent', outline: 'none', fontSize: '0.82rem',
background: 'transparent',
outline: 'none',
fontSize: '0.95rem'
}} }}
/> />
{/* Calendar row */} {/* Calendar row */}
<div style={{ ...rowStyle, padding: '4px 16px', position: 'relative' }}> <div style={{ ...rowStyle, position: 'relative' }}>
<span style={labelStyle}>Calendar</span> <span style={labelStyle}>Calendar</span>
<div ref={calendarSelectorRef} style={{ position: 'relative', flex: 1, display: 'flex', justifyContent: 'flex-end' }}> <div ref={calendarSelectorRef} style={{ position: 'relative', flex: 1, display: 'flex', justifyContent: 'flex-end' }}>
<button <button
type="button" type="button"
onClick={() => !event && setIsCalendarSelectorOpen(!isCalendarSelectorOpen)} onClick={() => !event && setIsCalendarSelectorOpen(!isCalendarSelectorOpen)}
disabled={!!event} disabled={!!event}
style={{ style={{
display: 'flex', alignItems: 'center', gap: '8px', display: 'flex', alignItems: 'center', gap: '6px',
background: 'transparent', border: 'none', fontWeight: 500, background: 'transparent', border: 'none', fontWeight: 500,
cursor: event ? 'default' : 'pointer', outline: 'none', cursor: event ? 'default' : 'pointer', outline: 'none',
color: 'var(--weekly-text)', fontSize: '0.95rem', color: 'var(--weekly-text)', fontSize: '0.82rem',
padding: '4px 0', maxWidth: '200px', justifyContent: 'flex-end' padding: '2px 0', maxWidth: '180px', justifyContent: 'flex-end'
}} }}
> >
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}> <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{selectedCal?.summary || selectedCal?.title || 'Select Calendar'} {selectedCal?.summary || selectedCal?.title || 'Select Calendar'}
</span> </span>
<div style={{ <div style={{
width: '12px', height: '12px', borderRadius: '50%', width: '10px', height: '10px', borderRadius: '50%', flexShrink: 0,
backgroundColor: selectedCal?.backgroundColor || selectedCal?.color || '#3b82f6' backgroundColor: selectedCal?.backgroundColor || selectedCal?.color || '#3b82f6'
}} /> }} />
{!event && (isCalendarSelectorOpen ? <ChevronUp size={14} /> : <ChevronDown size={14} />)} {!event && (isCalendarSelectorOpen ? <ChevronUp size={12} /> : <ChevronDown size={12} />)}
</button> </button>
{isCalendarSelectorOpen && ( {isCalendarSelectorOpen && (
<div style={{ <div style={{
position: 'absolute', top: '100%', right: 0, zIndex: 100, position: 'absolute', top: '100%', right: 0, zIndex: 100,
minWidth: '220px', backgroundColor: 'var(--weekly-bg-popover, #ffffff)', minWidth: '200px', backgroundColor: 'var(--weekly-bg-popover, #ffffff)',
borderRadius: '10px', boxShadow: '0 4px 15px rgba(0,0,0,0.1)', borderRadius: '8px', boxShadow: '0 4px 15px rgba(0,0,0,0.1)',
border: '1px solid var(--weekly-border)', marginTop: '5px', border: '1px solid var(--weekly-border)', marginTop: '2px',
padding: '6px', maxHeight: '250px', overflowY: 'auto' padding: '4px', maxHeight: '200px', overflowY: 'auto'
}}> }}>
{availableCalendars.map((cal: any) => ( {availableCalendars.map((cal: any) => (
<div <div
key={cal.id} key={cal.id}
onClick={() => { setCalendarId(cal.id); setIsCalendarSelectorOpen(false); }} onClick={() => { setCalendarId(cal.id); setIsCalendarSelectorOpen(false); }}
style={{ style={{
display: 'flex', alignItems: 'center', gap: '10px', display: 'flex', alignItems: 'center', gap: '8px',
padding: '8px 12px', borderRadius: '6px', cursor: 'pointer', padding: '6px 10px', borderRadius: '5px', cursor: 'pointer',
fontSize: '0.9rem', color: 'var(--weekly-text)', fontSize: '0.82rem', color: 'var(--weekly-text)',
backgroundColor: calendarId === cal.id ? 'var(--weekly-selection, rgba(59, 130, 246, 0.1))' : 'transparent', backgroundColor: calendarId === cal.id ? 'var(--weekly-selection, rgba(59, 130, 246, 0.1))' : 'transparent',
transition: 'background 0.2s', textAlign: 'left' transition: 'background 0.2s', textAlign: 'left'
}} }}
onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--weekly-hover, rgba(0,0,0,0.05))'} onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--weekly-hover, rgba(0,0,0,0.05))'}
onMouseLeave={(e) => e.currentTarget.style.backgroundColor = calendarId === cal.id ? 'var(--weekly-selection, rgba(59, 130, 246, 0.1))' : 'transparent'} onMouseLeave={(e) => e.currentTarget.style.backgroundColor = calendarId === cal.id ? 'var(--weekly-selection, rgba(59, 130, 246, 0.1))' : 'transparent'}
> >
<div style={{ width: '10px', height: '10px', borderRadius: '50%', backgroundColor: cal.backgroundColor || cal.color || '#3b82f6', flexShrink: 0 }} /> <div style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: cal.backgroundColor || cal.color || '#3b82f6', flexShrink: 0 }} />
<span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}> <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{cal.summary || cal.title} {cal.summary || cal.title}
</span> </span>
@ -395,50 +381,51 @@ export default function CalendarEventModal({
</div> </div>
</div> </div>
{/* All Day row */} {/* All Day */}
<div style={{ ...rowStyle, padding: '4px 16px' }}> <div style={rowStyle}>
<span style={labelStyle}>All Day</span> <span style={labelStyle}>All Day</span>
<input <input
type="checkbox" type="checkbox"
checked={allDay} checked={allDay}
onChange={e => setAllDay(e.target.checked)} onChange={e => setAllDay(e.target.checked)}
style={{ width: '18px', height: '18px', cursor: 'pointer' }} style={{ width: '16px', height: '16px', cursor: 'pointer' }}
/> />
</div> </div>
{/* Date/Time rows */} {/* Start / End on same row when not all-day */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', padding: '0 16px' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '2px', padding: '0 10px' }}>
<div style={rowStyle}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: '30px' }}>
<span style={labelStyle}>Starts</span> <span style={labelStyle}>Starts</span>
<input <input
type={allDay ? "date" : "datetime-local"} type={allDay ? "date" : "datetime-local"}
value={allDay ? startDate.toISOString().split('T')[0] : toLocalISOString(startDate)} value={allDay ? startDate.toISOString().split('T')[0] : toLocalISOString(startDate)}
onChange={e => handleStartDateChange(e.target.value)} onChange={e => handleStartDateChange(e.target.value)}
style={{ style={{
padding: '4px 8px', border: 'none', borderRadius: '6px', padding: '2px 6px', border: 'none', borderRadius: '4px',
background: 'var(--weekly-bg-secondary, #f3f4f6)', background: 'var(--weekly-bg-secondary, #f3f4f6)',
fontSize: '0.9rem', textAlign: 'center' fontSize: '0.8rem', textAlign: 'center',
}} }}
/> />
</div> </div>
<div style={rowStyle}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: '30px' }}>
<span style={labelStyle}>Ends</span> <span style={labelStyle}>Ends</span>
<input <input
type={allDay ? "date" : "datetime-local"} type={allDay ? "date" : "datetime-local"}
value={allDay ? endDate.toISOString().split('T')[0] : toLocalISOString(endDate)} value={allDay ? endDate.toISOString().split('T')[0] : toLocalISOString(endDate)}
onChange={e => setEndDate(new Date(e.target.value))} onChange={e => setEndDate(new Date(e.target.value))}
style={{ style={{
padding: '4px 8px', border: 'none', borderRadius: '6px', padding: '2px 6px', border: 'none', borderRadius: '4px',
background: 'var(--weekly-bg-secondary, #f3f4f6)', background: 'var(--weekly-bg-secondary, #f3f4f6)',
fontSize: '0.9rem', textAlign: 'center' fontSize: '0.8rem', textAlign: 'center',
}} }}
/> />
</div> </div>
</div> </div>
{/* Meta Fields Group */} {/* Divider */}
<div style={{ padding: '8px 16px', display: 'flex', flexDirection: 'column', gap: '12px', marginTop: '4px' }}> <div style={{ borderTop: '1px solid var(--weekly-border)', margin: '2px 10px' }} />
{/* Recurrence */}
{/* Repeat */}
<div style={rowStyle}> <div style={rowStyle}>
<span style={labelStyle}>Repeat</span> <span style={labelStyle}>Repeat</span>
<select value={recurrence} onChange={e => setRecurrence(e.target.value)} style={selectStyle}> <select value={recurrence} onChange={e => setRecurrence(e.target.value)} style={selectStyle}>
@ -450,11 +437,11 @@ export default function CalendarEventModal({
</select> </select>
</div> </div>
{/* Reminders */} {/* Alert */}
<div> <div style={{ padding: '0 10px' }}>
<div style={{ ...rowStyle, marginBottom: reminders.length > 0 ? '6px' : 0 }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: '32px' }}>
<span style={{ ...labelStyle, display: 'flex', alignItems: 'center', gap: '6px' }}> <span style={{ ...labelStyle, display: 'flex', alignItems: 'center', gap: '4px' }}>
<Bell size={14} /> Alert <Bell size={12} /> Alert
</span> </span>
{reminders.length === 0 && ( {reminders.length === 0 && (
<button onClick={addReminder} style={{ ...selectStyle, color: '#3b82f6', cursor: 'pointer', background: 'none', border: 'none' }}> <button onClick={addReminder} style={{ ...selectStyle, color: '#3b82f6', cursor: 'pointer', background: 'none', border: 'none' }}>
@ -463,7 +450,7 @@ export default function CalendarEventModal({
)} )}
</div> </div>
{reminders.map((reminder, idx) => ( {reminders.map((reminder, idx) => (
<div key={idx} style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '8px', marginBottom: '4px' }}> <div key={idx} style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '4px', marginBottom: '2px' }}>
<select <select
value={reminder.minutes} value={reminder.minutes}
onChange={e => updateReminder(idx, parseInt(e.target.value))} onChange={e => updateReminder(idx, parseInt(e.target.value))}
@ -474,20 +461,21 @@ export default function CalendarEventModal({
))} ))}
</select> </select>
<button onClick={() => setReminders(reminders.filter((_, i) => i !== idx))} <button onClick={() => setReminders(reminders.filter((_, i) => i !== idx))}
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: 'var(--weekly-text-light)' }}> style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '1px', color: 'var(--weekly-text-light)' }}>
<X size={14} /> <X size={12} />
</button> </button>
</div> </div>
))} ))}
{reminders.length > 0 && reminders.length < 2 && ( {reminders.length > 0 && reminders.length < 2 && (
<button onClick={addReminder} style={{ fontSize: '0.85rem', color: '#3b82f6', background: 'none', border: 'none', cursor: 'pointer', padding: '2px 0' }}> <button onClick={addReminder} style={{ fontSize: '0.78rem', color: '#3b82f6', background: 'none', border: 'none', cursor: 'pointer', padding: '1px 0' }}>
+ Add another alert + Add another alert
</button> </button>
)} )}
</div> </div>
{/* Busy Status */} {/* Status & Visibility side by side */}
<div style={rowStyle}> <div style={{ display: 'flex', gap: '8px', padding: '0 10px' }}>
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: '32px' }}>
<span style={labelStyle}>Status</span> <span style={labelStyle}>Status</span>
<select value={busyStatus} onChange={e => setBusyStatus(e.target.value)} style={selectStyle}> <select value={busyStatus} onChange={e => setBusyStatus(e.target.value)} style={selectStyle}>
{BUSY_STATUS_OPTIONS.map(opt => ( {BUSY_STATUS_OPTIONS.map(opt => (
@ -495,9 +483,8 @@ export default function CalendarEventModal({
))} ))}
</select> </select>
</div> </div>
<div style={{ width: '1px', background: 'var(--weekly-border)', margin: '4px 0' }} />
{/* Visibility */} <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: '32px' }}>
<div style={rowStyle}>
<span style={labelStyle}>Visibility</span> <span style={labelStyle}>Visibility</span>
<select value={visibility} onChange={e => setVisibility(e.target.value)} style={selectStyle}> <select value={visibility} onChange={e => setVisibility(e.target.value)} style={selectStyle}>
{VISIBILITY_OPTIONS.map(opt => ( {VISIBILITY_OPTIONS.map(opt => (
@ -505,6 +492,7 @@ export default function CalendarEventModal({
))} ))}
</select> </select>
</div> </div>
</div>
{/* URL (Conditional) */} {/* URL (Conditional) */}
{supportsURL && ( {supportsURL && (
@ -514,50 +502,50 @@ export default function CalendarEventModal({
onChange={e => setUrl(e.target.value)} onChange={e => setUrl(e.target.value)}
placeholder="URL" placeholder="URL"
style={{ style={{
width: '100%', padding: '6px 0', border: 'none', width: '100%', padding: '4px 10px', border: 'none',
borderBottom: '1px solid var(--weekly-border)', borderBottom: '1px solid var(--weekly-border)',
background: 'transparent', fontSize: '0.9rem', outline: 'none' background: 'transparent', fontSize: '0.82rem', outline: 'none'
}} }}
/> />
)} )}
{/* Expandable section: Attendees & Attachments */} {/* Expandable: Invitees & Attachments */}
<button <button
onClick={() => setShowMoreOptions(!showMoreOptions)} onClick={() => setShowMoreOptions(!showMoreOptions)}
style={{ style={{
display: 'flex', alignItems: 'center', gap: '6px', display: 'flex', alignItems: 'center', gap: '4px',
background: 'none', border: 'none', cursor: 'pointer', background: 'none', border: 'none', cursor: 'pointer',
color: '#3b82f6', fontSize: '0.9rem', padding: '4px 0', color: '#3b82f6', fontSize: '0.8rem', padding: '2px 10px',
}} }}
> >
{showMoreOptions ? <ChevronUp size={14} /> : <ChevronDown size={14} />} {showMoreOptions ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
Invitees & Attachments Invitees & Attachments
</button> </button>
{showMoreOptions && ( {showMoreOptions && (
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', padding: '0 10px' }}>
{/* Attendees */} {/* Attendees */}
<div> <div>
<span style={{ ...labelStyle, display: 'flex', alignItems: 'center', gap: '6px', marginBottom: '6px' }}> <span style={{ ...labelStyle, display: 'flex', alignItems: 'center', gap: '4px', marginBottom: '4px' }}>
<Users size={14} /> Invitees <Users size={12} /> Invitees
</span> </span>
{attendees.map((att, idx) => ( {attendees.map((att, idx) => (
<div key={idx} style={{ <div key={idx} style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '4px 8px', marginBottom: '4px', padding: '3px 6px', marginBottom: '2px',
background: 'var(--weekly-bg-secondary, #f3f4f6)', borderRadius: '6px', background: 'var(--weekly-bg-secondary, #f3f4f6)', borderRadius: '4px',
fontSize: '0.85rem', fontSize: '0.78rem',
}}> }}>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}> <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{att.displayName ? `${att.displayName} (${att.email})` : att.email} {att.displayName ? `${att.displayName} (${att.email})` : att.email}
</span> </span>
<button onClick={() => removeAttendee(att.email)} <button onClick={() => removeAttendee(att.email)}
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: 'var(--weekly-text-light)', flexShrink: 0 }}> style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '1px', color: 'var(--weekly-text-light)', flexShrink: 0 }}>
<X size={14} /> <X size={12} />
</button> </button>
</div> </div>
))} ))}
<div style={{ display: 'flex', gap: '6px' }}> <div style={{ display: 'flex', gap: '4px' }}>
<input <input
type="email" type="email"
value={newAttendeeEmail} value={newAttendeeEmail}
@ -565,43 +553,43 @@ export default function CalendarEventModal({
placeholder="Add email address" placeholder="Add email address"
onKeyDown={e => e.key === 'Enter' && addAttendee()} onKeyDown={e => e.key === 'Enter' && addAttendee()}
style={{ style={{
flex: 1, padding: '6px 8px', border: 'none', flex: 1, padding: '4px 6px', border: 'none',
borderBottom: '1px solid var(--weekly-border)', borderBottom: '1px solid var(--weekly-border)',
background: 'transparent', fontSize: '0.85rem', outline: 'none', background: 'transparent', fontSize: '0.78rem', outline: 'none',
}} }}
/> />
<button onClick={addAttendee} style={{ <button onClick={addAttendee} style={{
background: 'none', border: 'none', cursor: 'pointer', background: 'none', border: 'none', cursor: 'pointer',
color: '#3b82f6', padding: '4px', color: '#3b82f6', padding: '2px',
}}> }}>
<Plus size={16} /> <Plus size={14} />
</button> </button>
</div> </div>
</div> </div>
{/* Attachments (URL-based, Apple/Synology only) */} {/* Attachments (Apple/Synology only) */}
{supportsAttachments && ( {supportsAttachments && (
<div> <div>
<span style={{ ...labelStyle, display: 'flex', alignItems: 'center', gap: '6px', marginBottom: '6px' }}> <span style={{ ...labelStyle, display: 'flex', alignItems: 'center', gap: '4px', marginBottom: '4px' }}>
<Paperclip size={14} /> Attachments <Paperclip size={12} /> Attachments
</span> </span>
{attachments.map((att, idx) => ( {attachments.map((att, idx) => (
<div key={idx} style={{ <div key={idx} style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '4px 8px', marginBottom: '4px', padding: '3px 6px', marginBottom: '2px',
background: 'var(--weekly-bg-secondary, #f3f4f6)', borderRadius: '6px', background: 'var(--weekly-bg-secondary, #f3f4f6)', borderRadius: '4px',
fontSize: '0.85rem', fontSize: '0.78rem',
}}> }}>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}> <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{att.title || att.url} {att.title || att.url}
</span> </span>
<button onClick={() => removeAttachment(att.url)} <button onClick={() => removeAttachment(att.url)}
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: 'var(--weekly-text-light)', flexShrink: 0 }}> style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '1px', color: 'var(--weekly-text-light)', flexShrink: 0 }}>
<X size={14} /> <X size={12} />
</button> </button>
</div> </div>
))} ))}
<div style={{ display: 'flex', gap: '6px' }}> <div style={{ display: 'flex', gap: '4px' }}>
<input <input
type="url" type="url"
value={newAttachmentUrl} value={newAttachmentUrl}
@ -609,16 +597,16 @@ export default function CalendarEventModal({
placeholder="Add attachment URL" placeholder="Add attachment URL"
onKeyDown={e => e.key === 'Enter' && addAttachment()} onKeyDown={e => e.key === 'Enter' && addAttachment()}
style={{ style={{
flex: 1, padding: '6px 8px', border: 'none', flex: 1, padding: '4px 6px', border: 'none',
borderBottom: '1px solid var(--weekly-border)', borderBottom: '1px solid var(--weekly-border)',
background: 'transparent', fontSize: '0.85rem', outline: 'none', background: 'transparent', fontSize: '0.78rem', outline: 'none',
}} }}
/> />
<button onClick={addAttachment} style={{ <button onClick={addAttachment} style={{
background: 'none', border: 'none', cursor: 'pointer', background: 'none', border: 'none', cursor: 'pointer',
color: '#3b82f6', padding: '4px', color: '#3b82f6', padding: '2px',
}}> }}>
<Plus size={16} /> <Plus size={14} />
</button> </button>
</div> </div>
</div> </div>
@ -627,37 +615,37 @@ export default function CalendarEventModal({
)} )}
{/* Notes */} {/* Notes */}
<div style={{ marginTop: '4px' }}> <div style={{ padding: '0 10px' }}>
<Suspense fallback={ <Suspense fallback={
<textarea <textarea
value={description} value={description}
onChange={e => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
placeholder="Notes" placeholder="Notes"
rows={3} rows={2}
style={{ width: '100%', padding: '8px 0', border: 'none', borderBottom: '1px solid var(--weekly-border)', background: 'transparent', resize: 'none', fontSize: '0.9rem', outline: 'none' }} style={{ width: '100%', padding: '4px 0', border: 'none', borderBottom: '1px solid var(--weekly-border)', background: 'transparent', resize: 'none', fontSize: '0.82rem', outline: 'none' }}
/> />
}> }>
<RichTextEditor <RichTextEditor
value={description} value={description}
onChange={setDescription} onChange={setDescription}
placeholder="Notes" placeholder="Notes"
minHeight="80px" minHeight="60px"
/> />
</Suspense> </Suspense>
</div> </div>
</div> </div>
</div>
<div className="weekly-modal-actions" style={{ marginTop: '20px', display: 'flex', justifyContent: 'space-between', borderTop: '1px solid var(--weekly-border)', paddingTop: '15px' }}> {/* Actions */}
<div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}> <div style={{ marginTop: '10px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderTop: '1px solid var(--weekly-border)', paddingTop: '10px' }}>
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
{event && onDelete && ( {event && onDelete && (
<button <button
onClick={handleDelete} onClick={handleDelete}
disabled={isSaving || isDeleting} disabled={isSaving || isDeleting}
style={{ style={{
padding: '8px 12px', background: 'none', color: '#ef4444', padding: '6px 10px', background: 'none', color: '#ef4444',
border: 'none', borderRadius: '6px', fontSize: '0.9rem', border: 'none', borderRadius: '6px', fontSize: '0.82rem',
fontWeight: 500, cursor: 'pointer', transition: 'all 0.2s', fontWeight: 500, cursor: 'pointer',
opacity: isSaving || isDeleting ? 0.5 : 1 opacity: isSaving || isDeleting ? 0.5 : 1
}} }}
> >
@ -665,15 +653,15 @@ export default function CalendarEventModal({
</button> </button>
)} )}
{!event && ( {!event && (
<button className="weekly-btn-ghost" onClick={onClose} style={{ padding: '8px 12px', fontSize: '0.9rem', color: 'var(--weekly-text-light)', border: 'none', background: 'none', cursor: 'pointer' }}> <button onClick={onClose} style={{ padding: '6px 10px', fontSize: '0.82rem', color: 'var(--weekly-text-light)', border: 'none', background: 'none', cursor: 'pointer' }}>
Cancel Cancel
</button> </button>
)} )}
</div> </div>
<div style={{ display: 'flex', gap: '8px' }}> <div style={{ display: 'flex', gap: '6px' }}>
{event && ( {event && (
<button className="weekly-btn-ghost" onClick={onClose} style={{ padding: '8px 12px', fontSize: '0.9rem', color: 'var(--weekly-text-light)', border: 'none', background: 'none', cursor: 'pointer' }}> <button onClick={onClose} style={{ padding: '6px 10px', fontSize: '0.82rem', color: 'var(--weekly-text-light)', border: 'none', background: 'none', cursor: 'pointer' }}>
Cancel Cancel
</button> </button>
)} )}
@ -682,7 +670,7 @@ export default function CalendarEventModal({
onClick={handleSubmit} onClick={handleSubmit}
disabled={isSaving || isDeleting} disabled={isSaving || isDeleting}
style={{ style={{
padding: '8px 20px', borderRadius: '8px', fontSize: '0.95rem', padding: '6px 16px', borderRadius: '6px', fontSize: '0.85rem',
fontWeight: 600, backgroundColor: '#3b82f6', color: 'white', fontWeight: 600, backgroundColor: '#3b82f6', color: 'white',
border: 'none', cursor: 'pointer', border: 'none', cursor: 'pointer',
opacity: isSaving || isDeleting ? 0.7 : 1 opacity: isSaving || isDeleting ? 0.7 : 1