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:
parent
b9c05571be
commit
d991e7a65b
@ -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": {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user