/* ============================================================
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;