import { useEffect, useState, useRef } from 'react'; import { Container, Title, Button, Group, Text, Modal, TextInput, Textarea, Switch, Select, Stack, Box, ActionIcon, } from '@mantine/core'; import { DatePickerInput } from '@mantine/dates'; import { IconCalendar, IconClock, IconSettings, IconLogout, IconX } from '@tabler/icons-react'; import { useDisclosure } from '@mantine/hooks'; import { FixedCalendar } from '../components/common/FixedCalendar'; import { PopoverTimePicker } from '../components/common/PopoverTimePicker'; import { useNavigate } from 'react-router-dom'; import { useAppStore } from '../stores'; import { AnniversaryList } from '../components/anniversary/AnniversaryList'; import { ReminderList } from '../components/reminder/ReminderList'; import { NoteEditor } from '../components/note/NoteEditor'; import { FloatingAIChat } from '../components/ai/FloatingAIChat'; import appIcon from '../assets/icon.png'; import type { Event, EventType, RepeatType, PriorityType } from '../types'; import { calculateNextReminderDate, getReminderOptions, getDefaultReminderValue, calculateReminderTimes, getReminderValueFromTimes, formatReminderTimeDisplay } from '../utils/repeatCalculator'; export function HomePage() { const navigate = useNavigate(); const user = useAppStore((state) => state.user); const logout = useAppStore((state) => state.logout); const checkAuth = useAppStore((state) => state.checkAuth); const events = useAppStore((state) => state.events); const fetchEvents = useAppStore((state) => state.fetchEvents); const createEvent = useAppStore((state) => state.createEvent); const updateEventById = useAppStore((state) => state.updateEventById); const deleteEventById = useAppStore((state) => state.deleteEventById); const [opened, { open, close }] = useDisclosure(false); const [selectedEvent, setSelectedEvent] = useState(null); const [isEdit, setIsEdit] = useState(false); // Form state const [formType, setFormType] = useState('anniversary'); const [formTitle, setFormTitle] = useState(''); const [formContent, setFormContent] = useState(''); const [formDate, setFormDate] = useState(null); const [formTime, setFormTime] = useState(''); const [formIsLunar, setFormIsLunar] = useState(false); const [formRepeatType, setFormRepeatType] = useState('none'); const [formIsHoliday, setFormIsHoliday] = useState(false); const [formPriority, setFormPriority] = useState('none'); const [formReminderValue, setFormReminderValue] = useState('0'); // 提醒选项值 // Initialize auth and data on mount useEffect(() => { checkAuth(); fetchEvents().catch(console.error); }, []); // eslint-disable-line react-hooks/exhaustive-deps const handleLogout = async () => { await logout(); navigate('/landing'); }; const handleEventClick = (event: Event) => { setSelectedEvent(event); setIsEdit(true); setFormType(event.type); setFormTitle(event.title); setFormContent(event.content || ''); setFormDate(new Date(event.date)); setFormIsLunar(event.is_lunar); setFormRepeatType(event.repeat_type); setFormIsHoliday(event.is_holiday || false); setFormPriority(event.priority || 'none'); // 提取时间(如果日期中包含时间信息) const eventDate = new Date(event.date); const hours = eventDate.getHours(); const minutes = eventDate.getMinutes(); const hasTime = hours !== 0 || minutes !== 0; setFormTime(hasTime ? `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}` : ''); // 从 reminder_times 反推用户选择的提醒选项值 setFormReminderValue(getReminderValueFromTimes(event.reminder_times, event.date, hasTime)); open(); }; const handleAddClick = (type: EventType) => { setSelectedEvent(null); setIsEdit(false); setFormType(type); setFormTitle(''); setFormContent(''); setFormDate(null); setFormTime(''); setFormIsLunar(false); setFormRepeatType('none'); setFormIsHoliday(type === 'anniversary'); setFormPriority('none'); setFormReminderValue('0'); open(); }; const handleSubmit = async () => { if (!formTitle.trim() || !formDate) return; // 确保 date 是 Date 对象 const dateObj = formDate instanceof Date ? formDate : new Date(formDate as unknown as string); let dateStr: string; const year = dateObj.getFullYear(); const month = String(dateObj.getMonth() + 1).padStart(2, '0'); const day = String(dateObj.getDate()).padStart(2, '0'); if (formTime) { // 有时间:构建 UTC 时间格式(用户选择的是本地时间,需要转换为 UTC 存储) const hours = parseInt(formTime.split(':')[0]); const minutes = parseInt(formTime.split(':')[1]); // 先用本地时间创建日期对象,然后获取 UTC 时间 const localDate = new Date(year, dateObj.getMonth(), day, hours, minutes); dateStr = localDate.toISOString(); } else { // 无时间:使用 UTC 日期格式 dateStr = `${year}-${month}-${day}T00:00:00.000Z`; } // 构建事件数据,确保不包含 undefined 值 const eventData: Record = { type: formType, title: formTitle, date: dateStr, is_lunar: formIsLunar, repeat_type: formRepeatType, // 计算下一次提醒日期 next_reminder_date: calculateNextReminderDate(dateStr, formRepeatType, undefined), is_holiday: formIsHoliday ?? false, priority: formPriority, }; // 只有当 content 有值时才包含 if (formContent.trim()) { eventData.content = formContent; } // 计算提醒时间点(仅对提醒事项) if (formType === 'reminder') { const hasTime = !!formTime; eventData.reminder_times = calculateReminderTimes(dateStr, hasTime, formReminderValue); } if (isEdit && selectedEvent) { await updateEventById(selectedEvent.id, eventData); } else { await createEvent(eventData); } close(); resetForm(); fetchEvents(); }; const handleDeleteFromModal = async () => { if (!selectedEvent) return; await deleteEventById(selectedEvent.id); close(); resetForm(); }; const handleToggleComplete = async (event: Event) => { if (event.type !== 'reminder') return; // 使用当前期望的状态(取反) const newCompleted = !event.is_completed; const result = await updateEventById(event.id, { is_completed: newCompleted, }); if (result.error) { console.error('更新失败:', result.error); } // 乐观更新已处理 UI 响应,无需 fetchEvents }; const handleDelete = async (event: Event) => { if (event.type !== 'reminder') return; await deleteEventById(event.id); fetchEvents(); }; const handlePostpone = async (event: Event) => { if (event.type !== 'reminder') return; // 将日期顺延到今天,保留原事件的时间 const today = new Date(); const originalDate = new Date(event.date); // 提取原时间的小时和分钟 const hours = originalDate.getHours(); const minutes = originalDate.getMinutes(); // 构建新的本地时间字符串 const newDateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}T${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00`; // 构建更新数据 const updateData: Record = { date: newDateStr, priority: event.priority || 'none', }; // 如果是重复提醒,同步更新 next_reminder_date if (event.repeat_type && event.repeat_type !== 'none') { const nextReminderDate = calculateNextReminderDate( newDateStr, event.repeat_type as RepeatType, event.repeat_interval || undefined ); updateData.next_reminder_date = nextReminderDate; } const result = await updateEventById(event.id, updateData); if (result.error) { console.error('顺延失败:', result.error); } }; const handleDateChange = async ( event: Event, date: string, repeatType: string, repeatInterval: number | null ) => { if (event.type !== 'reminder') return; // 保留原事件的时间信息 const originalDate = new Date(event.date); const hours = originalDate.getHours(); const minutes = originalDate.getMinutes(); // 构建新的本地时间字符串 const newDateStr = `${date}T${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00`; // 构建更新数据 const updateData: Record = { date: newDateStr, priority: event.priority || 'none', }; // 如果是重复提醒,同步更新 next_reminder_date if (repeatType && repeatType !== 'none') { const nextReminderDate = calculateNextReminderDate( newDateStr, repeatType as RepeatType, repeatInterval || undefined ); updateData.next_reminder_date = nextReminderDate; } // 合并更新 const result = await updateEventById(event.id, updateData); if (result.error) { console.error('日期调整失败:', result.error); } fetchEvents(); // 刷新列表以更新UI }; const handlePriorityChange = async (event: Event, priority: PriorityType) => { if (event.type !== 'reminder') return; // 合并更新 priority 和 date,确保 updated_at 被更新 const result = await updateEventById(event.id, { priority, date: event.date, }); if (result.error) { console.error('优先级设置失败:', result.error); } fetchEvents(); // 刷新列表以更新UI }; const resetForm = () => { setFormType('anniversary'); setFormTitle(''); setFormContent(''); setFormDate(null); setFormTime(''); setFormIsLunar(false); setFormRepeatType('none'); setFormIsHoliday(false); setFormPriority('none'); setFormReminderValue('0'); setSelectedEvent(null); setIsEdit(false); }; const handleAIEventCreated = () => { fetchEvents(); }; return (
{/* Header */} 掐日子 掐日子 {/* 设置入口 */} {user?.nickname || user?.email} {/* Main Content - 3 column horizontal layout */}
{/* Left column - Anniversary */}
handleAddClick('anniversary')} />
{/* Middle column - Reminder */}
handleAddClick('reminder')} onDelete={handleDelete} onPostpone={handlePostpone} onDateChange={handleDateChange} onPriorityChange={handlePriorityChange} />
{/* Right column - Note */}
{/* AI Chat - Floating */} {/* Add/Edit Event Modal */} {isEdit ? '编辑事件' : '添加事件'} } size="md" styles={{ header: { borderBottom: '1px solid rgba(0,0,0,0.06)', }, body: { paddingTop: 20, }, }} > {/* Event type */}