From 868d35ba3e03138ba53f1eb443cb7323915f5609 Mon Sep 17 00:00:00 2001 From: Cosmo Date: Wed, 22 Apr 2026 19:33:38 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20redesigned=20add-event=20modal=20with?= =?UTF-8?q?=20calendar=20selector=20(=D0=94=D0=B0=D0=BD=D0=B8=D0=B8=D0=BB/?= =?UTF-8?q?=D0=A1=D0=B2=D0=B5=D1=82=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/calendar/route.ts | 19 ++-- components/CalendarTab.tsx | 179 ++++++++++++++++++++++++++----------- 2 files changed, 141 insertions(+), 57 deletions(-) diff --git a/app/api/calendar/route.ts b/app/api/calendar/route.ts index 01ac34d..e47593a 100644 --- a/app/api/calendar/route.ts +++ b/app/api/calendar/route.ts @@ -105,7 +105,7 @@ export async function GET(req: Request) { export async function POST(req: Request) { const body = await req.json() - const { title, date, startTime, endTime, allDay } = body + const { title, date, startTime, endTime, allDay, owner } = body const auth = getAuth(false) if (!auth) return NextResponse.json({ error: 'not_configured' }, { status: 500 }) @@ -113,6 +113,15 @@ export async function POST(req: Request) { const calendarClient = google.calendar({ version: 'v3', auth: auth as any }) const daniilCalendarId = process.env.DANIIL_CALENDAR_ID || 'daniilklimov25@gmail.com' + const svetaCalendarId = process.env.SVETA_CALENDAR_ID || '' + + const calendars: Record = { + daniil: { id: daniilCalendarId, name: 'Даниил', color: '#6366f1' }, + ...(svetaCalendarId ? { sveta: { id: svetaCalendarId, name: 'Света', color: '#ec4899' } } : {}), + } + + const selectedOwner = owner && calendars[owner] ? owner : 'daniil' + const cal = calendars[selectedOwner] let start: any, end: any if (allDay) { @@ -125,7 +134,7 @@ export async function POST(req: Request) { try { const res = await calendarClient.events.insert({ - calendarId: daniilCalendarId, + calendarId: cal.id, requestBody: { summary: title, start, end }, }) const e = res.data @@ -136,9 +145,9 @@ export async function POST(req: Request) { start: e.start?.dateTime || e.start?.date, end: e.end?.dateTime || e.end?.date, allDay: !e.start?.dateTime, - owner: 'daniil', - ownerName: 'Даниил', - color: '#6366f1', + owner: selectedOwner, + ownerName: cal.name, + color: cal.color, } }) } catch (err: any) { diff --git a/components/CalendarTab.tsx b/components/CalendarTab.tsx index 52d7fcb..30b58bf 100644 --- a/components/CalendarTab.tsx +++ b/components/CalendarTab.tsx @@ -27,27 +27,31 @@ function formatFullDate(iso: string): string { } // ————— Add Event Modal ————— +const CALENDAR_OPTIONS = [ + { owner: 'daniil', name: 'Даниил', color: '#6366f1' }, + { owner: 'sveta', name: 'Света', color: '#ec4899' }, +] + function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string; onClose: () => void; onSaved: (e: any) => void }) { const [title, setTitle] = useState('') const [date, setDate] = useState(defaultDate) const [startTime, setStartTime] = useState('10:00') const [endTime, setEndTime] = useState('11:00') const [allDay, setAllDay] = useState(false) + const [owner, setOwner] = useState('daniil') const [saving, setSaving] = useState(false) const [error, setError] = useState('') - 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: 15, outline: 'none', fontFamily: 'inherit', - width: '100%', - } + const selectedCal = CALENDAR_OPTIONS.find(c => c.owner === owner) || CALENDAR_OPTIONS[0] + + const dateObj = date ? new Date(date + 'T00:00:00') : new Date() + const dateLabel = dateObj.toLocaleDateString('ru-RU', { weekday: 'short', day: 'numeric', month: 'long' }) const save = async () => { if (!title.trim()) { setError('Введите название'); return } setSaving(true); setError('') try { - const body = { title: title.trim(), date, startTime: allDay ? null : startTime, endTime: allDay ? null : endTime, allDay } + const body = { title: title.trim(), date, startTime: allDay ? null : startTime, endTime: allDay ? null : endTime, allDay, owner } 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) @@ -55,75 +59,146 @@ function AddEventModal({ defaultDate, onClose, onSaved }: { defaultDate: string; } catch (e: any) { setError(e.message || 'Ошибка сохранения'); setSaving(false) } } + const fieldInputStyle: React.CSSProperties = { + padding: '14px 18px', borderRadius: 14, width: '100%', + background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.07)', + color: 'var(--text-primary)', fontSize: 15, outline: 'none', fontFamily: 'inherit', + } + + const fieldLabelStyle: React.CSSProperties = { + fontSize: 11, color: 'var(--text-secondary)', fontWeight: 600, + marginBottom: 8, display: 'block', textTransform: 'uppercase', letterSpacing: '0.08em', + } + return (
e.stopPropagation()}> - {/* Header */} -
-
-
- + + {/* Colored header */} +
+
+
+
+ +
+
+
Новое событие
+
{dateLabel}
+
- Новое событие +
-
-
- {/* Title */} + {/* Body */} +
+ + {/* Calendar selector */}
- - setTitle(e.target.value)} placeholder="Встреча, звонок, задача..." autoFocus style={inputStyle} /> + +
+ {CALENDAR_OPTIONS.map(cal => { + const isSelected = owner === cal.owner + return ( + + ) + })} +
- {/* Date */} + {/* Title */}
- - setDate(e.target.value)} style={inputStyle} /> + + setTitle(e.target.value)} placeholder="Что запланировано?" autoFocus style={fieldInputStyle} /> +
+ + {/* Date + All day */} +
+
+ + setDate(e.target.value)} style={fieldInputStyle} /> +
+
{/* Time */} {!allDay && (
- +
- setStartTime(e.target.value)} style={{ ...inputStyle, flex: 1 }} /> - - setEndTime(e.target.value)} style={{ ...inputStyle, flex: 1 }} /> +
+ + setStartTime(e.target.value)} style={{ ...fieldInputStyle, paddingLeft: 36 }} /> +
+
+
+ + setEndTime(e.target.value)} style={{ ...fieldInputStyle, paddingLeft: 36 }} /> +
)} - {/* All day toggle */} - - - {error &&
{error}
} + {error && ( +
+ {error} +
+ )} + {/* Submit */}