fix: redesign recurring delete as overlay with loading indicator
Replace inline delete buttons (Dieses/Künftige/Alle) with a proper overlay dialog matching the recurring edit overlay style. Shows animated dots on the active delete option while processing. Other options dim during deletion. v1.75.7 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
de3b4a103e
commit
51301bc497
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-weekly-todo-list",
|
||||
"version": "1.75.6",
|
||||
"version": "1.75.7",
|
||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -161,6 +161,7 @@ export default function CalendarEventModal({
|
||||
const [allDay, setAllDay] = useState(!!event?.allDay);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [deletingMode, setDeletingMode] = useState<string | null>(null);
|
||||
const [error, setError] = useState('');
|
||||
const [isCalendarSelectorOpen, setIsCalendarSelectorOpen] = useState(false);
|
||||
const calendarSelectorRef = useRef<HTMLDivElement>(null);
|
||||
@ -269,12 +270,14 @@ export default function CalendarEventModal({
|
||||
}
|
||||
|
||||
setIsDeleting(true);
|
||||
setDeletingMode(mode || 'all');
|
||||
try {
|
||||
await onDelete(event.id, event.calendarId, mode || 'all');
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to delete event');
|
||||
setIsDeleting(false);
|
||||
setDeletingMode(null);
|
||||
setIsDeleteConfirming(false);
|
||||
setShowRecurringDeleteOptions(false);
|
||||
}
|
||||
@ -874,34 +877,21 @@ export default function CalendarEventModal({
|
||||
{isDeleting ? '...' : isDeleteConfirming ? (language === 'de' ? 'Bestätigen?' : 'Confirm?') : (language === 'de' ? 'Löschen' : 'Delete')}
|
||||
</button>
|
||||
)}
|
||||
{event && onDelete && showRecurringDeleteOptions && (
|
||||
<div style={{ display: 'flex', gap: '3px', flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
{[
|
||||
{ mode: 'this', label: language === 'de' ? 'Dieses' : 'This' },
|
||||
{ mode: 'future', label: language === 'de' ? 'Künftige' : 'Future' },
|
||||
{ mode: 'all', label: language === 'de' ? 'Alle' : 'All' },
|
||||
].map(({ mode, label }) => (
|
||||
<button
|
||||
key={mode}
|
||||
onClick={() => handleDelete(mode)}
|
||||
disabled={isDeleting}
|
||||
style={{
|
||||
padding: '3px 7px', fontSize: '0.7rem', fontWeight: 500,
|
||||
color: '#ef4444', background: 'rgba(239,68,68,0.08)',
|
||||
border: '1px solid rgba(239,68,68,0.2)', borderRadius: '5px',
|
||||
cursor: 'pointer', opacity: isDeleting ? 0.5 : 1,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
{event && onDelete && showRecurringDeleteOptions && !isDeleting && (
|
||||
<button
|
||||
onClick={() => setShowRecurringDeleteOptions(false)}
|
||||
style={{ padding: '2px', color: 'var(--weekly-text-light)', background: 'none', border: 'none', cursor: 'pointer' }}
|
||||
style={{
|
||||
padding: '5px 10px', background: 'none', color: 'var(--weekly-text-light)',
|
||||
border: 'none', fontSize: '0.8rem', cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<X size={12} />
|
||||
{language === 'de' ? 'Abbrechen' : 'Cancel'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{event && onDelete && isDeleting && (
|
||||
<span style={{ fontSize: '0.8rem', color: '#ef4444', fontWeight: 500, padding: '5px 10px' }}>
|
||||
{language === 'de' ? 'Lösche' : 'Deleting'}<AnimatedDots />
|
||||
</span>
|
||||
)}
|
||||
{!event && (
|
||||
<button onClick={onClose} style={{ padding: '5px 10px', fontSize: '0.8rem', color: 'var(--weekly-text-light)', border: 'none', background: 'none', cursor: 'pointer' }}>
|
||||
@ -1000,6 +990,63 @@ export default function CalendarEventModal({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Recurring delete mode overlay */}
|
||||
{showRecurringDeleteOptions && (
|
||||
<div style={{
|
||||
position: 'fixed', inset: 0, zIndex: 3000,
|
||||
display: 'flex', justifyContent: 'center', alignItems: 'center',
|
||||
background: 'rgba(0,0,0,0.35)', backdropFilter: 'blur(2px)',
|
||||
}} onClick={() => !isDeleting && setShowRecurringDeleteOptions(false)}>
|
||||
<div style={{
|
||||
background: 'var(--weekly-bg, white)', color: 'var(--weekly-text, #333)',
|
||||
borderRadius: 12, width: '90%', maxWidth: 340,
|
||||
boxShadow: '0 12px 40px rgba(0,0,0,0.25)',
|
||||
overflow: 'hidden',
|
||||
}} onClick={e => e.stopPropagation()}>
|
||||
<div style={{
|
||||
padding: '14px 16px 10px', fontWeight: 700, fontSize: '0.9rem',
|
||||
borderBottom: '2px solid #ef4444',
|
||||
}}>
|
||||
{language === 'de' ? 'Wiederkehrendes Ereignis löschen' : 'Delete recurring event'}
|
||||
{!isDeleting && <button onClick={() => setShowRecurringDeleteOptions(false)} style={{
|
||||
float: 'right', background: 'none', border: 'none', cursor: 'pointer',
|
||||
color: 'var(--weekly-text-light)', padding: 0,
|
||||
}}><X size={16} /></button>}
|
||||
</div>
|
||||
<div style={{ padding: '4px 0' }}>
|
||||
{([
|
||||
{ value: 'this' as const, title: language === 'de' ? 'Nur dieses Ereignis' : 'This event only', desc: language === 'de' ? 'Alle anderen bleiben erhalten.' : 'All other events stay the same.' },
|
||||
{ value: 'future' as const, title: language === 'de' ? 'Dieses und folgende' : 'This and following', desc: language === 'de' ? 'Dieses und alle zukünftigen werden gelöscht.' : 'This and all future events will be deleted.' },
|
||||
{ value: 'all' as const, title: language === 'de' ? 'Alle Ereignisse' : 'All events', desc: language === 'de' ? 'Die gesamte Serie wird gelöscht.' : 'The entire series will be deleted.' },
|
||||
]).map(opt => (
|
||||
<button
|
||||
key={opt.value}
|
||||
onClick={() => handleDelete(opt.value)}
|
||||
disabled={isDeleting}
|
||||
style={{
|
||||
display: 'flex', gap: 10, padding: '10px 16px', cursor: 'pointer',
|
||||
width: '100%', textAlign: 'left', background: 'none',
|
||||
border: 'none', borderBottom: '1px solid var(--weekly-border, #eee)',
|
||||
color: 'var(--weekly-text, #333)',
|
||||
opacity: isDeleting && deletingMode !== opt.value ? 0.4 : 1,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontWeight: 600, fontSize: '0.82rem', color: '#ef4444' }}>{opt.title}</div>
|
||||
<div style={{ fontSize: '0.72rem', opacity: 0.5, marginTop: 1 }}>{opt.desc}</div>
|
||||
</div>
|
||||
{isDeleting && deletingMode === opt.value && (
|
||||
<span style={{ fontSize: '0.8rem', color: '#ef4444', fontWeight: 600 }}>
|
||||
<AnimatedDots />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user