CRM 1-a-1← Voltar ao CRM
Documentação técnica

Como o CRM 1-a-1 funciona por dentro

Referência para quem mantém o sistema: arquitetura, arquivos, dados, fluxos e publicação. O que mudou em cada versão está no histórico de versões.

1. Visão geral

O CRM 1-a-1 organiza a prospecção de reuniões individuais (1-a-1) entre membros do BNI. Cada pessoa tem a própria conta e enxerga só os próprios contatos. Com ele é possível:

Novas contas só entram depois de aprovadas por um administrador.

2. Arquitetura

O sistema tem duas partes:

O próprio PocketBase entrega os arquivos da tela, a partir da pasta pública dele. Um proxy HTTPS fica na frente. Tela e API estão no mesmo domínio, então não há problema de CORS.

Navegador ──HTTPS──► proxy ──► PocketBase
                                 ├─ /             arquivos da tela (index.html, css/, js/)
                                 ├─ /api/...      API REST (contas, contatos, arquivos)
                                 └─ banco SQLite  (users, contatos)

O endereço do servidor fica fixo no index.html, no campo oculto pb-url.

3. Estrutura dos arquivos

Os scripts são “clássicos” (não são módulos ES): todos compartilham o escopo global, e os botões chamam as funções direto pelo onclick. A ordem de carregamento importa e é a da tabela abaixo. O inicio.js vem por último porque é o único que executa código ao carregar.

ArquivoResponsabilidadePrincipais funções
index.htmlEstrutura das telas (entrada e app) e de todos os modais—
css/estilo.cssTodo o visual. Cores como variáveis em :root—
js/config.jsConstantes, contatos de exemplo, etapas do funil, mensagem padrão, sugestões de observação e o objeto estadolerObsModelos
js/utils.jsUtilidades gerais$, esc, toast, mostrarCarregando, hojeISO, formatarData, situacaoPrazo, copiarTexto, estadoParaSigla
js/api.jsComunicação com o PocketBase, cadastro, login e sessãopb, entrar, cadastrar, carregarContatosPB, salvarSessao
js/conta.jsMensagem padrão do WhatsApp, menu da conta, foto/logo e aprovações (admin)montarMensagem, linkWhatsApp, alterarAvatar, aprovarUsuario, recusarUsuario
js/importacao.jsImportação por CSV, por planilha Excel e dos contatos de exemploparseCSV, importarMeusContatos, lerPlanilhaExcel, autoMapearColunas, importarDaPlanilha
js/contatos.jsLista, busca, filtros, funil, radar, paginação, seleção em massa, criação, edição, exclusão e backuprenderizar, filtrados, renderizarLista, renderizarRadar, salvarContato, confirmarExclusaoEmMassa
js/visualizacao.jsFicha do contato, estrelas, campos que mudam conforme a etapa, sugestões de observaçãoabrirVisualizacao, salvarRapido, atualizarBlocosStatus, marcarContatado
js/painel.jsPainel “Meus 1-a-1”renderizarPainel11
js/rotas.jsLinks diretos: cada janela aberta escreve o endereço depois do #, e abrir o endereço vai direto para eladefinirRota, limparRota, aplicarRota, copiarLinkContato
js/agenda.jsIntegração com o UM Planner: conexão (Minha agenda) e envio do 1-a-1 marcado como tarefacarregarAgenda, salvarAgenda, enviarParaAgenda, tarefaDaAgenda, assinaturaAgenda, assinaturaAgendaAntiga, desenharInfoAgenda
js/inicio.jsAtalho Esc, animação da tela de entrada e retomada da sessão ao abrir a páginacalcularStats, salvarUltimasStats
versoes.htmlHistórico de versões—
documentacao.htmlEsta página—

4. Modelo de dados

Coleção users (contas)

É a coleção de autenticação padrão do PocketBase, com estes campos a mais:

