diff --git a/components/CalendarTab.tsx b/components/CalendarTab.tsx
index 6b7b3f9..8f809b7 100644
--- a/components/CalendarTab.tsx
+++ b/components/CalendarTab.tsx
@@ -1,6 +1,6 @@
'use client'
import { useState, useEffect, useMemo } from 'react'
-import { ChevronLeft, ChevronRight, Plus, X, Clock, MapPin, Trash2, Eye, EyeOff } from 'lucide-react'
+import { ChevronLeft, ChevronRight, Plus, X, Clock, MapPin, Trash2, Eye, EyeOff, CalendarDays, User, AlignLeft } from 'lucide-react'
interface CalendarEvent {
id: string
@@ -18,6 +18,15 @@ interface CalendarEvent {
const WEEKDAYS = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']
const MONTHS = ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь']
+function formatTime(iso: string): string {
+ return new Date(iso).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' })
+}
+
+function formatFullDate(iso: string): string {
+ return new Date(iso).toLocaleDateString('ru-RU', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })
+}
+
+// ————— Add Event Modal —————
function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string; onClose: () => void; onSaved: (e: any) => void }) {
const [title, setTitle] = useState('')
const [date, setDate] = useState(defaultDate)
@@ -27,10 +36,11 @@ function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string;
const [saving, setSaving] = useState(false)
const [error, setError] = useState('')
- const inputStyle = {
- padding: '12px 16px', borderRadius: 14,
+ const inputStyle: React.CSSProperties = {
+ padding: '14px 18px', 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',
+ color: 'var(--text-primary)', fontSize: 15, outline: 'none', fontFamily: 'inherit',
+ width: '100%',
}
const save = async () => {
@@ -46,30 +56,74 @@ function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string;
}
return (
-
-
e.stopPropagation()}>
-
-
Новое событие
-
+
+
e.stopPropagation()}>
+ {/* Header */}
+
-
-
setTitle(e.target.value)} placeholder="Название события" autoFocus style={inputStyle} />
-
setDate(e.target.value)} style={inputStyle} />
+
+
+ {/* Title */}
+
+
+ setTitle(e.target.value)} placeholder="Встреча, звонок, задача..." autoFocus style={inputStyle} />
+
+
+ {/* Date */}
+
+
+ setDate(e.target.value)} style={inputStyle} />
+
+
+ {/* Time */}
{!allDay && (
-
-
setStartTime(e.target.value)} style={{ ...inputStyle, flex: 1 }} />
-
setEndTime(e.target.value)} style={{ ...inputStyle, flex: 1 }} />
+
)}
-
{/* Weekday headers */}
-
+
{WEEKDAYS.map((d, i) => (
{
- if (dayEvents.length === 1) setSelectedEvent(dayEvents[0])
- else if (dayEvents.length === 0) {
- setAddDate(`${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`)
- setShowAddModal(true)
- }
- }}
+ onClick={() => handleDayClick(day)}
style={{
- borderRadius: 12, padding: '5px 4px',
+ borderRadius: 12, padding: '4px 5px',
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,
+ cursor: 'pointer', minHeight: 70,
+ display: 'flex', flexDirection: 'column', gap: 3,
transition: 'all 0.2s ease',
}}
>
{day}
- {dayEvents.slice(0, 2).map(e => (
-
{ 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}
+ {dayEvents.slice(0, 3).map(e => (
+
{ ev.stopPropagation(); setSelectedEvent(e) }}
+ style={{
+ display: 'flex', alignItems: 'center', gap: 4,
+ background: `${e.color}18`,
+ borderLeft: `3px solid ${e.color}`,
+ borderRadius: '0 6px 6px 0',
+ padding: '4px 6px',
+ cursor: 'pointer',
+ overflow: 'hidden',
+ }}
+ >
+ {!e.allDay && (
+
+ {formatTime(e.start)}
+
+ )}
+
+ {e.title}
+
))}
- {dayEvents.length > 2 && (
-
+{dayEvents.length - 2}
+ {dayEvents.length > 3 && (
+
+ +{dayEvents.length - 3}
+
)}
)
@@ -291,7 +558,7 @@ export default function CalendarTab() {
{/* Right panel */}
@@ -308,86 +575,34 @@ export default function CalendarTab() {
cursor: 'pointer', transition: 'all 0.25s ease',
}}>
-
-
{d.getDate()}
+
+ {d.getDate()}
+
+
+
{MONTHS[d.getMonth()].slice(0, 3)}
+
+ {e.allDay ? 'Весь день' : formatTime(e.start)}
+
-
{MONTHS[d.getMonth()].slice(0, 3)}
{e.title}
-
- {e.allDay ? 'Весь день' : d.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' })}
-
-
{e.ownerName}
+
{e.ownerName}
)
})}
- {/* Event detail modal */}
+ {/* Modals */}
{selectedEvent && (
-
{ setSelectedEvent(null); setConfirmDelete(false) }}>
-
e.stopPropagation()}>
-
-
-
-
-
{selectedEvent.title}
-
{selectedEvent.ownerName}
-
-
-
{ setSelectedEvent(null); setConfirmDelete(false) }} style={{ color: 'var(--text-secondary)', padding: 4 }}>
-
-
-
-
- {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.location && (
-
- {selectedEvent.location}
-
- )}
- {selectedEvent.description && (
-
{selectedEvent.description}
- )}
-
- {!confirmDelete ? (
-
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',
- }}>
- Удалить событие
-
- ) : (
-
- 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 ? 'Удаление...' : 'Да, удалить'}
-
- 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,
- }}>
- Отмена
-
-
- )}
-
-
-
-
+
setSelectedEvent(null)} onDelete={deleteEvent} />
+ )}
+
+ {dayPopover && (
+ setDayPopover(null)}
+ onSelect={e => { setDayPopover(null); setSelectedEvent(e) }}
+ />
)}
{showAddModal && (