1jQuery(document).ready(function($) { 2 // Configuração base do DataTable 3 const situacoesMap = { 4 'em_andamento': 'Em Andamento', 5 'paralisada': 'Paralisada', 6 'concluida': 'ConcluÃda', 7 'cancelada': 'Cancelada', 8 'indevido': 'Indevido' 9 }; 10 11 // Habilitar a busca para permitir os filtros personalizados 12 const baseConfig = { 13 language: { 14 emptyTable: "Nenhum registro encontrado", 15 info: "Mostrando de _START_ até _END_ de _TOTAL_ registros", 16 infoEmpty: "Mostrando 0 até 0 de 0 registros", 17 infoFiltered: "(Filtrados de _MAX_ registros)", 18 infoThousands: ".", 19 loadingRecords: "Carregando...", 20 processing: "Processando...", 21 zeroRecords: "Nenhum registro encontrado", 22 search: "Pesquisar", 23 paginate: { 24 next: "Próximo", 25 previous: "Anterior", 26 first: "Primeiro", 27 last: "Ãltimo" 28 }, 29 buttons: { 30 excel: "Exportar Excel", 31 pdf: "Exportar PDF" 32 } 33 }, 34 responsive: true, 35 searching: true, // Alterado para true para permitir filtros 36 dom: '<"top"B>rt<"bottom"ip>', 37 buttons: [ 38 { 39 extend: 'excel', 40 text: 'Exportar Excel', 41 className: 'btn-export', 42 title: 'Obras Públicas' 43 }, 44 { 45 extend: 'pdf', 46 text: 'Exportar PDF', 47 className: 'btn-export', 48 title: 'Obras Públicas', 49 orientation: 'landscape' 50 } 51 ], 52 pageLength: 10, 53 order: [[0, 'desc']], 54 columnDefs: [ 55 { 56 targets: 8, // Ãndice da coluna Situação 57 render: function(data, type, row) { 58 return situacoesMap[data] || data; 59 } 60 } 61 ] 62 }; 63 64 // Configuração especÃfica para a tabela de obras 65 const obrasConfig = { 66 ...baseConfig, 67 columnDefs: [ 68 { 69 targets: 1, 70 render: function(data, type, row) { 71 if (type === 'display') { 72 return '<span class="descricao-obra">' + data + '</span>'; 73 } 74 return data; 75 } 76 }, 77 { 78 targets: [3, 4], // Data InÃcio e Previsão Término 79 render: function(data, type, row) { 80 if (type === 'display' && data) { 81 // Verificar se a data já está formatada (contém /) 82 if (data.includes('/')) { 83 return data; 84 } 85 86 try { 87 const date = new Date(data); 88 // Verificar se a data é válida 89 if (isNaN(date.getTime())) { 90 return ''; 91 } 92 return date.toLocaleDateString('pt-BR'); 93 } catch (e) { 94 return ''; 95 } 96 } 97 return data; 98 } 99 } 100 ] 101 }; 102 103 // Configuração especÃfica para a tabela de declarações 104 const declaracoesConfig = { 105 ...baseConfig, 106 columnDefs: [ 107 { 108 targets: 2, // Ãndice da coluna de data de emissão 109 render: function(data, type, row) { 110 // Para exibição (HTML), manter a data como está se já estiver formatada 111 if (type === 'display' || type === 'filter') { 112 // Se já estiver formatada com barras (vindo assim do PHP), não mexer 113 if (data && data.includes('/')) { 114 return data; 115 } 116 117 // Se estiver vazia, retornar vazia 118 if (!data) { 119 return ''; 120 } 121 122 // Tentar converter para objeto Date e formatar 123 try { 124 const date = new Date(data); 125 if (isNaN(date.getTime())) { 126 return ''; // Data inválida 127 } 128 return date.toLocaleDateString('pt-BR'); 129 } catch (e) { 130 return ''; // Erro ao processar a data 131 } 132 } 133 return data; 134 } 135 } 136 ] 137 }; 138 139 // Inicializar DataTables 140 const obrasTable = $('#obras-table').DataTable(obrasConfig); 141 const declaracoesTable = $('#declaracoes-table').DataTable(declaracoesConfig); 142 143 // Função para aplicar os filtros 144 function aplicarFiltros() { 145 const situacao = $('#filtro-situacao').val(); 146 const descricao = $('#filtro-descricao').val().toLowerCase(); 147 const empresa = $('#filtro-empresa').val().toLowerCase(); 148 const ano = $('#filtro-ano').val(); 149 const numero = $('#filtro-numero').val(); 150 151 // Filtro customizado 152 $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) { 153 if (settings.nTable.id !== 'obras-table') return true; 154
155 const row = { 156 intervencao: data[0].toLowerCase(), 157 descricao: $(data[1]).text().toLowerCase(), 158 empresa: data[2].toLowerCase(), 159 situacao: data[6] 160 }; 161 162 // Verificar cada filtro 163 if (situacao && row.situacao !== situacoesMap[situacao]) return false; 164 if (descricao && !row.descricao.includes(descricao)) return false; 165 if (empresa && !row.empresa.includes(empresa)) return false; 166 167 // Filtrar por ano e número da intervenção 168 if (ano || numero) { 169 const partes = row.intervencao.split('/'); 170 if (partes.length === 2) { 171 const [num, anoInt] = partes; 172 if (ano && anoInt !== ano) return false; 173 if (numero && !num.includes(numero)) return false; 174 } 175 } 176 177 return true; 178 }); 179 180 // Redesenhar a tabela com os filtros aplicados 181 obrasTable.draw(); 182 183 // Limpar o filtro customizado 184 $.fn.dataTable.ext.search.pop(); 185 } 186 187 // Manipuladores de eventos para os filtros 188 $('#aplicar-filtros').on('click', function() { 189 aplicarFiltros(); 190 }); 191 192 $('#limpar-filtros').on('click', function() { 193 // Limpar todos os campos de filtro 194 $('#filtro-situacao').val(''); 195 $('#filtro-descricao').val(''); 196 $('#filtro-empresa').val(''); 197 $('#filtro-ano').val(''); 198 $('#filtro-numero').val(''); 199 200 // Redesenhar a tabela sem filtros 201 obrasTable.search('').columns().search('').draw(); 202 }); 203 204 // Permitir filtrar ao pressionar Enter nos campos de texto 205 $('.filtro-grupo input').on('keypress', function(e) { 206 if (e.which === 13) { 207 aplicarFiltros(); 208 } 209 }); 210 211 // Manipulação das abas 212 $('.obras-publicas-tab').on('click', function() { 213 const target = $(this).data('target'); 214 215 // Atualizar abas ativas 216 $('.obras-publicas-tab').removeClass('active'); 217 $(this).addClass('active'); 218 219 // Atualizar conteúdo ativo 220 $('.obras-publicas-content').removeClass('active'); 221 $(`#${target}`).addClass('active'); 222 223 // Reajustar colunas das tabelas 224 if (target === 'obras-content') { 225 obrasTable.columns.adjust().responsive.recalc(); 226 } else { 227 declaracoesTable.columns.adjust().responsive.recalc(); 228 } 229 }); 230 231 // Manipulação do modal 232 $(document).on('click', '.btn-detalhes-obra', function() { 233 const obraData = JSON.parse($(this).attr('data-obra')); 234 openModal(obraData); 235 }); 236 237 // Função para abrir o modal 238 function openModal(data) { 239 const modalContent = ` 240 <span class="obras-publicas-modal-close">×</span> 241 <h2>Detalhes da Obra</h2> 242 243 <div class="modal-grid"> 244 <div class="modal-section"> 245 <h3>Informações Gerais</h3> 246 <p><strong>Intervenção/Ano:</strong> ${data.intervencao_ano}</p> 247 <p><strong>Descrição:</strong> ${data.descricao_intervencao}</p> 248 <p><strong>Empresa:</strong> ${data.empresa}</p> 249 <p><strong>Objeto:</strong> ${data.objeto || 'Não informado'}</p> 250 <p><strong>Bem(Imóvel):</strong> ${data.bem_imovel || 'Não informado'}</p> 251 <p><strong>População atendida:</strong> ${data.populacao_atendida || 'Não informado'}</p> 252 </div> 253 254 <div class="modal-section"> 255 <h3>Datas e Prazos</h3> 256 <p><strong>Data InÃcio:</strong> ${data.data_inicio ? new Date(data.data_inicio).toLocaleDateString('pt-BR') : 'Não informado'}</p> 257 <p><strong>Previsão Término:</strong> ${data.previsao_termino ? new Date(data.previsao_termino).toLocaleDateString('pt-BR') : 'Não informado'}</p> 258 <p><strong>Ordem Serviço/Ano:</strong> ${data.ordem_servico_ano || 'Não informado'}</p> 259 <p><strong>Situação:</strong> ${situacoesMap[data.situacao] || data.situacao}</p> 260 </div> 261 262 <div class="modal-section"> 263 <h3>Valores e Medições</h3> 264 <p><strong>Valor Intervenção:</strong> ${data.valor_intervencao}</p> 265 <p><strong>Valor Contrato:</strong> ${data.valor_contrato}</p> 266 <p><strong>Valor Aditivo:</strong> ${data.valor_aditivo || 'Não informado'}</p> 267 <p><strong>Medições:</strong> ${data.medicoes || 'Não informado'}</p> 268 <p><strong>Percentual Empenhado:</strong> ${data.percentual_empenhado ? data.percentual_empenhado + '%' : 'Não informado'}</p> 269 </div> 270 271 <div class="modal-section"> 272 <h3>Responsáveis e Documentos</h3> 273 <p><strong>Fiscal Técnico:</strong> ${data.fiscal_tecnico || 'Não informado'}</p> 274 <p><strong>Contrato:</strong> ${data.link_contrato ? '<a href="' + data.link_contrato + '" target="_blank">Ver Contrato</a>' : 'Não disponÃvel'}</p> 275 ${data.anexos && data.anexos.length > 0 ? ` 276 <div class="anexos-lista"> 277 <h4>Anexos:</h4> 278 <div class="anexos-grid"> 279 ${data.anexos.map(anexo => ` 280 <a href="${anexo.arquivo_url}" target="_blank" class="btn-anexo">
281 <span class="dashicons dashicons-download"></span> 282 ${anexo.descricao} 283 </a> 284 `).join('')} 285 </div> 286 </div> 287 ` : ''} 288 </div> 289 </div> 290 291 <div class="obras-publicas-modal-buttons"> 292 <button class="modal-export-btn excel" id="modal-export-excel"> 293 <span class="dashicons dashicons-media-spreadsheet"></span> 294 Exportar Excel 295 </button> 296 <button class="modal-export-btn pdf" id="modal-export-pdf"> 297 <span class="dashicons dashicons-pdf"></span> 298 Exportar PDF 299 </button> 300 </div> 301 `; 302 303 $('#obras-modal-content').html(modalContent); 304 $('#obras-modal').show(); 305 306 // Adicionar eventos de clique para os botões de exportação 307 $('#modal-export-excel').on('click', function() { 308 const excelData = { 309 'Intervenção/Ano': data.intervencao_ano, 310 'Descrição': data.descricao_intervencao, 311 'Empresa': data.empresa, 312 'Data InÃcio': data.data_inicio ? new Date(data.data_inicio).toLocaleDateString('pt-BR') : 'Não informado', 313 'Previsão Término': data.previsao_termino ? new Date(data.previsao_termino).toLocaleDateString('pt-BR') : 'Não informado', 314 'Valor Intervenção': data.valor_intervencao, 315 'Valor Contrato': data.valor_contrato, 316 'Valor Aditivo': data.valor_aditivo || 'Não informado', 317 'Situação': situacoesMap[data.situacao] || data.situacao, 318 'Ordem Serviço/Ano': data.ordem_servico_ano || 'Não informado', 319 'Medições': data.medicoes || 'Não informado', 320 'Percentual Empenhado': data.percentual_empenhado ? data.percentual_empenhado + '%' : 'Não informado', 321 'Bem(Imóvel)': data.bem_imovel || 'Não informado', 322 'Objeto': data.objeto || 'Não informado', 323 'População atendida': data.populacao_atendida || 'Não informado', 324 'Fiscal Técnico': data.fiscal_tecnico || 'Não informado' 325 }; 326 327 // Criar e baixar arquivo Excel 328 const ws = XLSX.utils.json_to_sheet([excelData]); 329 const wb = XLSX.utils.book_new(); 330 XLSX.utils.book_append_sheet(wb, ws, "Detalhes da Obra"); 331 XLSX.writeFile(wb, `Obra_${data.intervencao_ano}.xlsx`); 332 }); 333 334 $('#modal-export-pdf').on('click', function() { 335 const docDefinition = { 336 content: [ 337 { text: 'Detalhes da Obra', style: 'header' }, 338 { text: '\n' }, 339 { text: 'Informações Gerais', style: 'subheader' }, 340 { text: `Intervenção/Ano: ${data.intervencao_ano}` }, 341 { text: `Descrição: ${data.descricao_intervencao}` }, 342 { text: `Empresa: ${data.empresa}` }, 343 { text: '\n' }, 344 { text: 'Datas e Prazos', style: 'subheader' }, 345 { text: `Data InÃcio: ${data.data_inicio ? new Date(data.data_inicio).toLocaleDateString('pt-BR') : 'Não informado'}` }, 346 { text: `Previsão Término: ${data.previsao_termino ? new Date(data.previsao_termino).toLocaleDateString('pt-BR') : 'Não informado'}` }, 347 { text: `Situação: ${situacoesMap[data.situacao] || data.situacao}` }, 348 { text: '\n' }, 349 { text: 'Valores e Medições', style: 'subheader' }, 350 { text: `Valor Intervenção: ${data.valor_intervencao}` }, 351 { text: `Valor Contrato: ${data.valor_contrato}` }, 352 { text: `Valor Aditivo: ${data.valor_aditivo || 'Não informado'}` }, 353 { text: `Percentual Empenhado: ${data.percentual_empenhado ? data.percentual_empenhado + '%' : 'Não informado'}` } 354 ], 355 styles: { 356 header: { 357 fontSize: 18, 358 bold: true, 359 margin: [0, 0, 0, 10] 360 }, 361 subheader: { 362 fontSize: 14, 363 bold: true, 364 margin: [0, 10, 0, 5] 365 } 366 } 367 }; 368
369 pdfMake.createPdf(docDefinition).download(`Obra_${data.intervencao_ano}.pdf`); 370 }); 371 } 372 373 $(document).on('click', '.obras-publicas-modal-close', function() { 374 $('#obras-modal').hide(); 375 }); 376 377 $(window).on('click', function(event) { 378 if ($(event.target).is('#obras-modal')) { 379 $('#obras-modal').hide(); 380 } 381 }); 382 383 // Funções auxiliares 384 function formatMoney(value) { 385 if (!value) return ''; 386 return new Intl.NumberFormat('pt-BR', { 387 minimumFractionDigits: 2, 388 maximumFractionDigits: 2 389 }).format(value); 390 } 391 392 function formatNumber(value) { 393 if (!value) return ''; 394 return new Intl.NumberFormat('pt-BR').format(value); 395 } 396 397 function renderAnexos(anexos) { 398 if (!anexos || !anexos.length) { 399 return '<li>Nenhum anexo disponÃvel</li>'; 400 } 401 402 return anexos.map(anexo => ` 403 <li> 404 <a href="${anexo.arquivo.url}" target="_blank"> 405 ${anexo.descricao} (${anexo.arquivo.filename}) 406 </a> 407 </li> 408 `).join(''); 409 } 410 411 // Funções de exportação 412 function exportPDF() { 413 // Implementar exportação PDF 414 } 415 416 function exportExcel() { 417 // Implementar exportação Excel 418 } 419 420 function exportJSON() { 421 // Implementar exportação JSON 422 } 423 424 function printDetails() { 425 window.print(); 426 } 427});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.