// ============================================================
// App.jsx — Lógica principal: auth + sheets + navegação
// ============================================================
const App = () => {
// ── Estado ─────────────────────────────────────────────────
const [authState, setAuthState] = React.useState('idle'); // idle | loading | ready | error
const [authError, setAuthError] = React.useState('');
const [user, setUser] = React.useState(() => {
try { return JSON.parse(localStorage.getItem('crm_user') || 'null'); } catch { return null; }
});
const [spreadsheetId, setSpreadsheetId] = React.useState(
() => localStorage.getItem('crm_spreadsheet_id') || null
);
const [leads, setLeads] = React.useState([]);
const [view, setView] = React.useState('dashboard');
const [modal, setModal] = React.useState(null);
const [syncStatus, setSyncStatus] = React.useState('idle'); // idle | saving | saved | error
const [gsiReady, setGsiReady] = React.useState(false);
const saveTimer = React.useRef(null);
// ── Aguardar Google Identity Services carregar ──────────────
React.useEffect(() => {
const check = setInterval(() => {
if (window.google && window.google.accounts) {
setGsiReady(true);
clearInterval(check);
}
}, 100);
return () => clearInterval(check);
}, []);
// ── Tentar login silencioso ao carregar ─────────────────────
React.useEffect(() => {
if (!gsiReady) return;
if (user && spreadsheetId) {
// Usuário já logou antes — tentar renovar token silenciosamente
setAuthState('loading');
Auth.silentSignIn(
(token) => handleAuthSuccess(token, true),
() => setAuthState('idle') // falhou silenciosamente → mostra tela de login
);
}
}, [gsiReady]);
// ── Fluxo pós-autenticação ──────────────────────────────────
async function handleAuthSuccess(token, silent = false) {
setAuthState('loading');
setAuthError('');
try {
// 1. Obter dados do usuário
const userInfo = await Auth.getUserInfo(token);
setUser(userInfo);
localStorage.setItem('crm_user', JSON.stringify(userInfo));
// 2. Verificar / criar planilha
let sheetId = localStorage.getItem('crm_spreadsheet_id');
if (sheetId) {
const exists = await Sheets.spreadsheetExists(token, sheetId);
if (!exists) sheetId = null; // planilha foi deletada → criar nova
}
if (!sheetId) {
sheetId = await Sheets.createSpreadsheet(token, userInfo.name);
localStorage.setItem('crm_spreadsheet_id', sheetId);
}
setSpreadsheetId(sheetId);
// 3. Carregar leads
const loadedLeads = await Sheets.loadLeads(token, sheetId);
setLeads(loadedLeads);
setAuthState('ready');
} catch (err) {
console.error('Erro na inicialização:', err);
setAuthError('Erro ao conectar com o Google. Tente novamente.');
setAuthState('error');
}
}
// ── Login manual ────────────────────────────────────────────
function handleSignIn() {
if (!gsiReady) return;
setAuthState('loading');
Auth.signIn(
(token) => handleAuthSuccess(token, false),
(err) => {
setAuthError('Login cancelado ou recusado. Tente novamente.');
setAuthState('idle');
}
);
}
// ── Logout ──────────────────────────────────────────────────
function handleSignOut() {
Auth.signOut();
setUser(null);
setSpreadsheetId(null);
setLeads([]);
setAuthState('idle');
}
// ── Salvar no Sheets (debounced 800ms) ──────────────────────
function debouncedSave(leadsToSave) {
clearTimeout(saveTimer.current);
setSyncStatus('saving');
saveTimer.current = setTimeout(async () => {
const token = Auth.getToken();
const sheetId = localStorage.getItem('crm_spreadsheet_id');
if (!token || !sheetId) { setSyncStatus('error'); return; }
try {
await Sheets.saveAllLeads(token, sheetId, leadsToSave);
setSyncStatus('saved');
setTimeout(() => setSyncStatus('idle'), 2000);
} catch (err) {
console.error('Erro ao salvar:', err);
setSyncStatus('error');
}
}, 800);
}
// ── Atualizar leads + triggerar save ─────────────────────────
const updateLeads = React.useCallback((updater) => {
setLeads(prev => {
const next = typeof updater === 'function' ? updater(prev) : updater;
debouncedSave(next);
return next;
});
}, []);
// ── CRUD de leads ───────────────────────────────────────────
const openLead = (lead) => setModal(lead);
const newLead = () => setModal({});
const closeModal = () => setModal(null);
const saveLead = (lead) => {
updateLeads(prev => {
const exists = prev.find(l => l.id === lead.id);
if (exists) return prev.map(l => l.id === lead.id ? lead : l);
return [...prev, lead];
});
};
// ── Indicador de sincronização ──────────────────────────────
const syncLabel = { idle: '', saving: '↻ Salvando...', saved: '✓ Salvo', error: '⚠ Erro ao salvar' };
const syncColor = { idle: 'transparent', saving: '#4a4f6a', saved: '#4ade80', error: '#fb923c' };
// ── Render ──────────────────────────────────────────────────
if (authState === 'idle' || authState === 'error') {
return (