/* ============================================================ KeS Metas — Backup e Restauração - Cria "pontos de restauração" (cópia dos dados da equipe) no banco - Baixa/!importa cópia em arquivo (.json) — segurança extra offline - Restaura um backup (sobrescreve os dados atuais) — com confirmação - Só o administrador acessa. ============================================================ */ const { useState, useEffect, useRef } = React; // Monta a "fatia" de dados que representa a equipe (igual ao que é salvo no banco). function fatiaDados(state) { const semSenha = (u) => { const c = Object.assign({}, u); delete c.senha; return c; }; return { users: (state.users || []).map(semSenha), metas: state.metas, activities: state.activities || [], meetings: state.meetings || [], stages: state.stages || [], leads: state.leads || [], comissaoEquipe: state.comissaoEquipe || {}, tarefas: state.tarefas || [], }; } function contar(dados) { return { usuarios: (dados.users || []).length, leads: (dados.leads || []).length, reunioes: (dados.meetings || []).length, tarefas: (dados.tarefas || []).length, }; } function fmtData(iso) { if (!iso) return "—"; try { return new Date(iso).toLocaleString("pt-BR"); } catch (e) { return iso; } } function BackupScreen({ state, dispatch, viewer, isOwner }) { const DB = window.KESDB; const isAdmin = !!isOwner; // backup é exclusivo da conta KeS (owner) const [lista, setLista] = useState([]); const [carregando, setCarregando] = useState(true); const [ocupado, setOcupado] = useState(false); const [confirmar, setConfirmar] = useState(null); // { tipo:"restaurar"|"excluir"|"arquivo", ... } const fileRef = useRef(null); async function recarregar() { setCarregando(true); const r = await DB.listBackups(state.teamId); setLista(r); setCarregando(false); } useEffect(() => { if (isAdmin && DB && DB.configured()) recarregar(); else setCarregando(false); }, [state.teamId]); if (!isAdmin) { return

Acesso restrito à conta KeS.

; } if (!DB || !DB.configured()) { return

Backup indisponível: banco de dados não configurado.

; } async function criarBackup() { setOcupado(true); const dados = fatiaDados(state); const r = await DB.createBackup(state.teamId, dados, contar(dados), ""); setOcupado(false); if (r.ok) { dispatch({ type: "TOAST", msg: "Backup criado com sucesso." }); recarregar(); } else dispatch({ type: "TOAST", msg: "Falha ao criar backup." }); } function baixarArquivo() { const dados = fatiaDados(state); const blob = new Blob([JSON.stringify(dados, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); const hoje = window.KES.iso(window.KES.SIM_TODAY); a.href = url; a.download = "kes-backup-" + (state.teamId || "dados") + "-" + hoje + ".json"; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); dispatch({ type: "TOAST", msg: "Cópia baixada." }); } function escolherArquivo() { if (fileRef.current) fileRef.current.click(); } function aoEscolherArquivo(e) { const file = e.target.files && e.target.files[0]; e.target.value = ""; // permite reescolher o mesmo arquivo if (!file) return; const reader = new FileReader(); reader.onload = () => { try { const dados = JSON.parse(reader.result); if (!dados || !Array.isArray(dados.users) || !Array.isArray(dados.leads)) { dispatch({ type: "TOAST", msg: "Arquivo inválido (não parece um backup do KeS)." }); return; } setConfirmar({ tipo: "arquivo", dados, resumo: contar(dados), nome: file.name }); } catch (err) { dispatch({ type: "TOAST", msg: "Não consegui ler o arquivo (JSON inválido)." }); } }; reader.readAsText(file); } // Aplica a restauração: grava no banco e atualiza a tela. async function restaurar(dados) { setOcupado(true); const completo = { users: dados.users || [], metas: dados.metas || window.KES.seed.metas, activities: dados.activities || [], meetings: dados.meetings || [], stages: dados.stages || window.KES.seed.stages, leads: dados.leads || [], comissaoEquipe: dados.comissaoEquipe || window.KES.seed.comissaoEquipe, tarefas: dados.tarefas || [], }; const r = await Promise.resolve(DB.save(completo)).catch(() => ({ ok: false })); if (r && r.ok === false) { setOcupado(false); dispatch({ type: "TOAST", msg: "⚠ Falha ao restaurar: não foi possível gravar no banco. Nada foi alterado." }); return; } dispatch({ type: "HYDRATE", data: completo }); setOcupado(false); dispatch({ type: "TOAST", msg: "Dados restaurados com sucesso." }); } async function confirmarAcao() { const c = confirmar; setConfirmar(null); if (!c) return; setOcupado(true); if (c.tipo === "excluir") { await DB.deleteBackup(c.id); setOcupado(false); dispatch({ type: "TOAST", msg: "Backup excluído." }); recarregar(); } else if (c.tipo === "restaurar") { const doc = await DB.getBackup(c.id); if (doc && doc.dados) await restaurar(doc.dados); // restaurar() libera o ocupado else { setOcupado(false); dispatch({ type: "TOAST", msg: "Não consegui carregar esse backup." }); } } else if (c.tipo === "arquivo") { await restaurar(c.dados); } } const resumoAtual = contar(fatiaDados(state)); return (

Backup e restauração

Crie pontos de restauração dos dados da equipe e recupere se algo for perdido.

{/* Criar backup / arquivo */}
Dados atuais desta equipe
{resumoAtual.usuarios} usuários · {resumoAtual.leads} leads · {resumoAtual.reunioes} reuniões · {resumoAtual.tarefas} tarefas
Criar backup agora Baixar cópia (.json) Restaurar de arquivo
Restaurar substitui os dados atuais da equipe. Faça um backup antes, por segurança.
{/* Lista de backups no banco */}

Backups salvos

{lista.length}
{carregando ? (
Carregando…
) : lista.length === 0 ? (
Nenhum backup ainda. Clique em "Criar backup agora".
) : (
{lista.map((b) => (
{fmtData(b.criadoEm)}
{b.resumo &&
{b.resumo.usuarios} usuários · {b.resumo.leads} leads · {b.resumo.reunioes} reuniões · {b.resumo.tarefas} tarefas
}
setConfirmar({ tipo: "restaurar", id: b.id, criadoEm: b.criadoEm })} disabled={ocupado}>Restaurar
))}
)}
{/* Confirmações */} {confirmar && ( setConfirmar(null)} footer={<> setConfirmar(null)} disabled={ocupado}>Cancelar {confirmar.tipo === "excluir" ? Excluir : {ocupado ? "Restaurando…" : "Restaurar agora"}} }> {confirmar.tipo === "excluir" ? (

Excluir este backup permanentemente? Essa ação não pode ser desfeita.

) : (

{confirmar.tipo === "arquivo" ? <>Restaurar a partir do arquivo {confirmar.nome}? : <>Restaurar o backup de {fmtData(confirmar.criadoEm)}?}

{confirmar.resumo &&

Vai aplicar: {confirmar.resumo.usuarios} usuários · {confirmar.resumo.leads} leads · {confirmar.resumo.reunioes} reuniões · {confirmar.resumo.tarefas} tarefas.

}
Isso substitui TODOS os dados atuais da equipe.
)}
)}
); } window.BackupScreen = BackupScreen;