# SouMulherAgenda - Notas do Projeto

## Arquitetura
- CodeIgniter 4, multi-tenant (saloes de beleza)
- XAMPP (MySQL, PHP), sem `spark` CLI
- Rodar SQL: `D:/xampp/mysql/bin/mysql.exe -u root soumulheragenda < file.sql`
- Tabela `usuarios` com campo `level`: 1=Admin, 2=Dono do salao, 3=Coordenador, 4=Profissional
- Multi-tenancy via `user_id` em todas as tabelas
- Helper `permission` auto-carregado via Autoload.php

## Usuarios Admin Conhecidos (Servidor souhub.com.br)
- ID 1: teste@souhub.com.br (Level 1, Admin)
- Locais: ID 32 jesiel_webmaster@hotmail.com, ID 37 zieltelles@gmail.com

## Deploy
- Apenas souhubcom (usuario pediu para NÃO deployar no soumulhe)
- SSH: root@server.whmservidor.com (senha YfJo6DxTfxJpoJ1)
- Path: /home/souhubcom/public_html/
- DB: souhubcom_sistema, user souhubcom_root, pass 3KgqH$%to9Wa
- WinSCP CLI via deploy.bat + deploy_script.txt
- Email SMTP: zieltelles@gmail.com via Gmail (app/Config/Email.php)

## Bugs Corrigidos (2026-02-23)
- UserModel.setDefaults forçava status=0 em todo insert (impedia login de empresas criadas pelo admin)
- EsqueceuSenha.php usava setFrom errado (contato@soumulheragenda.com.br) - removido para usar Email.php config

## Sistema Admin (Gestao do Sistema)
- Admin tem painel separado em `/admin/empresas` - NAO deve ver menus de cliente
- Modo suporte: admin troca `user_id` na sessao para ver ambiente do cliente
- Flag `modo_suporte` na sessao controla quando admin esta acessando como cliente
- Condicao usada em menus/views: `(!isAdmin() || session()->get('modo_suporte'))`

## Tabelas Admin Panel (criadas 2026-02-17)
- `planos` - Planos de assinatura (Bronze/Prata/Ouro/Diamante)
- `faturas` - Cobranças mensais por empresa
- `tickets_suporte` - Tickets de suporte
- `ticket_respostas` - Respostas dos tickets (chat-style)
- `admin_config` - Configurações globais (chave/valor)

## Padroes de Views
- Bootstrap 5 + Tailwind CDN (global via menu_admin.php, preflight:false) + Font Awesome 6 + SweetAlert2 + DataTables PT-BR
- AJAX: usar FormData + fetch (NAO JSON.stringify com Content-Type json)
- MensagemModel campos: user_id, titulo_acao, mensagem, tag, lida
- WhatsApp: Baileys API (`Divulgueregional\ConsumirApiBaileys\Baileys`)
- Cards padrão: Tailwind grid (bg-white rounded-lg shadow-sm border p-4, ícone text-2xl à esquerda, texto uppercase tracking-wider, número text-xl font-bold)
- Usar ícones Font Awesome (fas fa-*) nos botões, NÃO feather (fe fe-*) que ficam invisíveis em btn-outline

## Deploy via PuTTY (pscp + plink)
- PuTTY plink (comandos SSH): `"/c/Program Files/PuTTY/plink.exe" -ssh root@server.whmservidor.com -pw "YfJo6DxTfxJpoJ1" -batch "comando"`
- PuTTY pscp (upload arquivos): `"/c/Program Files/PuTTY/pscp.exe" -pw "YfJo6DxTfxJpoJ1" -batch "LOCAL" root@server.whmservidor.com:/home/souhubcom/public_html/REMOTE`
- NÃO usar WinSCP - preferir pscp direto (sem temp files)
- [Detalhes](feedback_deploy_pscp.md)

## Campos Adicionados (2026-03-20)
- `planos.recursos` (TEXT, JSON) - recursos inclusos no plano
- `usuarios.data_teste_fim` (DATE, NULL) - data fim do teste da empresa

## Arquivos-Chave
- `app/Controllers/Admin.php` - Painel admin completo (faturas, suporte, mensagens, config, teste empresas)
- `app/Views/include/menu_admin.php` - Menu lateral (~4100 linhas, Tailwind global, condicional admin/cliente)
- `app/Helpers/permission_helper.php` - isAdmin(), canAccess(), etc.
- `app/Config/Routes.php` - Rotas do sistema

