feat: add PIN lock screen auth + calendar owner filter toggles
All checks were successful
Deploy / deploy (push) Successful in 2m49s
All checks were successful
Deploy / deploy (push) Successful in 2m49s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { ChevronLeft, ChevronRight, Plus, X, Clock, MapPin, Trash2 } from 'lucide-react'
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import { ChevronLeft, ChevronRight, Plus, X, Clock, MapPin, Trash2, Eye, EyeOff } from 'lucide-react'
|
||||
|
||||
interface CalendarEvent {
|
||||
id: string
|
||||
@@ -28,31 +28,21 @@ function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string;
|
||||
const [error, setError] = useState('')
|
||||
|
||||
const inputStyle = {
|
||||
padding: '12px 16px',
|
||||
borderRadius: 14,
|
||||
background: 'rgba(255,255,255,0.05)',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
color: 'var(--text-primary)',
|
||||
fontSize: 14,
|
||||
outline: 'none',
|
||||
fontFamily: 'inherit',
|
||||
transition: 'border-color 0.2s ease',
|
||||
padding: '12px 16px', borderRadius: 14,
|
||||
background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.08)',
|
||||
color: 'var(--text-primary)', fontSize: 14, outline: 'none', fontFamily: 'inherit',
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
if (!title.trim()) { setError('Введите название'); return }
|
||||
setSaving(true)
|
||||
setError('')
|
||||
setSaving(true); setError('')
|
||||
try {
|
||||
const body = { title: title.trim(), date, startTime: allDay ? null : startTime, endTime: allDay ? null : endTime, allDay }
|
||||
const r = await fetch('/api/calendar', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
||||
const d = await r.json()
|
||||
if (d.error) throw new Error(d.error)
|
||||
onSaved(d.event)
|
||||
} catch (e: any) {
|
||||
setError(e.message || 'Ошибка сохранения')
|
||||
setSaving(false)
|
||||
}
|
||||
} catch (e: any) { setError(e.message || 'Ошибка сохранения'); setSaving(false) }
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -62,7 +52,6 @@ function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string;
|
||||
<span style={{ fontSize: 18, fontWeight: 700, color: 'var(--text-primary)' }}>Новое событие</span>
|
||||
<button onClick={onClose} style={{ color: 'var(--text-secondary)', padding: 4 }}><X size={18} /></button>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<input value={title} onChange={e => setTitle(e.target.value)} placeholder="Название события" autoFocus style={inputStyle} />
|
||||
<input type="date" value={date} onChange={e => setDate(e.target.value)} style={inputStyle} />
|
||||
@@ -73,25 +62,15 @@ function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string;
|
||||
</div>
|
||||
)}
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: 8, color: 'var(--text-secondary)', fontSize: 13, cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={allDay} onChange={e => setAllDay(e.target.checked)} />
|
||||
Весь день
|
||||
<input type="checkbox" checked={allDay} onChange={e => setAllDay(e.target.checked)} /> Весь день
|
||||
</label>
|
||||
{error && <div style={{ color: '#f87171', fontSize: 13 }}>{error}</div>}
|
||||
<button
|
||||
onClick={save}
|
||||
disabled={saving}
|
||||
style={{
|
||||
padding: '13px',
|
||||
borderRadius: 14,
|
||||
background: saving ? 'rgba(99,102,241,0.2)' : 'linear-gradient(135deg, rgba(99,102,241,0.4), rgba(139,92,246,0.3))',
|
||||
border: '1px solid rgba(129,140,248,0.3)',
|
||||
color: '#a5b4fc',
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
cursor: saving ? 'default' : 'pointer',
|
||||
transition: 'all 0.25s ease',
|
||||
}}
|
||||
>
|
||||
<button onClick={save} disabled={saving} style={{
|
||||
padding: '13px', borderRadius: 14,
|
||||
background: saving ? 'rgba(99,102,241,0.2)' : 'linear-gradient(135deg, rgba(99,102,241,0.4), rgba(139,92,246,0.3))',
|
||||
border: '1px solid rgba(129,140,248,0.3)', color: '#a5b4fc', fontSize: 14, fontWeight: 600,
|
||||
cursor: saving ? 'default' : 'pointer',
|
||||
}}>
|
||||
{saving ? 'Сохранение...' : 'Создать событие'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -110,6 +89,7 @@ export default function CalendarTab() {
|
||||
const [addDate, setAddDate] = useState<string>('')
|
||||
const [deleting, setDeleting] = useState(false)
|
||||
const [confirmDelete, setConfirmDelete] = useState(false)
|
||||
const [hiddenOwners, setHiddenOwners] = useState<Set<string>>(new Set())
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
@@ -128,14 +108,36 @@ export default function CalendarTab() {
|
||||
setEvents(prev => prev.filter(e => e.id !== event.id))
|
||||
setSelectedEvent(null)
|
||||
setConfirmDelete(false)
|
||||
} catch (e: any) {
|
||||
alert(e.message || 'Ошибка удаления')
|
||||
} finally {
|
||||
setDeleting(false)
|
||||
}
|
||||
} catch (e: any) { alert(e.message || 'Ошибка удаления') }
|
||||
finally { setDeleting(false) }
|
||||
}
|
||||
|
||||
const upcoming = events
|
||||
// Discover unique calendar owners from loaded events
|
||||
const calendarOwners = useMemo(() => {
|
||||
const map = new Map<string, { owner: string; ownerName: string; color: string; count: number }>()
|
||||
events.forEach(e => {
|
||||
const existing = map.get(e.owner)
|
||||
if (existing) { existing.count++ }
|
||||
else { map.set(e.owner, { owner: e.owner, ownerName: e.ownerName, color: e.color, count: 1 }) }
|
||||
})
|
||||
return Array.from(map.values())
|
||||
}, [events])
|
||||
|
||||
const toggleOwner = (owner: string) => {
|
||||
setHiddenOwners(prev => {
|
||||
const next = new Set(prev)
|
||||
if (next.has(owner)) next.delete(owner)
|
||||
else next.add(owner)
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
const filteredEvents = useMemo(() => {
|
||||
if (hiddenOwners.size === 0) return events
|
||||
return events.filter(e => !hiddenOwners.has(e.owner))
|
||||
}, [events, hiddenOwners])
|
||||
|
||||
const upcoming = filteredEvents
|
||||
.filter(e => new Date(e.start) >= new Date())
|
||||
.slice(0, 6)
|
||||
|
||||
@@ -150,7 +152,7 @@ export default function CalendarTab() {
|
||||
}
|
||||
|
||||
const getEventsForDay = (day: number) => {
|
||||
return events.filter(e => {
|
||||
return filteredEvents.filter(e => {
|
||||
const d = new Date(e.start)
|
||||
return d.getFullYear() === year && d.getMonth() === month && d.getDate() === day
|
||||
})
|
||||
@@ -158,7 +160,6 @@ export default function CalendarTab() {
|
||||
|
||||
const prevMonth = () => { if (month === 0) { setMonth(11); setYear(y => y - 1) } else setMonth(m => m - 1) }
|
||||
const nextMonth = () => { if (month === 11) { setMonth(0); setYear(y => y + 1) } else setMonth(m => m + 1) }
|
||||
|
||||
const today = new Date()
|
||||
|
||||
return (
|
||||
@@ -166,46 +167,64 @@ export default function CalendarTab() {
|
||||
{/* Main calendar grid */}
|
||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
|
||||
{/* Header */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 18 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<button onClick={prevMonth} style={{
|
||||
width: 36, height: 36, borderRadius: 12,
|
||||
background: 'rgba(255,255,255,0.04)',
|
||||
border: '1px solid rgba(255,255,255,0.06)',
|
||||
color: 'var(--text-primary)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
transition: 'all 0.2s ease',
|
||||
}}>
|
||||
<button onClick={prevMonth} style={{ width: 36, height: 36, borderRadius: 12, background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)', color: 'var(--text-primary)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<ChevronLeft size={16} />
|
||||
</button>
|
||||
<span style={{ fontSize: 20, fontWeight: 700, color: 'var(--text-primary)', minWidth: 180, textAlign: 'center' }}>
|
||||
{MONTHS[month]} {year}
|
||||
</span>
|
||||
<button onClick={nextMonth} style={{
|
||||
width: 36, height: 36, borderRadius: 12,
|
||||
background: 'rgba(255,255,255,0.04)',
|
||||
border: '1px solid rgba(255,255,255,0.06)',
|
||||
color: 'var(--text-primary)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
transition: 'all 0.2s ease',
|
||||
}}>
|
||||
<button onClick={nextMonth} style={{ width: 36, height: 36, borderRadius: 12, background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)', color: 'var(--text-primary)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<ChevronRight size={16} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => { setAddDate(today.toISOString().split('T')[0]); setShowAddModal(true) }}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '10px 18px', borderRadius: 14,
|
||||
background: 'linear-gradient(135deg, rgba(99,102,241,0.2), rgba(139,92,246,0.15))',
|
||||
border: '1px solid rgba(129,140,248,0.25)',
|
||||
color: '#a5b4fc', fontSize: 13, fontWeight: 600,
|
||||
transition: 'all 0.25s ease',
|
||||
}}
|
||||
>
|
||||
<Plus size={15} />
|
||||
Событие
|
||||
</button>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
{/* Calendar owner filters */}
|
||||
{calendarOwners.map(cal => {
|
||||
const isHidden = hiddenOwners.has(cal.owner)
|
||||
return (
|
||||
<button
|
||||
key={cal.owner}
|
||||
onClick={() => toggleOwner(cal.owner)}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '7px 14px', borderRadius: 12,
|
||||
background: isHidden ? 'rgba(255,255,255,0.02)' : `${cal.color}15`,
|
||||
border: `1px solid ${isHidden ? 'rgba(255,255,255,0.06)' : cal.color + '30'}`,
|
||||
color: isHidden ? 'var(--text-tertiary)' : cal.color,
|
||||
fontSize: 12, fontWeight: 600,
|
||||
transition: 'all 0.25s ease',
|
||||
opacity: isHidden ? 0.5 : 1,
|
||||
}}
|
||||
>
|
||||
{isHidden ? <EyeOff size={13} /> : <Eye size={13} />}
|
||||
{cal.ownerName}
|
||||
<span style={{
|
||||
fontSize: 10, padding: '1px 6px', borderRadius: 6,
|
||||
background: isHidden ? 'rgba(255,255,255,0.05)' : `${cal.color}20`,
|
||||
}}>
|
||||
{cal.count}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
|
||||
<button
|
||||
onClick={() => { setAddDate(today.toISOString().split('T')[0]); setShowAddModal(true) }}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '8px 18px', borderRadius: 14,
|
||||
background: 'linear-gradient(135deg, rgba(99,102,241,0.2), rgba(139,92,246,0.15))',
|
||||
border: '1px solid rgba(129,140,248,0.25)',
|
||||
color: '#a5b4fc', fontSize: 13, fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
<Plus size={15} />
|
||||
Событие
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Weekday headers */}
|
||||
@@ -238,43 +257,26 @@ export default function CalendarTab() {
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
padding: '5px 4px',
|
||||
background: isToday
|
||||
? 'linear-gradient(135deg, rgba(99,102,241,0.15), rgba(139,92,246,0.08))'
|
||||
: 'rgba(255,255,255,0.015)',
|
||||
border: isToday
|
||||
? '1px solid rgba(129,140,248,0.3)'
|
||||
: '1px solid transparent',
|
||||
cursor: 'pointer',
|
||||
minHeight: 56,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
borderRadius: 12, padding: '5px 4px',
|
||||
background: isToday ? 'linear-gradient(135deg, rgba(99,102,241,0.15), rgba(139,92,246,0.08))' : 'rgba(255,255,255,0.015)',
|
||||
border: isToday ? '1px solid rgba(129,140,248,0.3)' : '1px solid transparent',
|
||||
cursor: 'pointer', minHeight: 56,
|
||||
display: 'flex', flexDirection: 'column', gap: 2,
|
||||
transition: 'all 0.2s ease',
|
||||
}}
|
||||
>
|
||||
<span style={{
|
||||
fontSize: 12,
|
||||
fontWeight: isToday ? 700 : 500,
|
||||
fontSize: 12, fontWeight: isToday ? 700 : 500,
|
||||
color: isToday ? '#a5b4fc' : isWeekend ? 'rgba(248,113,113,0.6)' : 'var(--text-secondary)',
|
||||
textAlign: 'right', paddingRight: 4,
|
||||
}}>{day}</span>
|
||||
{dayEvents.slice(0, 2).map(e => (
|
||||
<div
|
||||
key={e.id}
|
||||
onClick={ev => { ev.stopPropagation(); setSelectedEvent(e) }}
|
||||
style={{
|
||||
fontSize: 10, fontWeight: 600,
|
||||
background: e.color + '1a',
|
||||
border: `1px solid ${e.color}30`,
|
||||
color: e.color,
|
||||
borderRadius: 6,
|
||||
padding: '2px 5px',
|
||||
overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<div key={e.id} onClick={ev => { ev.stopPropagation(); setSelectedEvent(e) }} style={{
|
||||
fontSize: 10, fontWeight: 600,
|
||||
background: e.color + '1a', border: `1px solid ${e.color}30`,
|
||||
color: e.color, borderRadius: 6, padding: '2px 5px',
|
||||
overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', cursor: 'pointer',
|
||||
}}>
|
||||
{e.title}
|
||||
</div>
|
||||
))}
|
||||
@@ -287,15 +289,11 @@ export default function CalendarTab() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right panel: upcoming events */}
|
||||
{/* Right panel */}
|
||||
<div style={{
|
||||
width: 220, flexShrink: 0,
|
||||
display: 'flex', flexDirection: 'column', gap: 10,
|
||||
overflowY: 'auto',
|
||||
background: 'rgba(255,255,255,0.02)',
|
||||
borderRadius: 22,
|
||||
padding: '20px 16px',
|
||||
border: '1px solid rgba(255,255,255,0.04)',
|
||||
width: 220, flexShrink: 0, display: 'flex', flexDirection: 'column', gap: 10,
|
||||
overflowY: 'auto', background: 'rgba(255,255,255,0.02)', borderRadius: 22,
|
||||
padding: '20px 16px', border: '1px solid rgba(255,255,255,0.04)',
|
||||
}}>
|
||||
<div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 4 }}>Ближайшие</div>
|
||||
{upcoming.length === 0 && !loading && (
|
||||
@@ -304,24 +302,13 @@ export default function CalendarTab() {
|
||||
{upcoming.map(e => {
|
||||
const d = new Date(e.start)
|
||||
return (
|
||||
<div
|
||||
key={e.id}
|
||||
onClick={() => setSelectedEvent(e)}
|
||||
style={{
|
||||
borderRadius: 16,
|
||||
padding: '14px 14px',
|
||||
background: `${e.color}0c`,
|
||||
border: `1px solid ${e.color}1a`,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.25s ease',
|
||||
}}
|
||||
>
|
||||
<div key={e.id} onClick={() => setSelectedEvent(e)} style={{
|
||||
borderRadius: 16, padding: '14px',
|
||||
background: `${e.color}0c`, border: `1px solid ${e.color}1a`,
|
||||
cursor: 'pointer', transition: 'all 0.25s ease',
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
|
||||
<div style={{
|
||||
width: 32, height: 32, borderRadius: 10,
|
||||
background: `${e.color}1a`,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
|
||||
}}>
|
||||
<div style={{ width: 32, height: 32, borderRadius: 10, background: `${e.color}1a`, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
||||
<span style={{ fontSize: 14, fontWeight: 700, color: e.color }}>{d.getDate()}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: e.color, fontWeight: 600 }}>{MONTHS[d.getMonth()].slice(0, 3)}</div>
|
||||
@@ -340,11 +327,9 @@ export default function CalendarTab() {
|
||||
{selectedEvent && (
|
||||
<div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(8px)', zIndex: 100, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }} onClick={() => { setSelectedEvent(null); setConfirmDelete(false) }}>
|
||||
<div style={{
|
||||
background: 'rgba(18,18,35,0.95)',
|
||||
backdropFilter: 'blur(40px)',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
borderRadius: 24, padding: 28, maxWidth: 380, width: '100%',
|
||||
boxShadow: '0 25px 60px rgba(0,0,0,0.5)',
|
||||
background: 'rgba(18,18,35,0.95)', backdropFilter: 'blur(40px)',
|
||||
border: '1px solid rgba(255,255,255,0.08)', borderRadius: 24, padding: 28,
|
||||
maxWidth: 380, width: '100%', boxShadow: '0 25px 60px rgba(0,0,0,0.5)',
|
||||
}} onClick={e => e.stopPropagation()}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 20 }}>
|
||||
<div style={{ display: 'flex', gap: 14 }}>
|
||||
@@ -356,78 +341,45 @@ export default function CalendarTab() {
|
||||
</div>
|
||||
<button onClick={() => { setSelectedEvent(null); setConfirmDelete(false) }} style={{ color: 'var(--text-secondary)', padding: 4 }}><X size={18} /></button>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 10,
|
||||
padding: '10px 14px', borderRadius: 12,
|
||||
background: 'rgba(255,255,255,0.03)',
|
||||
color: 'var(--text-secondary)', fontSize: 13,
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px', borderRadius: 12, background: 'rgba(255,255,255,0.03)', color: 'var(--text-secondary)', fontSize: 13 }}>
|
||||
<Clock size={15} />
|
||||
{selectedEvent.allDay
|
||||
? 'Весь день'
|
||||
: `${new Date(selectedEvent.start).toLocaleString('ru-RU', { day: 'numeric', month: 'long', hour: '2-digit', minute: '2-digit' })} — ${new Date(selectedEvent.end).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' })}`
|
||||
}
|
||||
{selectedEvent.allDay ? 'Весь день' : `${new Date(selectedEvent.start).toLocaleString('ru-RU', { day: 'numeric', month: 'long', hour: '2-digit', minute: '2-digit' })} — ${new Date(selectedEvent.end).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' })}`}
|
||||
</div>
|
||||
{selectedEvent.location && (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 10,
|
||||
padding: '10px 14px', borderRadius: 12,
|
||||
background: 'rgba(255,255,255,0.03)',
|
||||
color: 'var(--text-secondary)', fontSize: 13,
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px', borderRadius: 12, background: 'rgba(255,255,255,0.03)', color: 'var(--text-secondary)', fontSize: 13 }}>
|
||||
<MapPin size={15} /> {selectedEvent.location}
|
||||
</div>
|
||||
)}
|
||||
{selectedEvent.description && (
|
||||
<div style={{ fontSize: 13, color: 'var(--text-secondary)', marginTop: 4, lineHeight: 1.6 }}>{selectedEvent.description}</div>
|
||||
)}
|
||||
|
||||
{/* Delete button */}
|
||||
<div style={{ marginTop: 8, borderTop: '1px solid rgba(255,255,255,0.06)', paddingTop: 14 }}>
|
||||
{!confirmDelete ? (
|
||||
<button
|
||||
onClick={() => setConfirmDelete(true)}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '10px 14px', borderRadius: 12,
|
||||
background: 'rgba(239,68,68,0.08)',
|
||||
border: '1px solid rgba(239,68,68,0.2)',
|
||||
color: '#f87171', fontSize: 13, fontWeight: 600,
|
||||
width: '100%', justifyContent: 'center',
|
||||
transition: 'all 0.25s ease',
|
||||
}}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
Удалить событие
|
||||
<button onClick={() => setConfirmDelete(true)} style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '10px 14px', borderRadius: 12,
|
||||
background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.2)',
|
||||
color: '#f87171', fontSize: 13, fontWeight: 600,
|
||||
width: '100%', justifyContent: 'center',
|
||||
}}>
|
||||
<Trash2 size={14} /> Удалить событие
|
||||
</button>
|
||||
) : (
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<button
|
||||
onClick={() => deleteEvent(selectedEvent)}
|
||||
disabled={deleting}
|
||||
style={{
|
||||
flex: 1, padding: '10px 14px', borderRadius: 12,
|
||||
background: deleting ? 'rgba(239,68,68,0.1)' : 'rgba(239,68,68,0.2)',
|
||||
border: '1px solid rgba(239,68,68,0.35)',
|
||||
color: '#f87171', fontSize: 13, fontWeight: 600,
|
||||
cursor: deleting ? 'default' : 'pointer',
|
||||
transition: 'all 0.25s ease',
|
||||
}}
|
||||
>
|
||||
<button onClick={() => deleteEvent(selectedEvent)} disabled={deleting} style={{
|
||||
flex: 1, padding: '10px 14px', borderRadius: 12,
|
||||
background: deleting ? 'rgba(239,68,68,0.1)' : 'rgba(239,68,68,0.2)',
|
||||
border: '1px solid rgba(239,68,68,0.35)',
|
||||
color: '#f87171', fontSize: 13, fontWeight: 600,
|
||||
}}>
|
||||
{deleting ? 'Удаление...' : 'Да, удалить'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setConfirmDelete(false)}
|
||||
style={{
|
||||
flex: 1, padding: '10px 14px', borderRadius: 12,
|
||||
background: 'rgba(255,255,255,0.04)',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
color: 'var(--text-secondary)', fontSize: 13, fontWeight: 600,
|
||||
transition: 'all 0.25s ease',
|
||||
}}
|
||||
>
|
||||
<button onClick={() => setConfirmDelete(false)} style={{
|
||||
flex: 1, padding: '10px 14px', borderRadius: 12,
|
||||
background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)',
|
||||
color: 'var(--text-secondary)', fontSize: 13, fontWeight: 600,
|
||||
}}>
|
||||
Отмена
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user