CampoTipoUso
nametextoNome informado no cadastro
emaile-mailLogin
avatararquivoFoto ou logo exibida no topo
aprovadobooleanoConta liberada pelo admin. Sem isso, o login para na tela “Aguardando aprovação”
adminbooleanoMostra o menu “Aprovações pendentes”
mensagem_padraotextoModelo da mensagem do WhatsApp. Vazio = usa o padrão do sistema
obs_modelostexto (JSON)Sugestões de observação personalizadas. Vazio = usa as três padrão

Formato de cada item de obs_modelos:

{"id":"boa", "rotulo":"Conversa produtiva", "classe":"obs-boa", "estrelas":5, "texto":"... {nome} ..."}

Coleção contatos

CampoTipoUso
ownerrelação → usersDono do contato. Sempre a conta logada
nometexto, obrigatório—
empresa, grupo, cidadetextogrupo é o capítulo do BNI
estadotextoSigla da UF (RS, SC…)
categoriatextoÁreas de atuação separadas por |. Subnível depois de > (só a parte antes do > vira opção no filtro)
telefonetextoFormato livre
whatsapptextoSó números, com 55 e DDD (ex.: 5551999998888)
perfiltexto/URLLink do perfil no site do BNI
statusseleçãoEtapa do funil (tabela abaixo)
proximo_contatodataPróximo follow-up. Na etapa “1-a-1 marcado”, é a data da reunião
reuniao_tipotextoonline ou presencial
reuniao_localtextoURL da reunião ou endereço
reuniao_horatexto HH:MMHorário do 1-a-1 (v38)
agenda_assinaturatextoO que já foi mandado para a agenda do UM Planner (título, data, horário, formato, local e, desde a v41, o WhatsApp). Serve para só reenviar quando algo da reunião mudou (v38)
classificacaonúmero 0–5Nota do 1-a-1 concluído (0 = sem nota)
obstexto longoObservações

Coleção agenda_conexoes (v38)

Uma linha por pessoa, com a chave de integração do UM Planner. É uma coleção própria, com regra de dono, e não um campo em users: assim a chave não fica legível para outra conta.

CampoTipoUso
ownerrelação → users (única)Dono da conexão
chavetextoChave ump_… gerada no UM Planner
contatextoNome da conta do UM Planner, devolvido no teste da chave, só para mostrar

Regras: listar, ver, editar e excluir só o dono (owner = @request.auth.id); criar só com owner igual à própria conta. O script de setup do banco cria a coleção já com essas regras.

Etapas do funil (status)

ValorRótuloObservação
a_contatarA contatarEtapa inicial e valor usado quando a importação traz uma etapa inválida
contatadoContatadoDefinida sozinha ao clicar em WhatsApp num contato “A contatar”
conversandoConversando—
reuniao1-a-1 marcadoMostra formato e local da reunião
concluido1-a-1 concluídoMostra a classificação por estrelas
parceiroParceiro / clienteFica fora do radar
sem_interesseSem interesseFica fora do radar

Para criar uma etapa nova: acrescentar em STATUS (config.js), nos dois <select> de etapa do index.html, numa classe .tag-<valor> do CSS e nas opções do campo status no PocketBase.

5. Regras de acesso no servidor

A tela esconde o que cada pessoa não deve ver, mas quem garante a segurança é o servidor. Toda regra abaixo precisa estar configurada nas “API Rules” de cada coleção no PocketBase. Uma regra que existe só na tela pode ser contornada por qualquer pessoa com um navegador.
Coleção / açãoO que o servidor deve garantir
contatos: listar, ver, editar, excluirSó o dono, e só com conta aprovada: owner = @request.auth.id && @request.auth.aprovado = true
contatos: criarConta aprovada, e o owner enviado tem que ser a própria conta: @request.auth.aprovado = true && @request.body.owner = @request.auth.id
users: criar (cadastro)Aberto a visitantes, mas sem poder definir aprovado nem admin
users: editarA própria conta pode mudar nome, foto, mensagem e sugestões, mas não aprovado nem admin. Admin pode aprovar outras contas
users: listar pendentes e excluirSó admin (@request.auth.admin = true)
agenda_conexoes: tudoSó o dono. As regras já são criadas assim pelo script de setup do banco (v38)