## Sessão 2026-03-20
- [Detalhes completos](project_sessao_2026-03-20.md)

## Preferencias do Usuario
- [Salvar memoria apos conclusao](feedback_salvar_memoria.md) — Sempre atualizar memoria ao finalizar tarefas

## Alteracoes Recentes (2026-04-15)
- Barra de pesquisa em `app/Views/admin/empresas.php` (filtro nome/email, plano, status) - JS usa `getColIndex()` pelo header
- Campos `negociacao` (DECIMAL) e `recursos_liberados` (TEXT/JSON) adicionados na tabela `usuarios`
- Modal Nova Empresa: campo valor negociado + checkboxes de recursos (agendamento, financeiro, clientes, colaboradores, whatsapp, relatorios, produtos, imagens)
- Tabelas Ativas/Inativas agora mostram colunas Negociacao e Recursos
- Pagina de Relatorios Admin criada: `admin/relatorios` → `app/Views/admin/relatorios_admin.php`
  - Visao geral empresas, numeros do sistema, financeiro, receita potencial, distribuicao por plano, grafico ultimos 6 meses
  - Botao imprimir (print-friendly)
- Tabelas empresas: removido Negociacao/Recursos manuais, trocado por "Liberado no Plano" (mostra max colab/clientes do plano)
- Correcoes salvamento site cliente:
  - **BUG RAIZ**: `base_url()` retorna com `/` final, causando barra dupla `//site/salvarTexto` → nginx retornava 400
  - Fix: `baseUrl` JS usa `rtrim(base_url(),'/')`, URLs PHP usam `base_url('site/')`
  - TextoModel/CoresModel: adicionado `created_at`, `updated_at` ao allowedFields
  - Site::salvarTexto: permite conteudo vazio
  - Site::resetarLayout: usa query builder direto para delete
  - Todos os saves envolvidos com try/catch
  - resetarLayout agora reseta tambem: imagens (banner/logo/favicon), fonte_site, menus nav/footer
- Relatorios Admin refeito profissional: filtro por empresa, tabela detalhada com DataTables, graficos Chart.js, faturas e tickets recentes
- Checkout landing page: opcoes de pagamento ampliadas
  - Pagar Total ou Parcial (minimo 50%, restante combinar na entrega)
  - PIX + Cartao (combinar dois meios, campo de valor para cada)
  - Combinar na Entrega (pagar no atendimento)
  - WhatsApp (combinar pagamento)
  - Mensagem WhatsApp inclui forma de pagamento, valor parcial e combo
  - Validacao: campo parcial impede valor < 50% (blur + processarCheckout)
  - PIX e Cartao usam valor parcial quando selecionado (envia ao Mercado Pago da empresa)
  - Entrega: salva agendamento como Pendente sem processamento de pagamento
  - Cada empresa usa seu proprio Mercado Pago (config em mercadopago_config por user_id)
  - **BUG CRITICO CORRIGIDO**: JS enviava campos 'nome','telefone','email' mas backend esperava 'nome_cliente','telefone_cliente','email_cliente' → 100% dos agendamentos falhavam silenciosamente
  - Adicionado tratamento de erro nas respostas do fetch (verifica result.success)
  - Timezone: servidor esta em UTC, PHP nativo retornava hora errada. Corrigido com `date_default_timezone_set('America/Sao_Paulo')` em getHorariosDisponiveis e salvarAgendamento. Horarios passados agora bloqueados corretamente
- Icones `fe fe-*` (Feather) trocados por `fas fa-*` (Font Awesome) nos botoes de acao da tabela agendamentos-listar.php (edit, trash, eye, check, x) - eliminado quadrado preto
- Opcoes de pagamento no checkout: layout grid 3 colunas (lado a lado) em vez de lista vertical
- SweetAlert travando: corrigido fechando modal primeiro (limparCheckout) e mostrando Swal após 300ms delay
- Produtos do carrinho agora salvos como vendas: novo endpoint `site/salvarVendaProduto` cria venda + venda_produto + vincula/cria cliente
- Funcao unificada `salvarAgendamentosDoCarrinhoComStatus` agora salva servicos E produtos
- Agendamentos e vendas de produtos agora vinculados a conta do cliente (contas_cliente + itens_conta)
  - salvarAgendamento: busca/cria conta aberta → vincula horario com conta_id → adiciona item_conta tipo Agendamento
  - salvarVendaProduto: busca/cria conta aberta → cria venda com conta_id → adiciona item_conta tipo Venda
  - Produtos agora aparecem na aba Produtos da conta do cliente
