This commit is contained in:
Andrey Cunha
2026-03-10 23:32:02 -03:00
parent b32415212f
commit 9a0ef21ec8
63 changed files with 4412 additions and 637 deletions
@@ -1,11 +1,7 @@
<div id="dashConforme_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="DashConforme.instance()">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Top Inconformidades</h3>
</div>
<div class="panel-body">
<canvas id="graficoConforme_${instanceId}" height="120"></canvas>
</div>
<div class="dconf-card">
<div class="dconf-title">Atingimento por Loja</div>
<canvas id="graficoConforme_${instanceId}" height="120"></canvas>
</div>
</div>
@@ -1,3 +1,25 @@
div[id^="dashConforme_"] canvas {
max-height: 420px;
div[id^="dashConforme_"] .dconf-card {
background: #ececed;
border-radius: 24px;
padding: 10px 14px;
min-height: 230px;
}
div[id^="dashConforme_"] .dconf-title {
color: #004a6a;
font-size: 18px;
font-weight: 800;
text-align: center;
margin-bottom: 4px;
}
div[id^="dashConforme_"] canvas {
min-height: 165px;
max-height: 180px;
}
@media (max-width: 1300px) {
div[id^="dashConforme_"] .dconf-title {
font-size: 24px;
}
}
@@ -1,149 +1,129 @@
var DashConforme = SuperWidget.extend({
chart:null,
chart: null,
init:function(){
var self=this;
this.renderAmostra();
window.addEventListener("dashboardData",function(e){
init: function () {
var self = this;
this.render([]);
window.addEventListener("dashboardData", function (e) {
self.render(e.detail || []);
});
},
render:function(dados){
render: function (dados) {
var mapa = {};
dados.forEach(function(item){
var lista = String(item.listaNaoConforme || "").trim();
if(!lista){
return;
dados.forEach(function (item) {
var loja = String(item.loja || "Sem loja").trim();
if (!mapa[loja]) {
mapa[loja] = { total: 0, conforme: 0 };
}
mapa[loja].total += 1;
if (normalizaStatus(item) === "CONFORME") {
mapa[loja].conforme += 1;
}
lista.split("|").forEach(function(indicador){
var chave = String(indicador || "").trim();
if(!chave){
return;
}
if(!mapa[chave]){
mapa[chave] = 0;
}
mapa[chave] += 1;
});
});
var pares = Object.keys(mapa).map(function(k){
return {nome:k,valor:mapa[k]};
var pares = Object.keys(mapa).map(function (nome) {
var total = mapa[nome].total;
var pct = total ? (mapa[nome].conforme / total) * 100 : 0;
return { nome: nome, pct: Number(pct.toFixed(2)) };
});
pares.sort(function(a,b){
return b.valor - a.valor;
pares.sort(function (a, b) {
return b.pct - a.pct;
});
pares = pares.slice(0,10);
var labels = pares.map(function(p){ return p.nome; });
var valores = pares.map(function(p){ return p.valor; });
if(!labels.length){
labels = ["Aguardando filtro"];
valores = [0];
if (!pares.length) {
pares = [{ nome: "Aguardando filtro", pct: 0 }];
}
var ctx = document.getElementById("graficoConforme_"+this.instanceId);
if(!ctx){
return;
}
var labels = pares.map(function (p) { return p.nome; });
var valores = pares.map(function (p) { return p.pct; });
if(this.chart){
this.chart.destroy();
}
var ctx = document.getElementById("graficoConforme_" + this.instanceId);
if (!ctx) return;
this.chart = new Chart(ctx,{
type:"bar",
data:{
labels:labels,
datasets:[{
label:"Ocorrências",
data:valores,
backgroundColor:"#f39c12"
if (this.chart) this.chart.destroy();
var valueLabelsPlugin = {
id: "valueLabelsPlugin",
afterDatasetsDraw: function (chart) {
var c = chart.ctx;
c.save();
c.font = "700 10px Arial";
c.textAlign = "center";
chart.data.datasets.forEach(function (dataset, datasetIndex) {
var meta = chart.getDatasetMeta(datasetIndex);
meta.data.forEach(function (bar, index) {
var value = dataset.data[index];
if (value == null || Number(value) === 0) return;
var y = bar.y - 4;
var baseline = "bottom";
if (y < chart.chartArea.top + 12) {
y = bar.y + 10;
baseline = "top";
}
c.textBaseline = baseline;
c.fillStyle = baseline === "top" ? "#ffffff" : "#111111";
c.fillText(String(value).replace(".", ",") + "%", bar.x, y);
});
});
c.restore();
}
};
this.chart = new Chart(ctx, {
type: "bar",
plugins: [valueLabelsPlugin],
data: {
labels: labels,
datasets: [{
data: valores,
backgroundColor: "#0a5d82"
}]
},
options:{
responsive:true,
maintainAspectRatio:false,
indexAxis:"x",
plugins:{
legend:{display:false},
tooltip:{
callbacks:{
title:function(items){
if(labels.length === 1 && labels[0] === "Aguardando filtro"){
return "Aguardando filtro";
}
return items && items.length ? labels[items[0].dataIndex] : "";
}
}
options: {
responsive: true,
layout: {
padding: {
top: 14
}
},
scales:{
x:{
ticks:{
autoSkip:false,
maxRotation:35,
minRotation:0,
callback:function(value,index){
plugins: { legend: { display: false } },
scales: {
x: {
ticks: {
autoSkip: false,
maxRotation: 28,
minRotation: 10,
callback: function (value, index) {
var txt = labels[index] || "";
return txt.length > 38 ? txt.substring(0,38) + "..." : txt;
return txt.length > 22 ? txt.substring(0, 22) + "..." : txt;
}
}
},
y:{
beginAtZero:true,
precision:0
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function (value) {
return value + "%";
}
}
}
}
}
});
},
renderAmostra:function(){
var ctx = document.getElementById("graficoConforme_"+this.instanceId);
if(!ctx){
return;
}
if(this.chart){
this.chart.destroy();
}
this.chart = new Chart(ctx,{
type:"bar",
data:{
labels:["Aguardando filtro"],
datasets:[{
label:"Ocorrências",
data:[0],
backgroundColor:"#cfd8dc"
}]
},
options:{
responsive:true,
maintainAspectRatio:false,
plugins:{legend:{display:false}},
scales:{
x:{ticks:{autoSkip:false,maxRotation:0,minRotation:0}},
y:{beginAtZero:true,precision:0}
}
}
});
}
});
function normalizaStatus(item) {
return String(item.saidaAnalise || item.status || "").trim().toUpperCase();
}
@@ -1,123 +1,95 @@
var DashData = SuperWidget.extend({
init:function(){
console.log("DashData carregado");
var self=this;
window.addEventListener("dashboardFiltro",function(e){
console.log("DashData recebeu filtros:",e.detail);
self.carregar(e.detail);
init: function () {
var self = this;
window.addEventListener("dashboardFiltro", function (e) {
self.carregar(e.detail || {});
});
this.carregar({});
},
carregar:function(filtros){
console.log("Consultando dataset...", filtros);
var self=this;
carregar: function (filtros) {
$.ajax({
url:"/api/public/ecm/dataset/search",
type:"POST",
contentType:"application/json",
data:JSON.stringify({
datasetId:"ds_dashboard_checklist"
url: "/api/public/ecm/dataset/search",
type: "POST",
contentType: "application/json",
data: JSON.stringify({
datasetId: "ds_dashboard_checklist"
}),
success:function(res){
console.log("Resposta dataset:",res);
var dados=res.content || [];
console.log("Quantidade registros:", dados.length);
// FILTRO POR LOJA
if(filtros.loja){
dados = dados.filter(function(item){
return normalizaTexto(item.loja) === normalizaTexto(filtros.loja);
});
}
// FILTRO POR DATA
if(filtros.dataInicio || filtros.dataFim){
dados = dados.filter(function(item){
var dataInicioAuditoria = normalizaData(item.dataAuditoria);
var dataLimite = normalizaData(item.dataLimite);
var inicioFiltro = normalizaData(filtros.dataInicio);
var fimFiltro = normalizaData(filtros.dataFim);
// Mantem quando data de auditoria OU data limite estiver na faixa filtrada
return dataNaFaixa(dataInicioAuditoria, inicioFiltro, fimFiltro) ||
dataNaFaixa(dataLimite, inicioFiltro, fimFiltro);
});
}
// Mantem somente auditorias com status final (evita divergencia entre total e KPIs)
var antesStatus = dados.length;
dados = dados.filter(function(item){
success: function (res) {
var dados = (res.content || []).filter(function (item) {
var st = normalizaTexto(item.saidaAnalise || item.status || "");
return st === "CONFORME" || st === "NAO_CONFORME";
});
console.log("Registros com status final:", dados.length, "de", antesStatus);
// Ordena por data de auditoria mais recente
dados.sort(function(a,b){
dados = dados.filter(function (item) {
if (!matchMulti(item.ciclo, filtros.ciclo)) {
return false;
}
if (!matchMulti(item.regional, filtros.regional)) {
return false;
}
var sup = item.responsavelLoja;
if (!matchMulti(sup, filtros.supervisao)) {
return false;
}
if (filtros.dataInicio || filtros.dataFim) {
var dataAuditoria = normalizaData(item.dataAuditoria);
var dataLimite = normalizaData(item.dataLimite);
var inicio = normalizaData(filtros.dataInicio);
var fim = normalizaData(filtros.dataFim);
return dataNaFaixa(dataAuditoria, inicio, fim) || dataNaFaixa(dataLimite, inicio, fim);
}
return true;
});
dados.sort(function (a, b) {
var da = normalizaData(a.dataAuditoria);
var db = normalizaData(b.dataAuditoria);
if(da < db) return 1;
if(da > db) return -1;
if (da < db) return 1;
if (da > db) return -1;
return 0;
});
console.log("Registros filtrados:", dados);
window.dispatchEvent(
new CustomEvent("dashboardData",{detail:dados})
);
window.dispatchEvent(new CustomEvent("dashboardData", { detail: dados }));
},
error: function () {
window.dispatchEvent(new CustomEvent("dashboardData", { detail: [] }));
}
});
}
});
function normalizaTexto(valor){
function normalizaTexto(valor) {
return String(valor || "").trim().toUpperCase();
}
function normalizaData(valor){
var v = String(valor || "").trim();
return v;
function normalizaData(valor) {
return String(valor || "").trim();
}
function dataNaFaixa(data, inicio, fim){
if(!data){
return false;
}
if(inicio && data < inicio){
return false;
}
if(fim && data > fim){
return false;
}
function dataNaFaixa(data, inicio, fim) {
if (!data) return false;
if (inicio && data < inicio) return false;
if (fim && data > fim) return false;
return true;
}
function matchMulti(valorAtual, selecionados) {
if (!selecionados || !selecionados.length) {
return true;
}
var atual = normalizaTexto(valorAtual);
for (var i = 0; i < selecionados.length; i++) {
if (atual === normalizaTexto(selecionados[i])) {
return true;
}
}
return false;
}
@@ -1,9 +1,9 @@
<div id="dashGrafico_${instanceId}"
class="super-widget wcm-widget-class fluig-style-guide"
data-params="DashGrafico.instance()">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div style="height:260px; max-width:360px; margin:0 auto;">
<canvas id="grafico_${instanceId}"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="dgf-card">
<div class="dgf-title">Atingimento por Regional</div>
<canvas id="grafico_${instanceId}" height="120"></canvas>
</div>
</div>
@@ -1 +1,25 @@
/* Coloque aqui seu codigo CSS */
div[id^="dashGrafico_"] .dgf-card {
background: #ececed;
border-radius: 24px;
padding: 10px 14px;
min-height: 230px;
}
div[id^="dashGrafico_"] .dgf-title {
color: #004a6a;
font-size: 18px;
font-weight: 800;
text-align: center;
margin-bottom: 4px;
}
div[id^="dashGrafico_"] canvas {
min-height: 165px;
max-height: 180px;
}
@media (max-width: 1300px) {
div[id^="dashGrafico_"] .dgf-title {
font-size: 24px;
}
}
@@ -1,91 +1,149 @@
var DashGrafico = SuperWidget.extend({
chart:null,
chart: null,
init:function(){
init: function () {
var self = this;
this.render([]);
var self=this;
this.renderAmostra();
window.addEventListener("dashboardData", function (e) {
self.render(e.detail || []);
});
},
window.addEventListener("dashboardData",function(e){
render: function (dados) {
var mapa = {};
self.render(e.detail);
dados.forEach(function (item) {
var regiao = String(item.regional || "Sem regiao").trim();
if (!mapa[regiao]) {
mapa[regiao] = { total: 0, conforme: 0, naoConforme: 0 };
}
mapa[regiao].total += 1;
if (normalizaStatus(item) === "CONFORME") {
mapa[regiao].conforme += 1;
} else {
mapa[regiao].naoConforme += 1;
}
});
var labels = Object.keys(mapa).sort(function (a, b) { return a.localeCompare(b); });
if (!labels.length) {
labels = ["Aguardando filtro"];
this.plot(labels, [0], [0]);
return;
}
var atingimento = labels.map(function (l) {
var r = mapa[l];
return r.total ? Number(((r.conforme / r.total) * 100).toFixed(2)) : 0;
});
var ncMedio = labels.map(function (l) {
var r = mapa[l];
return r.total ? Number(((r.naoConforme / r.total) * 100).toFixed(2)) : 0;
});
this.plot(labels, atingimento, ncMedio);
},
plot: function (labels, atingimento, ncMedio) {
var canvas = document.getElementById("grafico_" + this.instanceId);
if (!canvas) return;
if (this.chart) {
this.chart.destroy();
}
var valueLabelsPlugin = {
id: "valueLabelsPlugin",
afterDatasetsDraw: function (chart) {
var c = chart.ctx;
c.save();
c.font = "700 10px Arial";
c.textAlign = "center";
chart.data.datasets.forEach(function (dataset, datasetIndex) {
var meta = chart.getDatasetMeta(datasetIndex);
meta.data.forEach(function (bar, index) {
var value = dataset.data[index];
if (value == null || Number(value) === 0) return;
var y = bar.y - 4;
var baseline = "bottom";
if (y < chart.chartArea.top + 12) {
y = bar.y + 10;
baseline = "top";
}
c.textBaseline = baseline;
c.fillStyle = baseline === "top" ? "#ffffff" : "#111111";
c.fillText(String(value).replace(".", ",") + "%", bar.x, y);
});
});
c.restore();
}
};
this.chart = new Chart(canvas, {
type: "bar",
plugins: [valueLabelsPlugin],
data: {
labels: labels,
datasets: [
{
label: "Atingimento (%)",
data: atingimento,
backgroundColor: "#0a5d82"
},
{
label: "Nao conforme (%)",
data: ncMedio,
backgroundColor: "#88a5b8"
}
]
},
options: {
responsive: true,
layout: {
padding: {
top: 14
}
},
plugins: {
legend: {
position: "top"
}
},
scales: {
x: {
ticks: {
autoSkip: false,
maxRotation: 20,
minRotation: 0,
callback: function (value, index) {
var txt = labels[index] || "";
return txt.length > 18 ? txt.substring(0, 18) + "..." : txt;
}
}
},
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function (value) {
return value + "%";
}
}
}
}
}
});
}
});
},
render:function(dados){
var conforme=dados.filter(function(i){
return normalizaStatus(i)=="CONFORME";
}).length;
var nao=dados.filter(function(i){
return normalizaStatus(i)=="NAO_CONFORME";
}).length;
var ctx=document.getElementById("grafico_"+this.instanceId);
if(this.chart){
this.chart.destroy();
function normalizaStatus(item) {
return String(item.saidaAnalise || item.status || "").trim().toUpperCase();
}
this.chart=new Chart(ctx,{
type:'doughnut',
data:{
labels:["Conforme ("+conforme+")","Não Conforme ("+nao+")"],
datasets:[{
data:[conforme,nao],
backgroundColor:["#2ecc71","#e74c3c"]
}]
},
options:{
responsive:true,
maintainAspectRatio:false,
cutout:"48%"
}
});
function normalizaStatus(item){
var raw = item.saidaAnalise || item.status || "";
return String(raw).trim().toUpperCase();
}
},
renderAmostra:function(){
var ctx=document.getElementById("grafico_"+this.instanceId);
if(!ctx){
return;
}
if(this.chart){
this.chart.destroy();
}
this.chart=new Chart(ctx,{
type:'doughnut',
data:{
labels:["Conforme (0)","Não Conforme (0)"],
datasets:[{
data:[1,0],
backgroundColor:["#cfd8dc","#eceff1"]
}]
},
options:{
responsive:true,
maintainAspectRatio:false,
cutout:"48%",
plugins:{
tooltip:{enabled:false}
}
}
});
}
});
@@ -1,44 +1,21 @@
<div id="dashKPI_${instanceId}"
class="super-widget wcm-widget-class fluig-style-guide"
data-params="DashKPI.instance()">
<div class="dkpi-grid">
<div class="dkpi-card">
<div class="dkpi-title">Atingimento | Geral | CP</div>
<div id="kpi_pct_${instanceId}" class="dkpi-big">0,00%</div>
<div class="dkpi-label">AUDITORIA</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<h4>Total Auditorias</h4>
<h2 id="kpi_total_${instanceId}">0</h2>
<div class="dkpi-card">
<div class="dkpi-title">Total de Auditorias | Geral | CP</div>
<div class="dkpi-total-row">
<div class="dkpi-bar-bg">
<div id="kpi_total_bar_${instanceId}" class="dkpi-bar-fill"></div>
</div>
<div id="kpi_total_${instanceId}" class="dkpi-total">0</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-success">
<div class="panel-body">
<h4>Conformes</h4>
<h2 id="kpi_ok_${instanceId}">0</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-danger">
<div class="panel-body">
<h4>Não Conformes</h4>
<h2 id="kpi_nok_${instanceId}">0</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-info">
<div class="panel-body">
<h4>% Conformidade</h4>
<h2 id="kpi_pct_${instanceId}">0%</h2>
</div>
</div>
</div>
</div>
</div>
@@ -1 +1,84 @@
/* Coloque aqui seu codigo CSS */
div[id^="dashKPI_"] .dkpi-grid {
display: grid;
grid-template-columns: 1fr 1.4fr;
gap: 4px;
}
div[id^="dashKPI_"] .dkpi-card {
background: #ececed;
border-radius: 18px;
padding: 8px 12px;
min-height: 120px;
}
div[id^="dashKPI_"] .dkpi-title {
color: #004a6a;
font-size: 14px;
font-weight: 800;
margin-bottom: 4px;
line-height: 1.08;
}
div[id^="dashKPI_"] .dkpi-big {
font-size: 20px;
font-weight: 800;
text-align: center;
margin-top: 2px;
margin-bottom: 2px;
}
div[id^="dashKPI_"] .dkpi-label {
text-align: center;
color: #44525a;
font-size: 11px;
font-weight: 700;
}
div[id^="dashKPI_"] .dkpi-total-row {
display: flex;
align-items: center;
gap: 10px;
margin-top: 6px;
}
div[id^="dashKPI_"] .dkpi-bar-bg {
flex: 1;
height: 30px;
border-radius: 4px;
background: #e0e5ea;
overflow: hidden;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}
div[id^="dashKPI_"] .dkpi-bar-fill {
width: 0;
height: 100%;
background: linear-gradient(180deg, #1f6f95 0%, #095777 100%);
transition: width 280ms ease;
}
div[id^="dashKPI_"] .dkpi-total {
min-width: 44px;
font-size: 16px;
font-weight: 800;
color: #111111;
text-align: right;
}
@media (max-width: 1300px) {
div[id^="dashKPI_"] .dkpi-grid {
grid-template-columns: 1fr;
}
div[id^="dashKPI_"] .dkpi-title {
font-size: 16px;
}
div[id^="dashKPI_"] .dkpi-big {
font-size: 28px;
}
div[id^="dashKPI_"] .dkpi-total {
font-size: 22px;
}
}
@@ -1,41 +1,32 @@
var DashKPI = SuperWidget.extend({
init:function(){
init: function () {
var self = this;
var self=this;
window.addEventListener("dashboardData", function (e) {
self.calcular(e.detail || []);
});
},
window.addEventListener("dashboardData",function(e){
calcular: function (dados) {
var total = dados.length;
var conformes = dados.filter(function (i) {
return normalizaStatus(i) === "CONFORME";
}).length;
self.calcular(e.detail);
var pct = total ? (conformes / total) * 100 : 0;
var pctTxt = pct.toFixed(2).replace(".", ",") + "%";
$("#kpi_total_" + this.instanceId).text(total);
$("#kpi_pct_" + this.instanceId).text(pctTxt);
var largura = total ? Math.max(12, Math.min(100, pct)) : 0;
$("#kpi_total_bar_" + this.instanceId).css("width", largura + "%");
}
});
},
calcular:function(dados){
var total=dados.length;
var conformes=dados.filter(function(i){
return normalizaStatus(i)=="CONFORME";
}).length;
var nao=dados.filter(function(i){
return normalizaStatus(i)=="NAO_CONFORME";
}).length;
var pct= total ? Math.round((conformes/total)*100) : 0;
$("#kpi_total_"+this.instanceId).text(total);
$("#kpi_ok_"+this.instanceId).text(conformes);
$("#kpi_nok_"+this.instanceId).text(nao);
$("#kpi_pct_"+this.instanceId).text(pct+"%");
}
});
function normalizaStatus(item){
var raw = item.saidaAnalise || item.status || "";
return String(raw).trim().toUpperCase();
function normalizaStatus(item) {
var raw = item.saidaAnalise || item.status || "";
return String(raw).trim().toUpperCase();
}
@@ -1,18 +1,10 @@
<div id="dashRegional_${instanceId}"
class="super-widget wcm-widget-class fluig-style-guide"
data-params="DashRegional.instance()">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Não Conformes por Regional</h3>
</div>
<div class="panel-body">
<canvas id="graficoRegional_${instanceId}" height="120"></canvas>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="drg-card">
<div class="drg-title">Auditorias Realizadas</div>
<canvas id="graficoRegionalTotal_${instanceId}" height="120"></canvas>
</div>
</div>
@@ -1,12 +1,25 @@
div[id^="dashRegional_"] {
position: relative !important;
z-index: 1;
div[id^="dashRegional_"] .drg-card {
background: #ececed;
border-radius: 24px;
padding: 10px 14px;
min-height: 230px;
}
div[id^="dashRegional_"] .panel {
overflow: hidden;
div[id^="dashRegional_"] .drg-title {
color: #004a6a;
font-size: 18px;
font-weight: 800;
text-align: center;
margin-bottom: 4px;
}
div[id^="dashRegional_"] canvas {
max-height: 260px;
min-height: 165px;
max-height: 180px;
}
@media (max-width: 1300px) {
div[id^="dashRegional_"] .drg-title {
font-size: 22px;
}
}
@@ -1,102 +1,112 @@
var DashRegional = SuperWidget.extend({
chart:null,
chartTotal: null,
init:function(){
init: function () {
var self = this;
this.render([]);
var self=this;
this.renderAmostra();
window.addEventListener("dashboardData", function (e) {
self.render(e.detail || []);
});
},
window.addEventListener("dashboardData",function(e){
render: function (dados) {
var mapa = {};
self.render(e.detail);
});
},
render:function(dados){
var mapa={};
// soma nao conformes por regional
dados.forEach(function(i){
var regional=i.regional || "Sem regional";
if(!mapa[regional]) mapa[regional]=0;
mapa[regional]+=parseInt(i.qtdNaoConforme||0,10) || 0;
});
// transformar em arrays
var labels=Object.keys(mapa);
var valores=Object.values(mapa);
if(!labels.length){
labels=["Aguardando filtro"];
valores=[0];
}
// canvas
var ctx=document.getElementById("graficoRegional_"+this.instanceId);
if(this.chart){
this.chart.destroy();
}
// criar gráfico
this.chart=new Chart(ctx,{
type:'bar',
data:{
labels:labels,
datasets:[{
label:"Não conformes",
data:valores,
backgroundColor:"#e74c3c"
}]
},
options:{
responsive:true,
plugins:{
legend:{display:false}
}
}
});
},
renderAmostra:function(){
var ctx=document.getElementById("graficoRegional_"+this.instanceId);
if(!ctx){
return;
}
if(this.chart){
this.chart.destroy();
}
this.chart=new Chart(ctx,{
type:'bar',
data:{
labels:["Aguardando filtro"],
datasets:[{
label:"Não conformes",
data:[0],
backgroundColor:"#cfd8dc"
}]
},
options:{
responsive:true,
plugins:{legend:{display:false}}
}
});
}
dados.forEach(function (item) {
var regiao = String(item.regional || "Sem regiao").trim();
if (!mapa[regiao]) {
mapa[regiao] = 0;
}
mapa[regiao] += 1;
});
var labels = Object.keys(mapa);
if (!labels.length) {
this.renderGrafico(["Aguardando filtro"], [0]);
return;
}
labels.sort(function (a, b) {
return a.localeCompare(b);
});
var totais = labels.map(function (l) { return mapa[l]; });
this.renderGrafico(labels, totais);
},
renderGrafico: function (labels, totais) {
var ctxTotal = document.getElementById("graficoRegionalTotal_" + this.instanceId);
if (!ctxTotal) return;
if (this.chartTotal) this.chartTotal.destroy();
var valueLabelsPlugin = {
id: "valueLabelsPlugin",
afterDatasetsDraw: function (chart) {
var ctx = chart.ctx;
ctx.save();
ctx.font = "700 10px Arial";
ctx.textAlign = "center";
chart.data.datasets.forEach(function (dataset, datasetIndex) {
var meta = chart.getDatasetMeta(datasetIndex);
meta.data.forEach(function (bar, index) {
var value = dataset.data[index];
if (value == null) return;
var y = bar.y - 4;
var baseline = "bottom";
if (y < chart.chartArea.top + 12) {
y = bar.y + 10;
baseline = "top";
}
ctx.textBaseline = baseline;
ctx.fillStyle = baseline === "top" ? "#ffffff" : "#111111";
ctx.fillText(String(value), bar.x, y);
});
});
ctx.restore();
}
};
this.chartTotal = new Chart(ctxTotal, {
type: "bar",
plugins: [valueLabelsPlugin],
data: {
labels: labels,
datasets: [{
data: totais,
backgroundColor: "#0a5d82"
}]
},
options: {
responsive: true,
layout: {
padding: {
top: 14
}
},
plugins: { legend: { display: false } },
scales: {
x: {
ticks: {
autoSkip: false,
maxRotation: 22,
minRotation: 0,
callback: function (value, index) {
var txt = labels[index] || "";
return txt.length > 18 ? txt.substring(0, 18) + "..." : txt;
}
}
},
y: { beginAtZero: true, precision: 0 }
}
}
});
}
});
@@ -1,44 +1,46 @@
<div id="dashTabela_${instanceId}"
<div id="dashTabela_${instanceId}"
class="super-widget wcm-widget-class fluig-style-guide"
data-params="DashTabela.instance()">
<div class="dtb-card">
<div class="dtb-title">Detalhe das Auditorias</div>
<table class="table table-hover">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Data Inicio</th>
<th>Prazo</th>
<th>Ciclo</th>
<th>Analista</th>
<th>Gestor</th>
<th>Loja</th>
<th>Status</th>
</tr>
</thead>
<thead>
<tr>
<th>Data Início</th>
<th>Data Limite</th>
<th>Ciclo</th>
<th>Auditor</th>
<th>Responsável Loja</th>
<th>Loja</th>
<th>Status</th>
</tr>
</thead>
<tbody id="lista_${instanceId}">
<tr>
<td colspan="7" class="text-center">Aguardando filtro...</td>
</tr>
</tbody>
</table>
</div>
<tbody id="lista_${instanceId}">
<tr>
<td colspan="7" class="text-center">Aguardando filtro...</td>
</tr>
</tbody>
</table>
<div class="dash-table-footer">
<div class="dash-page-size">
<label for="page_size_${instanceId}">Itens por página</label>
<select id="page_size_${instanceId}" class="form-control input-sm">
<option value="5" selected>5</option>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
</div>
<div id="page_info_${instanceId}" class="dash-page-info">Aguardando filtro</div>
<div class="dash-page-actions">
<button id="page_prev_${instanceId}" type="button" class="btn btn-default btn-sm">Anterior</button>
<button id="page_next_${instanceId}" type="button" class="btn btn-default btn-sm">Próxima</button>
<div class="dash-table-footer">
<div class="dash-page-size">
<label for="page_size_${instanceId}">Itens por pagina</label>
<select id="page_size_${instanceId}" class="form-control input-sm">
<option value="5" selected>5</option>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
</div>
<div id="page_info_${instanceId}" class="dash-page-info">Aguardando filtro</div>
<div class="dash-page-actions">
<button id="page_prev_${instanceId}" type="button" class="btn btn-default btn-sm">Anterior</button>
<button id="page_next_${instanceId}" type="button" class="btn btn-default btn-sm">Proxima</button>
</div>
</div>
</div>
</div>
</div>
@@ -1,8 +1,26 @@
div[id^="dashTabela_"] .dtb-card {
background: #ececed;
border-radius: 34px;
padding: 16px 20px;
}
div[id^="dashTabela_"] .dtb-title {
color: #004a6a;
font-size: 34px;
font-weight: 800;
text-align: center;
margin-bottom: 10px;
}
div[id^="dashTabela_"] table th,
div[id^="dashTabela_"] table td {
vertical-align: top;
}
div[id^="dashTabela_"] table th {
color: #0a4f70;
}
div[id^="dashTabela_"] table th:nth-child(1),
div[id^="dashTabela_"] table td:nth-child(1),
div[id^="dashTabela_"] table th:nth-child(2),
@@ -23,7 +41,7 @@ div[id^="dashTabela_"] .dash-table-footer {
justify-content: space-between;
gap: 10px;
padding-top: 8px;
border-top: 1px solid #eee;
border-top: 1px solid #d4d9dd;
}
div[id^="dashTabela_"] .dash-page-size {
@@ -44,10 +62,20 @@ div[id^="dashTabela_"] .dash-page-size select {
div[id^="dashTabela_"] .dash-page-info {
font-size: 12px;
color: #666;
color: #44525a;
}
div[id^="dashTabela_"] .dash-page-actions {
display: flex;
gap: 6px;
}
@media (max-width: 1300px) {
div[id^="dashTabela_"] .dtb-title {
font-size: 24px;
}
div[id^="dashTabela_"] .dash-table-footer {
flex-wrap: wrap;
}
}
@@ -1,34 +1,35 @@
<div id="filtroDash_${instanceId}"
<div id="filtroDash_${instanceId}"
class="super-widget wcm-widget-class fluig-style-guide"
data-params="FiltroDash.instance()">
<div class="fdash-wrap">
<div class="fdash-title">AUDITORIAS DE LOJA - IAF 2026</div>
<div class="row">
<div class="col-md-3">
<label>Data início</label>
<input type="date" class="form-control" id="dtInicio_${instanceId}">
</div>
<div class="col-md-3">
<label>Data fim</label>
<input type="date" class="form-control" id="dtFim_${instanceId}">
</div>
<div class="col-md-4">
<label>Filial</label>
<input class="form-control" id="filial_${instanceId}" list="filial_list_${instanceId}" placeholder="Todas">
<datalist id="filial_list_${instanceId}">
<option value="Todas"></option>
</datalist>
</div>
<div class="col-md-2">
<label>&nbsp;</label>
<button type="button" class="btn btn-primary btn-block" data-filtrar>
Filtrar
</button>
</div>
</div>
<div class="fdash-filters-row">
<div class="fdash-filter-card">
<div class="fdash-filter-label">CICLO</div>
<div id="chips_ciclo_${instanceId}" class="fdash-chips"></div>
</div>
<div class="fdash-filter-card">
<div class="fdash-filter-label">REGIONAL</div>
<div id="chips_regional_${instanceId}" class="fdash-chips"></div>
</div>
<div class="fdash-filter-card">
<div class="fdash-filter-label">GESTOR</div>
<div id="chips_supervisao_${instanceId}" class="fdash-chips"></div>
</div>
</div>
<div class="fdash-actions">
<div class="fdash-date">
<label for="dtInicio_${instanceId}">Data inicio</label>
<input type="date" class="form-control" id="dtInicio_${instanceId}">
</div>
<div class="fdash-date">
<label for="dtFim_${instanceId}">Data fim</label>
<input type="date" class="form-control" id="dtFim_${instanceId}">
</div>
<button type="button" id="limpar_${instanceId}" class="btn btn-default">Limpar</button>
<button type="button" id="filtrar_${instanceId}" class="btn btn-primary">Aplicar filtros</button>
</div>
</div>
</div>
@@ -1,7 +1,131 @@
div[id^="filtroDash_"] input[list] {
padding-right: 30px;
div[id^="filtroDash_"] .fdash-wrap {
background: #9d9d9f;
border-radius: 16px;
padding: 12px;
color: #ffffff;
}
div[id^="filtroDash_"] .btn[data-filtrar] {
font-weight: 600;
div[id^="filtroDash_"] .fdash-title {
text-align: center;
font-size: 30px;
line-height: 1.12;
font-weight: 800;
margin-bottom: 10px;
letter-spacing: 0.5px;
}
div[id^="filtroDash_"] .fdash-filters-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
div[id^="filtroDash_"] .fdash-filter-card {
background: #ececed;
border-radius: 12px;
padding: 8px;
}
div[id^="filtroDash_"] .fdash-filter-label {
color: #004a6a;
font-size: 15px;
font-weight: 800;
margin-bottom: 6px;
}
div[id^="filtroDash_"] .fdash-chips {
display: flex;
flex-wrap: wrap;
gap: 5px;
min-height: 36px;
}
div[id^="filtroDash_"] .fdash-chip {
border: 1px solid #0a4f70;
background: #0a5d82;
color: #ffffff;
padding: 3px 8px;
border-radius: 6px;
font-weight: 700;
font-size: 12px;
cursor: pointer;
}
div[id^="filtroDash_"] .fdash-chip.is-off {
background: #f0f3f5;
color: #1c4a62;
}
div[id^="filtroDash_"] .fdash-actions {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 8px;
margin-top: 8px;
}
div[id^="filtroDash_"] .fdash-date {
min-width: 160px;
}
div[id^="filtroDash_"] .fdash-date label {
margin: 0 0 2px 0;
color: #ffffff;
font-weight: 700;
font-size: 12px;
}
div[id^="filtroDash_"] .fdash-date input {
border-radius: 8px;
height: 34px;
font-size: 13px;
}
div[id^="filtroDash_"] .fdash-actions .btn {
font-weight: 700;
border-radius: 8px;
min-height: 34px;
padding: 6px 12px;
font-size: 13px;
}
div[id^="filtroDash_"] .fdash-actions button[id^="limpar_"] {
background: #e6ebef;
border-color: #c7d2da;
color: #1c4a62;
}
div[id^="filtroDash_"] .fdash-actions button[id^="filtrar_"] {
background: #0a5d82;
border-color: #0a4f70;
color: #ffffff;
}
div[id^="filtroDash_"] .fdash-actions button[id^="filtrar_"]:hover,
div[id^="filtroDash_"] .fdash-actions button[id^="filtrar_"]:focus {
background: #084d6c;
border-color: #083f58;
}
@media (max-width: 1300px) {
div[id^="filtroDash_"] .fdash-title {
font-size: 24px;
}
div[id^="filtroDash_"] .fdash-filters-row {
grid-template-columns: 1fr;
}
div[id^="filtroDash_"] .fdash-filter-label {
font-size: 14px;
}
div[id^="filtroDash_"] .fdash-chip {
font-size: 12px;
}
div[id^="filtroDash_"] .fdash-actions {
flex-wrap: wrap;
justify-content: flex-start;
}
}
@@ -1,111 +1,139 @@
var FiltroDash = SuperWidget.extend({
lojasCarregadas:[],
init: function(){
console.log("FiltroDash carregado");
this.carregarLojas();
dadosBase: [],
selecionados: {
ciclo: {},
regional: {},
supervisao: {}
},
bindings: {
local:{
'filtrar': ['click_filtrar']
}
},
carregarLojas: function(){
init: function () {
var self = this;
this.carregarBase(function () {
self.renderFiltros();
self.aplicar();
});
$("#filtrar_" + this.instanceId).on("click", function () {
self.aplicar();
});
$("#limpar_" + this.instanceId).on("click", function () {
self.limpar();
});
},
carregarBase: function (done) {
var self = this;
var dataList = $("#filial_list_"+this.instanceId);
$.ajax({
url: "/api/public/ecm/dataset/search",
type: "POST",
contentType: "application/json",
data: JSON.stringify({
datasetId: "ds_lojas_api",
constraints: [],
fields: [],
order: []
datasetId: "ds_dashboard_checklist"
}),
success: function(response){
var dados = response.content || [];
self.lojasCarregadas = dados.map(function(loja){
return String(loja.LOJA || "").trim();
}).filter(function(v){ return !!v; });
self.lojasCarregadas.sort(function(a,b){
return a.localeCompare(b);
success: function (response) {
self.dadosBase = (response.content || []).filter(function (item) {
var st = normalizaTexto(item.saidaAnalise || item.status || "");
return st === "CONFORME" || st === "NAO_CONFORME";
});
self.lojasCarregadas.forEach(function(loja){
dataList.append(
'<option value="'+loja+'"></option>'
);
});
done();
},
error: function () {
self.dadosBase = [];
done();
}
});
},
filtrar:function(){
renderFiltros: function () {
var ciclos = this.valoresUnicos(function (x) { return x.ciclo; });
var regionais = this.valoresUnicos(function (x) { return x.regional; });
var supervisoes = this.valoresUnicos(function (x) { return x.responsavelLoja; });
console.log("Filtrar clicado");
this.renderChips("ciclo", ciclos, "#chips_ciclo_" + this.instanceId);
this.renderChips("regional", regionais, "#chips_regional_" + this.instanceId);
this.renderChips("supervisao", supervisoes, "#chips_supervisao_" + this.instanceId);
},
var lojaDigitada = String($("#filial_"+this.instanceId).val() || "").trim();
valoresUnicos: function (getter) {
var mapa = {};
this.dadosBase.forEach(function (item) {
var v = String(getter(item) || "").trim();
if (!v) {
return;
}
mapa[v] = true;
});
return Object.keys(mapa).sort(function (a, b) {
return a.localeCompare(b);
});
},
var filtros = {
renderChips: function (tipo, valores, alvoSelector) {
var self = this;
var alvo = $(alvoSelector);
alvo.empty();
dataInicio: $("#dtInicio_"+this.instanceId).val(),
dataFim: $("#dtFim_"+this.instanceId).val(),
loja: this.resolverLoja(lojaDigitada)
if (!valores.length) {
alvo.append('<span class="text-muted">Sem dados</span>');
return;
}
valores.forEach(function (valor) {
var chip = $("<button>")
.attr("type", "button")
.addClass("fdash-chip")
.addClass("is-off")
.attr("data-tipo", tipo)
.attr("data-valor", valor)
.text(valor);
chip.on("click", function () {
var bucket = self.selecionados[tipo];
if (bucket[valor]) {
delete bucket[valor];
chip.addClass("is-off");
} else {
bucket[valor] = true;
chip.removeClass("is-off");
}
self.aplicar();
});
alvo.append(chip);
});
},
limpar: function () {
this.selecionados = {
ciclo: {},
regional: {},
supervisao: {}
};
console.log("Filtros enviados:", filtros);
window.dispatchEvent(
new CustomEvent("dashboardFiltro",{detail:filtros})
);
$("#dtInicio_" + this.instanceId).val("");
$("#dtFim_" + this.instanceId).val("");
$("#filtroDash_" + this.instanceId + " .fdash-chip").addClass("is-off");
this.aplicar();
},
resolverLoja:function(lojaDigitada){
if(!lojaDigitada){
return "";
}
var buscado = lojaDigitada.toUpperCase();
if(buscado === "TODAS" || buscado === "TODOS"){
return "";
}
var lojas = this.lojasCarregadas || [];
for(var i=0;i<lojas.length;i++){
if(lojas[i].toUpperCase() === buscado){
return lojas[i];
}
}
for(var j=0;j<lojas.length;j++){
if(lojas[j].toUpperCase().indexOf(buscado) >= 0){
return lojas[j];
}
}
return lojaDigitada;
aplicar: function () {
var filtros = {
dataInicio: $("#dtInicio_" + this.instanceId).val(),
dataFim: $("#dtFim_" + this.instanceId).val(),
ciclo: Object.keys(this.selecionados.ciclo),
regional: Object.keys(this.selecionados.regional),
supervisao: Object.keys(this.selecionados.supervisao)
};
window.dispatchEvent(new CustomEvent("dashboardFiltro", { detail: filtros }));
}
});
function normalizaTexto(valor) {
return String(valor || "").trim().toUpperCase();
}