6. Fluxos principais

6.1 Cadastro e aprovação

  1. A pessoa informa nome, e-mail e senha (mínimo de 8 caracteres). A conta é criada com aprovado = false.
  2. A tela mostra “Aguardando aprovação”.
  3. O admin abre menu → Aprovações pendentes e escolhe Aprovar (aprovado = true) ou Recusar (a conta é excluída).
  4. A partir daí, o login funciona normalmente.

6.2 Login e sessão

6.3 Lista de contatos

6.4 Radar de follow-up

6.5 WhatsApp e mensagem padrão

O botão abre https://wa.me/<whatsapp>?text=<mensagem>. A mensagem sai do modelo da conta (ou do padrão do sistema), com estes campos trocados pelos dados do contato:

CampoVira
{primeiro_nome}Primeira palavra do nome
{nome}Nome completo
{empresa}, {grupo}, {cidade}O campo correspondente
{categoria}Só a primeira área da categoria

Quando o contato não tem o dado, o trecho da linha em volta do campo some junto. Assim não sobra “Pelo que vi, você está no grupo de .”. Depois disso, espaços e linhas em branco repetidos são limpos.

Clicar em WhatsApp num contato “A contatar” move o contato para “Contatado” automaticamente.

6.6 Ficha do contato

6.7 Importação

Por CSV. A tela traz um prompt pronto para levar a uma IA junto com a lista bagunçada. A IA devolve um CSV com estas colunas:

nome,empresa,grupo,cidade,estado,categoria,telefone,whatsapp,perfil,status,proximo_contato,obs

Separador é vírgula, e texto com vírgula vai entre aspas duplas. Linhas sem nome são ignoradas. Na importação:

Por planilha Excel (.xlsx/.xls). O arquivo é lido no próprio navegador, pela biblioteca SheetJS.

Contatos de exemplo. Há três contatos de teste embutidos no código. A importação deles pula os que já existem (compara pelo WhatsApp ou, sem WhatsApp, pelo nome).

Todas as importações gravam um contato por requisição, em sequência, e no fim mostram quantos entraram e quantos deram erro.

6.8 Exclusão em massa

Em ☑ Selecionar, os cards viram caixas de seleção, e dá para marcar a página inteira. Para confirmar, é preciso digitar EXCLUIR. Cada contato é excluído com uma requisição própria.

6.9 Painel “Meus 1-a-1”

6.10 Agenda no UM Planner (v38)

Conectar:

  1. No UM Planner, a pessoa gera uma chave de integração em Configurações → IA e integrações → Integrações.
  2. No CRM, cola a chave em menu → Minha agenda (UM Planner).
  3. “Testar e salvar” confere a chave no UM Planner (teste: true) antes de guardar em agenda_conexoes. A conexão é carregada no login e na retomada da sessão (carregarAgenda).

Enviar: depois de salvar na ficha (salvarRapido) ou em “Editar tudo” (salvarContato), enviarParaAgenda manda o contato quando:

A chamada vai direto do navegador para o UM Planner:

POST https://umplanner.com/api/umplanner/receber-tarefa
{ "chave": "ump_…", "origem": "crm-bni", "idExterno": "<id do contato>",
  "titulo": "1-a-1: Nome (Empresa)", "data": "AAAA-MM-DD", "horario": "HH:MM",
  "formato": "online|presencial", "endereco": "…", "url": "https://…",
  "anotacoes": "link da ficha no CRM, contato, empresa, telefone, WhatsApp (wa.me), grupo, cidade, perfil, observações",
  "clienteNome": "Nome", "clienteWhatsapp": "5551999998888", "soAtualizar": true|ausente }

6.11 Links diretos (v39)

Toda janela aberta escreve o seu endereço depois do #. Copiar esse endereço (ou usar o botão 🔗 Link da ficha) e abrir depois vai direto para a janela.

