import React, { useState, useEffect } from 'react'; import { JobApplication, JobFeedItem, UserProfile, NotificationLog, CalendarInterviewEvent, ApplicationStatus } from './types'; import { INITIAL_USER_PROFILE, INITIAL_APPLICATIONS, INITIAL_JOB_FEEDS, INITIAL_CALENDAR_EVENTS, INITIAL_NOTIFICATIONS } from './data/mockData'; import { fetchApplications, saveApplication, updateApplicationStatus, deleteApplicationRecord, fetchUserProfile, saveUserProfile, fetchNotifications, saveNotificationLog, fetchCalendarEvents, saveCalendarEvent } from './lib/localStorageEngine'; import { Navbar } from './components/Navbar'; import { Sidebar } from './components/Sidebar'; import { ApplicationsTracker } from './components/ApplicationsTracker'; import { WalkthroughTailor } from './components/WalkthroughTailor'; import { AutonomousEngine } from './components/AutonomousEngine'; import { JobFeeds } from './components/JobFeeds'; import { AnalyticsDashboard } from './components/AnalyticsDashboard'; import { CalendarScheduler } from './components/CalendarScheduler'; import { NotificationCenter } from './components/NotificationCenter'; import { ProfileSettings } from './components/ProfileSettings'; import { GoogleWorkspaceHub } from './components/GoogleWorkspaceHub'; import { TailoredResumeModal } from './components/TailoredResumeModal'; import { NewApplicationModal } from './components/NewApplicationModal'; export default function App() { const [activeTab, setActiveTab] = useState('applications'); // App state const [applications, setApplications] = useState(INITIAL_APPLICATIONS); const [userProfile, setUserProfile] = useState(INITIAL_USER_PROFILE); const [jobFeeds, setJobFeeds] = useState(INITIAL_JOB_FEEDS); const [calendarEvents, setCalendarEvents] = useState(INITIAL_CALENDAR_EVENTS); const [notifications, setNotifications] = useState(INITIAL_NOTIFICATIONS); // Modals & UI States const [isAutoApplying, setIsAutoApplying] = useState(false); const [selectedAppForModal, setSelectedAppForModal] = useState(null); const [isNewAppModalOpen, setIsNewAppModalOpen] = useState(false); const [walkthroughInitialUrl, setWalkthroughInitialUrl] = useState(''); // Initial Load from Firestore & Live Job Feeds useEffect(() => { async function loadData() { try { const firestoreApps = await fetchApplications(); if (firestoreApps.length > 0) setApplications(firestoreApps); const profile = await fetchUserProfile(); if (profile) setUserProfile(profile); const events = await fetchCalendarEvents(); if (events.length > 0) setCalendarEvents(events); const notifs = await fetchNotifications(); if (notifs.length > 0) setNotifications(notifs); // Fetch live job feeds from APIs + RSS const feedsRes = await fetch('/api/jobs/fetch-feeds'); if (feedsRes.ok) { const feedsData = await feedsRes.json(); if (feedsData.success && Array.isArray(feedsData.jobs) && feedsData.jobs.length > 0) { setJobFeeds(prev => [...feedsData.jobs, ...prev]); } } } catch (err) { console.warn('Initial load fallback to initial dataset:', err); } } loadData(); }, []); // Handlers const handleUpdateStatus = async (id: string, newStatus: ApplicationStatus, note?: string) => { setApplications(prev => prev.map(a => a.id === id ? { ...a, status: newStatus } : a)); await updateApplicationStatus(id, newStatus, note); // Send email alert for important status updates if (['Interviewing', 'Offer', 'Screening'].includes(newStatus)) { const app = applications.find(a => a.id === id); if (app) { const notif = await saveNotificationLog({ timestamp: new Date().toISOString(), type: 'STATUS_CHANGE', recipient: userProfile.email, subject: `Application Status Alert: ${app.company} — ${newStatus}`, body: `Your job application for ${app.role} at ${app.company} has moved to ${newStatus}.`, status: 'SENT', platform: 'Gmail' }); setNotifications(prev => [notif, ...prev]); } } }; const handleDeleteApplication = async (id: string) => { setApplications(prev => prev.filter(a => a.id !== id)); await deleteApplicationRecord(id); }; const handleSaveApplicationRecord = async (appData: Omit) => { const saved = await saveApplication(appData); setApplications(prev => [saved, ...prev.filter(a => a.id !== saved.id)]); // Notification alert const notif = await saveNotificationLog({ timestamp: new Date().toISOString(), type: 'AUTO_APPLY', recipient: userProfile.email, subject: `Application Logged: ${saved.company} (${saved.role})`, body: `Successfully logged application for ${saved.role} at ${saved.company} via ${saved.platform}. Match score: ${saved.matchScore}%.`, status: 'SENT', platform: 'Gmail' }); setNotifications(prev => [notif, ...prev]); }; const handleSaveUserProfile = async (updated: UserProfile) => { setUserProfile(updated); await saveUserProfile(updated); }; const handleToggleAutoApply = () => { const updated = { ...userProfile, autoApplyCriteria: { ...userProfile.autoApplyCriteria, enabled: !userProfile.autoApplyCriteria.enabled } }; handleSaveUserProfile(updated); }; // Run Autonomous Auto-Apply Engine Batch const handleRunAutoApplyBatch = async () => { setIsAutoApplying(true); try { const res = await fetch('/api/gemini/auto-apply-run', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jobFeeds, userProfile, criteria: userProfile.autoApplyCriteria }) }); const data = await res.json(); if (data.success && Array.isArray(data.results)) { for (const item of data.results) { if (item.autoApplied) { const feed = jobFeeds.find(f => f.id === item.jobId); await handleSaveApplicationRecord({ company: item.company, role: item.role, platform: feed?.platform || 'Greenhouse', location: feed?.location || 'San Francisco, CA', salaryRange: feed?.salary || '$180,000 - $220,000', status: 'Applied', matchScore: item.matchScore, appliedAt: new Date().toISOString(), jobUrl: feed?.url, jobDescription: feed?.description, notes: item.reason, autoApplied: true, timeline: [{ date: new Date().toLocaleDateString(), action: 'Autonomous AI Auto-Applied' }] }); } } } } catch (err) { console.error('Autonomous auto-apply failed:', err); } finally { setIsAutoApplying(false); } }; // Schedule Interview const handleScheduleInterview = async (eventData: Omit) => { const res = await fetch('/api/calendar/schedule-interview', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...eventData, userEmail: userProfile.email }) }); const data = await res.json(); const savedEvent = await saveCalendarEvent({ ...eventData, googleCalendarEventId: data.googleCalendarEventId, syncedToGoogle: true }); setCalendarEvents(prev => [...prev.filter(e => e.id !== savedEvent.id), savedEvent]); // Update application status to Interviewing if (eventData.applicationId) { await handleUpdateStatus(eventData.applicationId, 'Interviewing', `Interview scheduled for ${new Date(eventData.startTime).toLocaleDateString()}`); } // Log notification const notif = await saveNotificationLog({ timestamp: new Date().toISOString(), type: 'INTERVIEW_SCHEDULED', recipient: userProfile.email, subject: `Google Calendar Synced: ${eventData.company} Interview`, body: `Interview for ${eventData.role} at ${eventData.company} scheduled for ${new Date(eventData.startTime).toLocaleString()}. Synced to Google Calendar.`, status: 'SENT', platform: 'Google Calendar & Gmail' }); setNotifications(prev => [notif, ...prev]); }; // Send test email const handleSendTestEmail = async (recipient: string, subject: string, body: string) => { const res = await fetch('/api/notifications/send-email', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ recipient, subject, body }) }); const data = await res.json(); const notif = await saveNotificationLog({ timestamp: new Date().toISOString(), type: 'SYSTEM', recipient, subject, body, status: 'SENT', platform: 'Gmail' }); setNotifications(prev => [notif, ...prev]); }; // Navigate & Tailor const handleNavigateToWalkthrough = (jobUrl?: string) => { if (jobUrl) setWalkthroughInitialUrl(jobUrl); setActiveTab('walkthrough'); }; return (
{/* Top Navigation */} setIsNewAppModalOpen(true)} onRunAutoApplyNow={handleRunAutoApplyBatch} isAutoApplying={isAutoApplying} /> {/* Main Container */}
{/* Left Sidebar */} ['Applied', 'Screening'].includes(a.status)).length} autoAppliedCount={applications.filter(a => a.autoApplied).length} /> {/* Main Workspace Area */}
{activeTab === 'applications' && ( setSelectedAppForModal(app)} onOpenNewAppModal={() => setIsNewAppModalOpen(true)} onNavigateToWalkthrough={handleNavigateToWalkthrough} /> )} {activeTab === 'walkthrough' && ( )} {activeTab === 'auto-apply' && ( a.autoApplied)} onSaveProfile={handleSaveUserProfile} onRunAutoApplyBatch={handleRunAutoApplyBatch} isAutoApplying={isAutoApplying} /> )} {activeTab === 'feeds' && ( handleNavigateToWalkthrough(item.url)} onQuickApplyFeed={async (item) => { await handleSaveApplicationRecord({ company: item.company, role: item.title, platform: item.platform, location: item.location, salaryRange: item.salary, status: 'Applied', matchScore: item.estimatedMatch, appliedAt: new Date().toISOString(), jobUrl: item.url, jobDescription: item.description, autoApplied: true, timeline: [{ date: new Date().toLocaleDateString(), action: 'Quick AI Auto-Applied from feed' }] }); }} /> )} {activeTab === 'analytics' && ( )} {activeTab === 'calendar' && ( )} {activeTab === 'notifications' && ( )} {activeTab === 'profile' && ( )} {activeTab === 'workspace' && ( )}
{/* Tailored Resume Inspection Modal */} {selectedAppForModal && ( setSelectedAppForModal(null)} /> )} {/* New Application Modal */} {isNewAppModalOpen && ( setIsNewAppModalOpen(false)} onSaveApplication={handleSaveApplicationRecord} /> )}
); }