- Promocoes corrigidas na landing page:
  - Servico com promo: calcula preco com desconto (porcentagem ou valor fixo) e passa pro modal de agendamento
  - Produto com promo: botao "Comprar com Desconto" adiciona direto ao carrinho com preco promocional
  - Nome do item inclui "(PROMO)" para identificar
  - Descricao mostra "De R$ X por R$ Y"
  - tipo_desconto 'valor': campo valor = preco final do combo (NAO desconto a subtrair)
  - tipo_desconto 'porcentagem': calcula desconto % sobre preco original
  - tipo_aplicacao 'pacote': combo servico+produto com preco fixo
  - Campo 'produtos' pode ser JSON array, ID simples, ou NULL (IDs podem estar em 'servicos')
  - Helper extrairIds() trata todos os formatos
- Modal Nova Empresa: olhinho (toggle mostrar/esconder senha) no campo senha
- Relatorio clientes: removido campo "Buscar" por texto, mantido filtros de data + botao "Filtrar"
- Deploy feito no servidor souhub.com.br

## Obsidian Vault
- Vault path: `D:\xampp\htdocs\mente-claude\Mente do Claude`
- Nota do projeto SouHub criada em `Projetos/SouHub.md` (2026-04-15)

## Esquema de tabelas (importante)
- Tabela `faturas` usa coluna `user_id` (NAO `empresa_id`) - chave para empresa dona
- Tabela `tickets_suporte` usa `user_id`
- Sempre verificar nome da coluna antes de usar `empresa_id` em queries de faturas/tickets
- Tabela `vendas` (servidor souhub.com.br): tem `desconto DECIMAL(10,2)` e `valor_final DECIMAL(10,2)` adicionados em 2026-04-27 (estavam ausentes em prod, presentes no VendaModel allowedFields). VendasAgendamentos::criarVenda usa ambos no insert

## Bug Corrigido (2026-04-27)
- `Admin::relatorios()` quebrava com "Unknown column 'empresa_id' in WHERE" porque queries de faturas usavam `empresa_id` em vez de `user_id` (8 ocorrencias entre linhas 1714-1793 do Admin.php)
- Fix aplicado e deploy feito no souhub.com.br

## Indisponibilidade Colaborador - bloqueio preciso (2026-04-27)
- Comportamento final: colaborador SEMPRE aparece na lista de selecao mesmo quando 'indisponivel' ou 'almoco'. Apenas o INTERVALO INFORMADO fica bloqueado (cinza/Ocupado) no modal de horarios
- Auto-reset mantido: `getColaboradoresByServico` (VendasAgendamentos.php + Colaborador.php) limpa indisponibilidades expiradas antes de listar
- `getHorariosDisponiveis` agora calcula sobreposicao precisa:
  - $inicioReal = max(NOW(), inicio_do_dia_selecionado)
  - $fimReal = min(tempo_indisponibilidade, fim_do_dia_selecionado)
  - se inicio < fim → bloqueia somente esse intervalo (nao bloqueia dia inteiro como antes)
- Para data futura dentro do periodo: bloqueia de 00:00 ate hora_fim_indisp do dia (e nao 00:00-23:59)
- Para data futura fora do periodo: nao bloqueia nada
- Deploy feito no souhub.com.br

## Bug Corrigido - Logica Ocupado/Agendado (2026-04-27)
- `VendasAgendamentos.php` (calcularIntervalosOcupados + separarHorarios) marcava ocupado em SLOTS de 30min alinhados (`for time<fim, step 1800s`). Falhava quando agendamento existente nao alinhava (ex: 13:45+30min) ou tinha duracao nao multipla de 30 - permitia conflitos reais passarem como "disponivel" e gerava falsos "ocupado"
- Fix: `calcularIntervalosOcupados` agora retorna lista de pares `{start_ts, end_ts}` (timestamps reais, sem arredondamento). `separarHorarios` usa formula de sobreposicao de intervalos: `novo_inicio < ocup_fim && novo_fim > ocup_inicio`
- Mesma formula aplicada ao bloqueio por indisponibilidade (almoco/indisponivel) - antes tinha 2 ifs redundantes, agora 1 unico check de sobreposicao
- Cobre todos casos: alinhados, desalinhados, duracoes 15/45/90 min, encosta sem sobrepor (15:00 logo apos 14:00-15:00 = livre)
- Deploy feito no souhub.com.br