EndereçoAbre
#contato/<id>Ficha do contato
#editar/<id>Edição completa (“Editar tudo”)
#novoNovo contato
#meus-1a1Painel Meus 1-a-1
#agendaMinha agenda (UM Planner)
#mensagemMensagem padrão do WhatsApp
#importarImportar contatos
#aprovacoesAprovações pendentes (só admin)

6.12 Backup

menu → Backup baixa todos os contatos da conta num arquivo prospeccao-bni-backup-AAAA-MM-DD.json. Hoje não existe importação desse arquivo pela tela.

7. Chamadas à API

Todas passam pela função pb() (api.js), que envia JSON e o cabeçalho Authorization com o token da sessão. Qualquer resposta de erro vira uma exceção com a mensagem do PocketBase.

MétodoCaminhoUso
POST/api/collections/users/recordsCadastro
POST/api/collections/users/auth-with-passwordLogin
POST/api/collections/users/auth-refreshRetomar a sessão ao abrir a página
PATCH/api/collections/users/records/:idMensagem padrão, sugestões, foto (multipart, campo avatar) e aprovação pelo admin
GET/api/collections/users/records?filter=aprovado=falseContas pendentes (admin)
DELETE/api/collections/users/records/:idRecusar cadastro (admin)
GET/api/files/users/:id/:arquivoExibir a foto da conta
GET/api/collections/contatos/records?perPage=500&page=N&sort=-updatedCarregar todos os contatos
POST/api/collections/contatos/recordsCriar contato e importações
PATCH/api/collections/contatos/records/:idSalvar, mudar etapa, descartar data
DELETE/api/collections/contatos/records/:idExcluir (individual e em massa)
GET/POST/PATCH/DELETE/api/collections/agenda_conexoes/recordsConexão com a agenda do UM Planner
POSThttps://umplanner.com/api/umplanner/receber-tarefaOutro sistema: testar a chave e criar/atualizar a tarefa do 1-a-1 (não passa por pb(), e sim por chamarUMPlanner)

9. Dependências externas

Não há npm, bundler nem outra dependência de build.

10. Versões e publicação

O número da versão é inteiro e sobe de um em um. Ele aparece no rodapé do CRM e fica marcado de três formas no index.html:

Passo a passo de uma versão nova

  1. Fazer as mudanças em app/ e testar abrindo app/index.html no navegador. Ele conversa com o servidor de verdade.
  2. Registrar a versão em versoes.html e, se mudou algo técnico, atualizar esta documentação e o número no comentário do topo dela.
  3. Rodar empacotar.ps1 -Versao NN no Windows. Ele:
    • carimba a versão;
    • confere que todo arquivo pedido pelo index.html existe;
    • recusa publicar se estas páginas públicas contiverem detalhes de infraestrutura;
    • gera a lista de conferência SHA-256 e o pacote crm_bni_vNN.zip.
  4. No servidor, rodar o script de deploy. Ele:
    • busca o pacote de número mais alto e confere a integridade de cada arquivo;
    • guarda uma cópia do que estava no ar;
    • publica só os arquivos da lista;
    • mantém as últimas 4 versões;
    • confirma que o site responde com a versão nova.

Versão que muda o banco (campo ou coleção nova, como a v38): depois do deploy, rodar o script de setup do banco (setup-bni.sh). Ele chega junto no pacote, pede a senha do superusuário e só acrescenta o que falta. Rodar de novo é seguro.

Voltar uma versão: rodar o script de deploy com o número desejado (ex.: deploy-bni.sh 36).

Esta página e o histórico de versões ficam públicos, sem login. Por isso não contêm endereços internos, caminhos do servidor, credenciais nem links de compartilhamento, e o empacotador bloqueia a publicação se algum desses aparecer aqui.

11. Como manter o código

12. Limitações conhecidas

Documentação da versão 41 · Histórico de versões · Voltar ao CRM