--- name: Feriados save silencioso description: Bug "feriados nao salva" sem mensagem de erro - causas comuns e padrao de erro silencioso no fetch type: project originSessionId: 1b4251bd-0c4c-49b7-8f1c-b8e3f6c185ed --- ## Sintoma Usuário clica "Salvar" no modal de Feriados (`/direcao/feriados`) e nada acontece — sem mensagem, sem feedback. Mesmo com tipo `recesso` ("RECESSO ESCOLAR" e similares). ## Causa raiz mais comum JS original usava `fetch().then(r=>r.json()).then(...)` SEM `.catch()`. Qualquer falha (HTML 500, 401 com payload não-JSON, exceção PHP) virava promise rejection silenciosa. **Why:** Diagnosticado em 2026-05-06. Localmente a `feriados` table nem existia (migration `2026-05-05-200000_CreateFeriados` não tinha rodado), mas o erro 500 era invisível no frontend. Em produção a tabela existe (verificado via `/cron/teste-feriado?token=...`). **How to apply:** 1. Verificar se `feriados` table existe (`/d/xampp/mysql/bin/mysql.exe -u root ag_music_banco -e "SHOW TABLES LIKE 'feriados';"`). Se não, rodar `php spark migrate`. 2. Em produção, testar `https://sistema.agmusic.com.br/cron/teste-feriado?token=agm-cron-7Hk29Lqp2Mz5RtVx` — se devolver JSON com `data_hoje/eh_feriado/config_ativa`, a tabela existe. 3. O `salvarFeriado()` (view) e `FeriadoController::salvar()` foram blindados em 2026-05-06 com try/catch, parse com fallback, detecção de 401, validação de tipo/data. ## Pontos importantes do código - View `direcao/feriados.php`: agora tem `_feriadosCache` em window pra `editar(id)` (antes era `editar(${JSON.stringify(f)})` no onclick — quebrava com aspas no nome). - `FeriadoController::salvar()`: valida tipo contra ENUM `['nacional','estadual','municipal','recesso','facultativo']`, valida data `^\d{4}-\d{2}-\d{2}$`, valida `data_fim >= data` quando preenchido, retorna `errors` do Model em 400, captura `Throwable` em 500. - `FeriadoModel`: `useTimestamps=true`, `allowedFields=['data','data_fim','descricao','tipo','recorrente','ativo']` — created_at/updated_at NÃO precisam estar em allowedFields (CI4 injeta direto). ## Intervalos de feriado/recesso (2026-05-06) - Migration `2026-05-06-100000_AddDataFimFeriados` adicionou `data_fim DATE NULL` em `feriados` - `data_fim NULL` = dia único; preenchido = intervalo `[data..data_fim]` (inclusive) - `getFeriadoNaData()` reescrito com query SQL pura: `(recorrente=0 AND data <= ? AND COALESCE(data_fim, data) >= ?) OR (recorrente=1 AND … )` - `listarPorAno()` agora retorna `data_ano` + `data_fim_ano` (ambos null-safe) - View mostra coluna "Período" (ex: `15/07/2026 → 31/07/2026`) e "Dias" (`17 dias` quando intervalo) - Modal: campo "Data Fim" opcional. Se = data início, é normalizado pra NULL no controller. ## Acesso produção (status 2026-05-06) - SSH (porta 22, 2222, 2200, 5022, 7822) RECUSANDO conexão de fora — só HTTPS responde - Para deploy: usar FileZilla/cPanel ou aguardar SSH voltar