225 lines
7.2 KiB
TypeScript
225 lines
7.2 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { UserRole, CalendarEvent, AppSettings, UserStatus } from './types';
|
|
import { LoginScreen } from './components/LoginScreen';
|
|
import { CalendarView } from './components/CalendarView';
|
|
import { EventModal } from './components/EventModal';
|
|
import { SettingsModal } from './components/SettingsModal';
|
|
import { AssistantView } from './components/AssistantView';
|
|
import { SpaceBackground } from './components/SpaceBackground';
|
|
import { FairyDust } from './components/FairyDust';
|
|
import { getStoredEvents, saveEvent, seedInitialData, deleteEvent, getSettings, saveSettings } from './services/storageService';
|
|
import { mcpService } from './services/mcpService';
|
|
import { generateDailyHoroscope } from './services/aiService';
|
|
import { parseISO, set } from 'date-fns';
|
|
|
|
const App: React.FC = () => {
|
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
const [currentUser, setCurrentUser] = useState<UserRole | null>(null);
|
|
const [currentStatus, setCurrentStatus] = useState<UserStatus>('online');
|
|
const [events, setEvents] = useState<CalendarEvent[]>([]);
|
|
const [settings, setSettings] = useState<AppSettings>(getSettings());
|
|
|
|
// Modal State
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
const [isAssistantOpen, setIsAssistantOpen] = useState(false);
|
|
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
|
|
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null);
|
|
|
|
// Theme Injection
|
|
useEffect(() => {
|
|
const root = document.documentElement;
|
|
root.style.setProperty('--mom-primary', settings.momColor);
|
|
root.style.setProperty('--dad-primary', settings.dadColor);
|
|
root.style.setProperty('--mom-soft', `${settings.momColor}33`);
|
|
root.style.setProperty('--dad-soft', `${settings.dadColor}33`);
|
|
}, [settings]);
|
|
|
|
useEffect(() => {
|
|
seedInitialData();
|
|
setEvents(getStoredEvents());
|
|
const storedUser = localStorage.getItem('ourtime_user');
|
|
if (storedUser) {
|
|
setCurrentUser(storedUser as UserRole);
|
|
setIsAuthenticated(true);
|
|
}
|
|
}, []);
|
|
|
|
// Daily Horoscope Check
|
|
useEffect(() => {
|
|
const checkHoroscope = async () => {
|
|
if (!isAuthenticated || !currentUser || settings.momZodiac === 'Unknown' || settings.dadZodiac === 'Unknown') return;
|
|
|
|
const today = new Date();
|
|
const todayStr = today.toISOString().split('T')[0];
|
|
const horoscopeId = `horoscope-${todayStr}`;
|
|
|
|
// Check if we already have a magic event for today with this ID
|
|
const hasHoroscope = events.some(e => e.id === horoscopeId);
|
|
|
|
if (!hasHoroscope) {
|
|
// Generate it
|
|
console.log("Generating daily horoscope...");
|
|
const prediction = await generateDailyHoroscope(settings.momZodiac, settings.dadZodiac, settings);
|
|
|
|
const newEvent: CalendarEvent = {
|
|
id: horoscopeId,
|
|
title: `✨ ${prediction}`,
|
|
description: `Daily guidance for ${settings.momZodiac} & ${settings.dadZodiac}`,
|
|
startTime: new Date().toISOString(), // Use current time/date
|
|
endTime: new Date().toISOString(),
|
|
isAllDay: true,
|
|
category: 'magic',
|
|
createdBy: currentUser
|
|
};
|
|
|
|
handleSaveEvent(newEvent);
|
|
}
|
|
};
|
|
|
|
// Slight delay to ensure load
|
|
const timer = setTimeout(checkHoroscope, 2000);
|
|
return () => clearTimeout(timer);
|
|
}, [isAuthenticated, currentUser, events, settings]);
|
|
|
|
// MCP Service Connection
|
|
useEffect(() => {
|
|
if (settings.enableMcp && settings.mcpServerUrl) {
|
|
mcpService.connect(settings.mcpServerUrl);
|
|
const unsubscribe = mcpService.subscribe((newEvent) => {
|
|
handleSaveEvent(newEvent);
|
|
});
|
|
return () => {
|
|
unsubscribe();
|
|
mcpService.disconnect();
|
|
};
|
|
} else {
|
|
mcpService.disconnect();
|
|
}
|
|
}, [settings.enableMcp, settings.mcpServerUrl]);
|
|
|
|
const handleLogin = (role: UserRole) => {
|
|
setCurrentUser(role);
|
|
setIsAuthenticated(true);
|
|
localStorage.setItem('ourtime_user', role);
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
setCurrentUser(null);
|
|
setIsAuthenticated(false);
|
|
localStorage.removeItem('ourtime_user');
|
|
};
|
|
|
|
const handleAddEventClick = (date?: Date) => {
|
|
setSelectedEvent(null);
|
|
setSelectedDate(date);
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const handleEditEventClick = (event: CalendarEvent) => {
|
|
setSelectedEvent(event);
|
|
setSelectedDate(parseISO(event.startTime));
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const handleSaveEvent = (event: CalendarEvent) => {
|
|
const updatedEvents = saveEvent(event);
|
|
setEvents(updatedEvents);
|
|
};
|
|
|
|
const handleMoveEvent = (eventId: string, newDate: Date) => {
|
|
const event = events.find(e => e.id === eventId);
|
|
if (!event) return;
|
|
|
|
const oldStart = parseISO(event.startTime);
|
|
const oldEnd = parseISO(event.endTime);
|
|
const duration = oldEnd.getTime() - oldStart.getTime();
|
|
|
|
// Create new start time with the new date but keeping the old hours/minutes
|
|
const newStart = set(newDate, {
|
|
hours: oldStart.getHours(),
|
|
minutes: oldStart.getMinutes(),
|
|
seconds: oldStart.getSeconds()
|
|
});
|
|
|
|
const newEnd = new Date(newStart.getTime() + duration);
|
|
|
|
const updatedEvent = {
|
|
...event,
|
|
startTime: newStart.toISOString(),
|
|
endTime: newEnd.toISOString()
|
|
};
|
|
|
|
handleSaveEvent(updatedEvent);
|
|
};
|
|
|
|
const handleDeleteEvent = (id: string) => {
|
|
const updatedEvents = deleteEvent(id);
|
|
setEvents(updatedEvents);
|
|
};
|
|
|
|
const handleSaveSettings = (newSettings: AppSettings) => {
|
|
setSettings(newSettings);
|
|
saveSettings(newSettings);
|
|
};
|
|
|
|
if (!isAuthenticated || !currentUser) {
|
|
return (
|
|
<>
|
|
<SpaceBackground />
|
|
<FairyDust />
|
|
<LoginScreen onLogin={handleLogin} />
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen text-gray-200 font-sans selection:bg-purple-500 selection:text-white">
|
|
<SpaceBackground />
|
|
<FairyDust />
|
|
|
|
<CalendarView
|
|
events={events}
|
|
currentUser={currentUser}
|
|
currentStatus={currentStatus}
|
|
onStatusChange={setCurrentStatus}
|
|
onAddEvent={handleAddEventClick}
|
|
onEditEvent={handleEditEventClick}
|
|
onMoveEvent={handleMoveEvent}
|
|
onLogout={handleLogout}
|
|
onDeleteEvent={handleDeleteEvent}
|
|
onOpenSettings={() => setIsSettingsOpen(true)}
|
|
onOpenAssistant={() => setIsAssistantOpen(true)}
|
|
settings={settings}
|
|
/>
|
|
|
|
<EventModal
|
|
isOpen={isModalOpen}
|
|
onClose={() => setIsModalOpen(false)}
|
|
onSave={handleSaveEvent}
|
|
onDelete={handleDeleteEvent}
|
|
currentUserRole={currentUser}
|
|
selectedDate={selectedDate}
|
|
initialEvent={selectedEvent}
|
|
/>
|
|
|
|
<SettingsModal
|
|
isOpen={isSettingsOpen}
|
|
onClose={() => setIsSettingsOpen(false)}
|
|
settings={settings}
|
|
onSave={handleSaveSettings}
|
|
/>
|
|
|
|
<AssistantView
|
|
isOpen={isAssistantOpen}
|
|
onClose={() => setIsAssistantOpen(false)}
|
|
currentUser={currentUser}
|
|
settings={settings}
|
|
onEventCreated={handleSaveEvent}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default App;
|