## Bug Corrigido - Filtro Colaboradores por Servico (2026-04-27)
- `VendasAgendamentos::getColaboradoresByServico` recebia `servico_id` mas NAO filtrava por ele - retornava todos os colaboradores da empresa, sem importar quais servicos cada um atende
- Coluna `colaboradores.servicos` e CSV (ex: "102,105,106") - agora filtrada com `FIND_IN_SET(servico_id, servicos) > 0`
- Tambem adicionado filtro `ativo = 1` para nao mostrar colaboradores demitidos
- Versao paralela em `Colaborador::getColaboradoresByServico` ja filtrava corretamente (com array_filter)
- Deploy feito no souhub.com.br

## Bug Corrigido - Horarios Colaborador (2026-04-27)
- `VendasAgendamentos::gerarHorariosBase()` nao gerava nenhum horario quando colaborador tinha `hora_inicio = hora_fim = 00:00:00` (loop `for ($hour=0; $hour<0; ...)` nao executa)
- Sintoma: colaborador aparece "disponivel" mas modal de horarios fica vazio
- Fix: detectar configuracao invalida (vazio, ambos 00:00, ou hora_fim<=hora_inicio) e usar fallback 08h-18h. Mantida logica de hora_fim=0 -> 24 (meia-noite) para casos validos
- Colaborador JESIEL (id 138, user_id 3 clara teste) corrigido no banco para 08:00-18:00
- Deploy feito no souhub.com.br

## Botoes com faixa preta (2026-04-27)
- Tailwind preflight esta desabilitado (`corePlugins: { preflight: false }` em menu_admin.php), entao `<button>` SEM classe Bootstrap `.btn` mantem o border padrao do navegador (quadrado preto ao redor do icone)
- Ao usar `<button>` com so classes Tailwind tipo `text-blue-600`, sempre adicionar `border-0 bg-transparent p-1` para nao mostrar o border nativo
- Corrigido em dashboard.php (botoes editar/excluir da tabela de agendamentos)
- Padrao: usar `class="btn btn-sm btn-outline-..."` (Bootstrap) OU `class="border-0 bg-transparent p-1 ..."` (Tailwind puro)

## Dropdown Header - Init Explicito (2026-04-27)
- Dropdown do usuario logado (no header de todas as paginas) nao abria em algumas views admin
- Causa provavel: Bootstrap dropdown nao auto-inicializando em todas as paginas (algumas views carregam outras versoes do bootstrap.min.js sem popper, ou conflitos de ordem de scripts)
- Solucao em `app/Views/include/menu_admin.php` (apos linha do bootstrap.bundle.min.js):
  1. Init explicito via `new bootstrap.Dropdown(el)` em todos `[data-bs-toggle="dropdown"]` no DOMContentLoaded
  2. Retry com setTimeout caso o `bootstrap` global ainda nao tenha carregado
  3. Fallback de click manual no `#dropdownUser` (toggle classe `.show` no menu) para garantir que abre mesmo se o Bootstrap falhar
- Deploy feito no souhub.com.br

## Configuracoes Admin - Aba Logs (2026-04-27)
- Adicionada aba "Logs do Sistema" em `admin/configuracoes` com 2 sub-abas (pills):
  - Log do Sistema: ultimas 300 linhas do arquivo `writable/logs/log-*.log` mais recente, terminal-style com cores por nivel (CRITICAL/ERROR/WARNING/INFO/DEBUG), filtro de texto e botao copiar
  - Atendimento de Suporte: ultimas 100 respostas em tickets via JOIN `ticket_respostas` + `tickets_suporte` + `usuarios` (autor + empresa). Mostra Ticket #, Empresa, Assunto, Atendido por (nome+email), Tipo (Admin/Cliente via flag is_admin), trecho da resposta, data e status do ticket
- Backend: `Admin::configuracoes()` agora passa `logsSistema`, `logFileAtual`, `atendimentosSuporte` para a view
- Deploy feito no souhub.com.br
