import React, { useState, useEffect } from 'react'; import { FileSpreadsheet, FileText, FolderGit2, StickyNote, ExternalLink, CheckCircle2, CloudUpload, RefreshCw, Sparkles, Plus, Trash2, Lock, Download, Search, Check, Zap, Globe } from 'lucide-react'; import { JobApplication } from '../types'; import { googleWorkspaceSignIn, googleSignOut, exportApplicationsToGoogleSheets, createTailoredGoogleDoc, uploadFileToGoogleDrive, listGoogleDriveWorkspaceFiles, saveGoogleKeepNote, getCachedAccessToken } from '../lib/googleAuth'; interface GoogleWorkspaceHubProps { applications: JobApplication[]; userEmail?: string; userName?: string; } export const GoogleWorkspaceHub: React.FC = ({ applications, userEmail, userName }) => { const [isConnected, setIsConnected] = useState(false); const [accessToken, setAccessToken] = useState(null); const [connectedUser, setConnectedUser] = useState<{ email?: string; name?: string }>({ email: userEmail || 'user@gmail.com', name: userName || 'Job Applicant' }); // State for Google Sheets const [isExportingSheets, setIsExportingSheets] = useState(false); const [lastSheetExport, setLastSheetExport] = useState<{ url: string; time: string } | null>(null); // State for Google Docs const [docType, setDocType] = useState<'resume' | 'coverLetter'>('resume'); const [docTitle, setDocTitle] = useState(''); const [docContent, setDocContent] = useState(''); const [isCreatingDoc, setIsCreatingDoc] = useState(false); const [generatedDocs, setGeneratedDocs] = useState>([]); // State for Google Drive const [driveFiles, setDriveFiles] = useState([]); const [isLoadingDrive, setIsLoadingDrive] = useState(false); const [driveSearch, setDriveSearch] = useState(''); const [uploadFileName, setUploadFileName] = useState(''); const [uploadFileContent, setUploadFileContent] = useState(''); const [isUploadingDrive, setIsUploadingDrive] = useState(false); // State for Google Keep const [keepNotes, setKeepNotes] = useState>([ { id: 'k1', title: 'Senior Frontend Tech Interview Prep', content: 'Key focus points: React Fiber architecture, System Design for real-time dashboards, and Google Workspace OAuth flows.', color: 'amber', time: '10 mins ago' }, { id: 'k2', title: 'Recruiter Follow-up - Stripe', content: 'Email Sarah regarding second round schedule. Confirm availability for Thursday 2 PM PST.', color: 'sky', time: '2 hours ago' } ]); const [newNoteTitle, setNewNoteTitle] = useState(''); const [newNoteContent, setNewNoteContent] = useState(''); const [newNoteColor, setNewNoteColor] = useState('amber'); const [isSavingKeepNote, setIsSavingKeepNote] = useState(false); const [notificationMsg, setNotificationMsg] = useState(null); useEffect(() => { const savedConnected = localStorage.getItem('ai_job_portal_google_connected'); const savedToken = getCachedAccessToken() || localStorage.getItem('ai_job_portal_google_token'); const savedUser = localStorage.getItem('ai_job_portal_google_user'); if (savedConnected === 'true') { setIsConnected(true); if (savedToken) setAccessToken(savedToken); if (savedUser) { try { setConnectedUser(JSON.parse(savedUser)); } catch (e) {} } loadDriveFiles(savedToken || undefined); } }, []); const [showTokenInputModal, setShowTokenInputModal] = useState(false); const [customTokenInput, setCustomTokenInput] = useState(''); const handleConnectGoogle = async () => { try { const res = await googleWorkspaceSignIn(); if (res) { setIsConnected(true); setAccessToken(res.accessToken); setConnectedUser({ email: res.user.email || 'dkifer19@gmail.com', name: res.user.displayName || 'David Kifer' }); localStorage.setItem('ai_job_portal_google_connected', 'true'); localStorage.setItem('ai_job_portal_google_token', res.accessToken); localStorage.setItem('ai_job_portal_google_user', JSON.stringify({ email: res.user.email || 'dkifer19@gmail.com', name: res.user.displayName || 'David Kifer' })); showToast('✓ Google Workspace connected successfully!'); loadDriveFiles(res.accessToken); return; } } catch (err: any) { console.error("Google Auth Popup Exception:", err); const msg = err?.message || String(err); if (msg.includes('unauthorized-domain')) { showToast(`Domain Error: http://localhost:3000 must be added to Firebase Console -> Authentication -> Settings -> Authorized Domains.`); } else { showToast(`Google Sign-In Error: ${msg}`); } setShowTokenInputModal(true); return; } }; const handleApplyCustomToken = () => { if (customTokenInput.trim()) { setAccessToken(customTokenInput.trim()); setIsConnected(true); setConnectedUser({ email: 'dkifer19@gmail.com', name: 'David Kifer' }); showToast('✓ Custom Google OAuth Access Token connected!'); loadDriveFiles(customTokenInput.trim()); } else { setIsConnected(true); setConnectedUser({ email: 'dkifer19@gmail.com', name: 'David Kifer (Sandbox Mode)' }); showToast('Connected in Offline Workspace Sandbox Mode.'); } setShowTokenInputModal(false); }; const handleDisconnectGoogle = async () => { await googleSignOut(); setIsConnected(false); setAccessToken(null); localStorage.removeItem('ai_job_portal_google_connected'); localStorage.removeItem('ai_job_portal_google_token'); localStorage.removeItem('ai_job_portal_google_user'); showToast('Disconnected Google Workspace.'); }; const showToast = (msg: string) => { setNotificationMsg(msg); setTimeout(() => setNotificationMsg(null), 4000); }; // 1. Google Sheets Handler const handleExportToSheets = async () => { setIsExportingSheets(true); try { const res = await exportApplicationsToGoogleSheets(applications, accessToken || undefined); setLastSheetExport({ url: res.spreadsheetUrl, time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) }); if (res.isLocalCsv && res.spreadsheetUrl) { const link = document.createElement('a'); link.href = res.spreadsheetUrl; link.download = `AI_Job_Applications_Tracker_${new Date().toISOString().split('T')[0]}.csv`; document.body.appendChild(link); link.click(); document.body.removeChild(link); showToast('✓ Applications exported & downloaded to Spreadsheet CSV file!'); } else { showToast(res.message || 'Exported applications to Google Sheets!'); } } catch (err: any) { showToast(`Sheets Export: ${err.message || 'Prepared export file'}`); } finally { setIsExportingSheets(false); } }; // 2. Google Docs Handler const handleCreateGoogleDoc = async (e: React.FormEvent) => { e.preventDefault(); if (!docTitle.trim() || !docContent.trim()) return; setIsCreatingDoc(true); try { const res = await createTailoredGoogleDoc( docTitle, docContent, docType, accessToken || undefined ); if (res.isLocalDoc && res.documentUrl) { const link = document.createElement('a'); link.href = res.documentUrl; link.download = `${docTitle.replace(/[^a-z0-9_-]/gi, '_')}.html`; document.body.appendChild(link); link.click(); document.body.removeChild(link); showToast('✓ Document compiled & downloaded to local file!'); } else { showToast('Tailored document exported to Google Docs!'); } setGeneratedDocs(prev => [ { title: docTitle, url: res.documentUrl, time: 'Just now' }, ...prev ]); setDocTitle(''); setDocContent(''); } catch (err: any) { showToast(`Google Docs: ${err.message}`); } finally { setIsCreatingDoc(false); } }; // 3. Google Drive Handlers const loadDriveFiles = async (token?: string) => { setIsLoadingDrive(true); try { const files = await listGoogleDriveWorkspaceFiles(token || accessToken || undefined); setDriveFiles(files); } catch (err) { console.warn('Drive files load error:', err); } finally { setIsLoadingDrive(false); } }; const handleUploadDriveFile = async (e: React.FormEvent) => { e.preventDefault(); if (!uploadFileName.trim()) return; setIsUploadingDrive(true); try { const res = await uploadFileToGoogleDrive( uploadFileName, uploadFileContent, 'text/plain', accessToken || undefined ); if (res.isLocalFile && res.driveUrl) { const link = document.createElement('a'); link.href = res.driveUrl; link.download = uploadFileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); showToast(`✓ ${uploadFileName} saved & downloaded to local workspace!`); } else { showToast(`✓ ${uploadFileName} uploaded to Google Drive!`); } setDriveFiles(prev => [ { id: res.fileId, name: uploadFileName, webViewLink: res.driveUrl, createdTime: new Date().toISOString() }, ...prev ]); setUploadFileName(''); setUploadFileContent(''); } catch (err: any) { showToast(`Drive upload: ${err.message}`); } finally { setIsUploadingDrive(false); } }; // 4. Google Keep Note Handlers const handleSaveKeepNote = async (e: React.FormEvent) => { e.preventDefault(); if (!newNoteTitle.trim() || !newNoteContent.trim()) return; setIsSavingKeepNote(true); try { const note = await saveGoogleKeepNote( newNoteTitle, newNoteContent, newNoteColor, accessToken || undefined ); setKeepNotes(prev => [ { id: note.noteId, title: note.title, content: note.content, color: note.color, time: 'Just now' }, ...prev ]); setNewNoteTitle(''); setNewNoteContent(''); showToast('Note synced with Google Keep!'); } catch (err: any) { showToast(`Keep note: ${err.message}`); } finally { setIsSavingKeepNote(false); } }; const colorClasses: Record = { amber: 'bg-amber-500/10 border-amber-500/30 text-amber-300', sky: 'bg-sky-500/10 border-sky-500/30 text-sky-300', emerald: 'bg-emerald-500/10 border-emerald-500/30 text-emerald-300', purple: 'bg-purple-500/10 border-purple-500/30 text-purple-300', rose: 'bg-rose-500/10 border-rose-500/30 text-rose-300' }; return (
{/* Toast Notification */} {notificationMsg && (
{notificationMsg}
)} {/* Header Banner */}
Native Google Workspace OAuth Integration

Google Workspace Hub

Seamlessly sync application tracking data with Google Sheets, save tailored resumes & cover letters to Google Docs, manage application files in Google Drive, and capture interview notes with Google Keep.

{/* Account Status Card */}
{isConnected ? 'Workspace Connected' : 'Authorization Required'}
{isConnected ? (
{connectedUser.name}
{connectedUser.email}
) : (

Enable automatic sync for Sheets, Drive, Docs & Keep.

)}
{/* Grid Section: 4 Apps */}
{/* APP 1: GOOGLE SHEETS */}

Google Sheets Tracker

Sync all job submissions to structured spreadsheet

v4 REST API

Instantly export your live pipeline ({applications.length} applications) into a formatted Google Sheet with frozen headers, automatic status columns, and match scores.

Active Tracked Records {applications.length} Jobs
{lastSheetExport && (
Exported at {lastSheetExport.time}
Open in Sheets
)}
{/* APP 2: GOOGLE DOCS */}

Google Docs Generator

Export tailored resumes & cover letters to Docs

Docs API
setDocTitle(e.target.value)} className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3.5 py-2 text-xs text-slate-100 placeholder-slate-500 focus:outline-none focus:border-blue-500" />