feat: add calendar event deletion with confirmation
All checks were successful
Deploy / deploy (push) Successful in 4m37s
All checks were successful
Deploy / deploy (push) Successful in 4m37s
This commit is contained in:
@@ -145,3 +145,31 @@ export async function POST(req: Request) {
|
|||||||
return NextResponse.json({ error: err.message || 'Failed to create event' }, { status: 500 })
|
return NextResponse.json({ error: err.message || 'Failed to create event' }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function DELETE(req: Request) {
|
||||||
|
const { searchParams } = new URL(req.url)
|
||||||
|
const eventId = searchParams.get('eventId')
|
||||||
|
const calendarId = searchParams.get('calendarId')
|
||||||
|
|
||||||
|
if (!eventId) {
|
||||||
|
return NextResponse.json({ error: 'eventId is required' }, { status: 400 })
|
||||||
|
}
|
||||||
|
|
||||||
|
const auth = getAuth(false)
|
||||||
|
if (!auth) {
|
||||||
|
return NextResponse.json({ error: 'not_configured' }, { status: 500 })
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetCalendarId = calendarId || process.env.DANIIL_CALENDAR_ID || 'daniilklimov25@gmail.com'
|
||||||
|
const calendarClient = google.calendar({ version: 'v3', auth: auth as any })
|
||||||
|
|
||||||
|
try {
|
||||||
|
await calendarClient.events.delete({
|
||||||
|
calendarId: targetCalendarId,
|
||||||
|
eventId,
|
||||||
|
})
|
||||||
|
return NextResponse.json({ success: true })
|
||||||
|
} catch (err: any) {
|
||||||
|
return NextResponse.json({ error: err.message || 'Failed to delete event' }, { status: 500 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { ChevronLeft, ChevronRight, Plus, X, Clock, MapPin } from 'lucide-react'
|
import { ChevronLeft, ChevronRight, Plus, X, Clock, MapPin, Trash2 } from 'lucide-react'
|
||||||
|
|
||||||
interface CalendarEvent {
|
interface CalendarEvent {
|
||||||
id: string
|
id: string
|
||||||
@@ -102,6 +102,8 @@ export default function CalendarTab() {
|
|||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null)
|
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null)
|
||||||
const [showAddModal, setShowAddModal] = useState(false)
|
const [showAddModal, setShowAddModal] = useState(false)
|
||||||
|
const [deleting, setDeleting] = useState(false)
|
||||||
|
const [confirmDelete, setConfirmDelete] = useState(false)
|
||||||
const [addDate, setAddDate] = useState<string>('')
|
const [addDate, setAddDate] = useState<string>('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -112,6 +114,23 @@ export default function CalendarTab() {
|
|||||||
.catch(() => setLoading(false))
|
.catch(() => setLoading(false))
|
||||||
}, [year, month])
|
}, [year, month])
|
||||||
|
|
||||||
|
|
||||||
|
const deleteEvent = async (event: CalendarEvent) => {
|
||||||
|
setDeleting(true)
|
||||||
|
try {
|
||||||
|
const r = await fetch(`/api/calendar?eventId=${event.id}`, { method: 'DELETE' })
|
||||||
|
const d = await r.json()
|
||||||
|
if (d.error) throw new Error(d.error)
|
||||||
|
setEvents(prev => prev.filter(e => e.id !== event.id))
|
||||||
|
setSelectedEvent(null)
|
||||||
|
setConfirmDelete(false)
|
||||||
|
} catch (e: any) {
|
||||||
|
alert(e.message || 'Ошибка удаления')
|
||||||
|
} finally {
|
||||||
|
setDeleting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Upcoming events (next 30 days)
|
// Upcoming events (next 30 days)
|
||||||
const upcoming = events
|
const upcoming = events
|
||||||
.filter(e => new Date(e.start) >= new Date())
|
.filter(e => new Date(e.start) >= new Date())
|
||||||
@@ -273,7 +292,7 @@ export default function CalendarTab() {
|
|||||||
|
|
||||||
{/* Event detail modal */}
|
{/* Event detail modal */}
|
||||||
{selectedEvent && (
|
{selectedEvent && (
|
||||||
<div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)', zIndex: 100, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }} onClick={() => setSelectedEvent(null)}>
|
<div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)', zIndex: 100, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }} onClick={() => { setSelectedEvent(null); setConfirmDelete(false) }}>
|
||||||
<div style={{ background: 'var(--bg)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 20, padding: 24, maxWidth: 360, width: '100%', boxShadow: '0 20px 60px rgba(0,0,0,0.5)' }} onClick={e => e.stopPropagation()}>
|
<div style={{ background: 'var(--bg)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 20, padding: 24, maxWidth: 360, width: '100%', boxShadow: '0 20px 60px rgba(0,0,0,0.5)' }} onClick={e => e.stopPropagation()}>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 }}>
|
||||||
<div style={{ width: 4, height: 40, borderRadius: 2, background: selectedEvent.color, marginRight: 12, flexShrink: 0, marginTop: 2 }} />
|
<div style={{ width: 4, height: 40, borderRadius: 2, background: selectedEvent.color, marginRight: 12, flexShrink: 0, marginTop: 2 }} />
|
||||||
@@ -299,6 +318,34 @@ export default function CalendarTab() {
|
|||||||
{selectedEvent.description && (
|
{selectedEvent.description && (
|
||||||
<div style={{ fontSize: 13, color: 'var(--text-secondary)', marginTop: 4, lineHeight: 1.5 }}>{selectedEvent.description}</div>
|
<div style={{ fontSize: 13, color: 'var(--text-secondary)', marginTop: 4, lineHeight: 1.5 }}>{selectedEvent.description}</div>
|
||||||
)}
|
)}
|
||||||
|
{/* Delete button */}
|
||||||
|
<div style={{ marginTop: 8, borderTop: '1px solid rgba(255,255,255,0.08)', paddingTop: 12 }}>
|
||||||
|
{!confirmDelete ? (
|
||||||
|
<button
|
||||||
|
onClick={() => setConfirmDelete(true)}
|
||||||
|
style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '8px 14px', borderRadius: 10, background: 'rgba(239,68,68,0.1)', border: '1px solid rgba(239,68,68,0.3)', color: '#f87171', fontSize: 13, fontWeight: 600, cursor: 'pointer', touchAction: 'manipulation', width: '100%', justifyContent: 'center' }}
|
||||||
|
>
|
||||||
|
<Trash2 size={14} />
|
||||||
|
Удалить событие
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div style={{ display: 'flex', gap: 8 }}>
|
||||||
|
<button
|
||||||
|
onClick={() => deleteEvent(selectedEvent)}
|
||||||
|
disabled={deleting}
|
||||||
|
style={{ flex: 1, padding: '8px 14px', borderRadius: 10, background: deleting ? 'rgba(239,68,68,0.15)' : 'rgba(239,68,68,0.25)', border: '1px solid rgba(239,68,68,0.5)', color: '#f87171', fontSize: 13, fontWeight: 600, cursor: deleting ? 'default' : 'pointer', touchAction: 'manipulation' }}
|
||||||
|
>
|
||||||
|
{deleting ? 'Удаление...' : 'Да, удалить'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setConfirmDelete(false)}
|
||||||
|
style={{ flex: 1, padding: '8px 14px', borderRadius: 10, background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)', color: 'var(--text-secondary)', fontSize: 13, fontWeight: 600, cursor: 'pointer', touchAction: 'manipulation' }}
|
||||||
|
>
|
||||||
|
Отмена
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user