This commit is contained in:
2026-03-10 18:34:47 -03:00
parent 0754466359
commit b32415212f
165 changed files with 14613 additions and 2352 deletions
+2168
View File
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,3 @@
eclipse.preferences.version=1
org.eclipse.debug.ui.PREF_LAUNCH_PERSPECTIVES=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?>\r\n<launchPerspectives/>\r\n
preferredTargets=default\:default|default,org.eclipse.wst.jsdt.chromium.debug.ui.toggleTargetId\:default|
preferredTargets=default\:default|default,org.eclipse.wst.jsdt.chromium.debug.ui.toggleTargetId\:default|org.eclipse.wst.jsdt.chromium.debug.ui.toggleTargetId\:org.eclipse.wst.jsdt.chromium.debug.ui.toggleTargetId|
@@ -1,3 +1,3 @@
browsers=<?xml version\="1.0" encoding\="UTF-8"?>\r\n<web-browsers current\="0">\r\n<system/>\r\n<external location\="C\:\\Program Files\\Internet Explorer\\iexplore.exe" name\="Internet Explorer"/>\r\n<external location\="C\:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" name\="Chrome"/>\r\n</web-browsers>
eclipse.preferences.version=1
internalWebBrowserHistory=file\:///C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/layout/TIginseng_layoutblocado/src/main/webapp/resources/images/icon.png|*|file\:/C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/layout/TIginseng_layoutblocado/src/main/webapp/resources/images/icon.png|*|file\:///C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/widget/widget_code_compras/src/main/webapp/resources/images/icon.png|*|file\:/C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/widget/widget_code_compras/src/main/webapp/resources/images/icon.png|*|
internalWebBrowserHistory=file\:///C\:/Users/andrey.cunha/eclipse-workspace.new/Auditoria_checklist/wcm/widget/helloWidget/src/main/webapp/resources/images/dev.png|*|file\:/C\:/Users/andrey.cunha/eclipse-workspace.new/Auditoria_checklist/wcm/widget/helloWidget/src/main/webapp/resources/images/dev.png|*|file\:///C\:/Users/andrey.cunha/eclipse-workspace.new/Auditoria_checklist/wcm/widget/helloWidget/src/main/webapp/resources/images/icon.png|*|file\:/C\:/Users/andrey.cunha/eclipse-workspace.new/Auditoria_checklist/wcm/widget/helloWidget/src/main/webapp/resources/images/icon.png|*|file\:///C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/layout/TIginseng_layoutblocado/src/main/webapp/resources/images/icon.png|*|file\:/C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/layout/TIginseng_layoutblocado/src/main/webapp/resources/images/icon.png|*|file\:///C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/widget/widget_code_compras/src/main/webapp/resources/images/icon.png|*|file\:/C\:/Users/andrey.cunha/eclipse-workspace.new/Ginseng_chamados/wcm/widget/widget_code_compras/src/main/webapp/resources/images/icon.png|*|
File diff suppressed because one or more lines are too long
@@ -183,3 +183,5 @@
2026-02-23 10:29:20,949 [Worker-4: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
2026-02-24 10:35:56,560 [Worker-3: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
2026-02-27 16:07:17,391 [Worker-3: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
2026-03-05 14:13:17,734 [Worker-6: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
2026-03-06 09:35:04,306 [Worker-6: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
@@ -1,22 +1,12 @@
INDEX VERSION 1.4+C:\Users\andrey.cunha\eclipse-workspace.new\.metadata\.plugins\org.eclipse.wst.jsdt.core\indexes
3963123655.index
3049976914.index
1889139231.index
2056597484.index
1252593962.index
160423874.index
3477032517.index
3528551409.index
17091598.index
4202715187.index
3974601650.index
1053469344.index
3371662347.index
3537614094.index
1338284093.index
1604404410.index
2070855566.index
1999126708.index
707046764.index
223663271.index
1180771715.index
2887418869.index
3963123655.index
7156200.index
2938933076.index
3537614094.index
1300356610.index
3826028853.index
3670622443.index
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Auditoria</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
<nature>com.totvs.tds.ecm.designer.nature</nature>
</natures>
</projectDescription>
+118
View File
@@ -0,0 +1,118 @@
function createDataset(fields, constraints, sortFields) {
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("PDV");
dataset.addColumn("LOJA");
dataset.addColumn("RESPONSAVEL_LOJA");
dataset.addColumn("REGIONAL");
dataset.addColumn("UF");
dataset.addColumn("CIDADE");
dataset.addColumn("STATUS");
dataset.addColumn("CANAL");
dataset.addColumn("ID");
dataset.addColumn("LOGIN_LOJA");
dataset.addColumn("COLLEAGUE_ID");
try {
var clientService = fluigAPI.getAuthorizeClientService();
var data = {
companyId: String(getValue("WKCompany") || "1"),
serviceCode: "GINSENG API",
endpoint: "/base_pdvs",
method: "get",
timeoutService: "60000",
params: {}
};
var vo = clientService.invoke(JSON.stringify(data));
var response = String(vo.getResult() || "");
var obj = JSON.parse(response);
var list = (obj && obj.data && obj.data.length) ? obj.data : [];
if (!list.length) {
dataset.addRow(["", "Sem lojas retornadas", "", "", "", "", "", "", "", "", ""]);
return dataset;
}
var filter = getSearchFilter(constraints);
for (var i = 0; i < list.length; i++) {
var item = list[i] || {};
var pdv = clean(item.PDV);
var loja = clean(item["PDV DESC"] || item.DESCRICAO);
var responsavel = clean(item.GESTAO);
var regional = clean(item.SUPERVISOR || item["REGIAO 2"] || item.REGIAO);
var uf = clean(item.UF || item["UF 2"]);
var cidade = clean(item.CIDADE);
var status = clean(item.STATUS);
var canal = clean(item.CANAL);
var id = clean(item.id);
var loginLoja = pdv ? (pdv + ".ginseng") : "";
var colleagueId = resolveColleagueId(loginLoja);
if (!matchesFilter(filter, [pdv, loja, responsavel, regional, uf, cidade, status, canal, loginLoja, colleagueId])) {
continue;
}
dataset.addRow([
pdv,
loja,
responsavel,
regional,
uf,
cidade,
status,
canal,
id,
loginLoja,
colleagueId
]);
}
} catch (e) {
dataset = DatasetBuilder.newDataset();
dataset.addColumn("ERRO");
dataset.addRow([String(e)]);
}
return dataset;
}
function resolveColleagueId(login) {
try {
var c1 = DatasetFactory.createConstraint("login", login, login, ConstraintType.MUST);
var c2 = DatasetFactory.createConstraint("active", "true", "true", ConstraintType.MUST);
var ds = DatasetFactory.getDataset("colleague", null, [c1, c2], null);
if (ds && ds.rowsCount > 0) {
return clean(ds.getValue(0, "colleaguePK.colleagueId"));
}
} catch (e) {}
return "";
}
function getSearchFilter(constraints) {
if (!constraints || !constraints.length) return "";
for (var i = 0; i < constraints.length; i++) {
var c = constraints[i];
var field = String(c.fieldName || "").toUpperCase();
if (field === "SEARCH" || field === "LOJA" || field === "PDV") {
return normalizeSearch(c.initialValue);
}
}
return "";
}
function matchesFilter(filter, values) {
if (!filter) return true;
for (var i = 0; i < values.length; i++) {
if (normalizeSearch(values[i]).indexOf(filter) > -1) return true;
}
return false;
}
function normalizeSearch(text) {
var t = clean(text).toUpperCase();
t = t.replace(/[%*_]/g, "");
return t;
}
function clean(value) {
return String(value || "").trim();
}
Binary file not shown.
@@ -0,0 +1,585 @@
body { background: #ffffff; }
.audit-shell {
background: #f4f8fd;
border: 1px solid #dbe4ee;
border-radius: 14px;
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.06);
padding: 10px 10px 14px 10px;
margin: 14px auto 0 auto;
max-width: 1140px;
}
.audit-section {
border: 1px solid #e2e8f0;
border-radius: 14px;
background: #ffffff;
padding: 12px;
margin-bottom: 10px;
box-shadow: 0 2px 6px rgba(15, 23, 42, 0.05);
}
h1.audit-main-title {
margin: 2px 0 10px 0;
font-size: 18px !important;
line-height: 1.2;
font-weight: 700 !important;
color: #ffffff !important;
background: #0b556b;
border-radius: 10px;
padding: 8px 12px;
}
.section-title {
margin: 0 0 10px 0;
font-size: 20px;
font-weight: 700;
color: #1e293b;
}
.audit-status {
display: none;
}
.progress-section {
padding: 5px 10px 5px 10px;
margin-top: 2px;
}
.progress-section .section-title {
font-size: 16px;
margin-bottom: 6px;
}
.audit-context-card .section-title {
font-size: 17px;
}
.progress-inline {
display: grid;
grid-template-columns: 1fr auto;
gap: 16px;
align-items: center;
}
.progress-track {
width: 100%;
height: 5px;
border-radius: 999px;
background: #e6ebf2;
overflow: hidden;
}
.progress-fill {
width: 0%;
height: 100%;
background: linear-gradient(90deg, #3ea9c8 0%, #3ea9c8 100%);
transition: width .18s ease-in-out;
}
.progress-percent {
font-size: 14px;
line-height: 1;
color: #0f172a;
font-weight: 700;
min-width: 42px;
text-align: right;
}
.progress-meta {
margin-top: 3px;
font-size: 9px;
color: #52637b;
font-weight: 600;
}
.instruction-section {
display: block;
background: #ffffff;
}
.intro-data-section {
padding: 0;
overflow: hidden;
background: linear-gradient(90deg, #f4f8fd 0%, #edf3fa 100%);
}
.intro-data-section .instruction-section {
padding: 12px;
border-radius: 14px 14px 0 0;
background: #ffffff;
}
.intro-divider {
height: 1px;
background: #dfe7f0;
}
.instruction-head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 6px;
}
.instruction-icon {
width: 34px;
height: 34px;
border-radius: 999px;
background: #3ea9c8;
color: #fff;
font-size: 22px;
font-weight: 700;
line-height: 34px;
text-align: center;
}
.instruction-title {
font-size: 16px;
color: #1f2f46;
font-weight: 700;
margin-bottom: 0;
}
.instruction-steps {
display: grid;
grid-template-columns: repeat(3, minmax(160px, 1fr));
gap: 6px 16px;
font-size: 10px;
color: #334155;
line-height: 1.3;
}
.instruction-steps span {
display: inline-block;
border: 1px solid #b9d4ea;
background: #f4f9ff;
color: #0f3f66;
border-radius: 999px;
padding: 4px 9px;
width: fit-content;
}
.status-pill {
background: #eef5fb;
border: 1px solid #d9e8f6;
color: #0b556b;
border-radius: 999px;
padding: 6px 10px;
font-size: 12px;
font-weight: 600;
}
.audit-context-card {
background: #ffffff;
padding: 12px;
border-radius: 0 0 14px 14px;
}
.audit-context-row > div {
margin-bottom: 8px;
}
.audit-label {
font-size: 12px;
letter-spacing: 0.3px;
color: #16486a;
margin-bottom: 4px;
font-weight: 700;
}
.label-icon { opacity: 0.8; margin-right: 4px; }
.audit-input {
height: 32px;
font-size: 12px;
border-color: #c9d7e7;
border-radius: 7px;
padding: 5px 9px;
}
.audit-input:focus {
border-color: #74a5d3;
box-shadow: 0 0 0 2px rgba(34, 93, 143, 0.12);
}
.audit-card { margin-top: 12px; }
.audit-score { font-size: 22px; font-weight: 700; }
.audit-score small { font-size: 12px; font-weight: 400; color: #7f8c8d; display: block; }
#pilaresContainer {
display: grid;
grid-template-columns: repeat(4, minmax(220px, 1fr));
gap: 14px;
background: #f4f8fd;
border: 0;
border-radius: 10px;
padding: 8px 12px 10px 12px;
}
h2.section-title.pilares-title {
margin-top: 4px;
margin-bottom: 10px;
font-size: 17px !important;
}
.audit-send-wrap {
margin-top: 10px;
text-align: right;
padding: 10px 12px 2px 12px;
border-top: 1px solid #dbe5f0;
}
.audit-send-btn {
min-width: 120px;
height: 38px;
border-radius: 8px;
font-size: 14px;
font-weight: 700;
background: #0b556b;
border-color: #0b556b;
}
.audit-send-btn:hover,
.audit-send-btn:focus {
background: #0a4b5f;
border-color: #0a4b5f;
}
.pilar-panel {
border: 1px solid #d6e1ee;
border-radius: 12px;
background: #ffffff;
padding: 8px;
min-height: 108px;
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.08);
transition: box-shadow .18s ease, transform .18s ease;
}
.pilar-panel:hover {
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.12);
transform: translateY(-1px);
}
.pilar-panel.is-open {
grid-column: 1 / -1;
box-shadow: 0 12px 24px rgba(15, 23, 42, 0.14);
}
.pilar-summary-card {
grid-column: span 3;
border: 1px solid #d6e1ee;
border-radius: 12px;
background: #ffffff;
padding: 10px 12px;
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.08);
}
.summary-title {
font-size: 13px;
font-weight: 700;
color: #000000;
margin-bottom: 8px;
}
.summary-item {
font-size: 12px;
line-height: 1.45;
color: #000000;
margin-bottom: 6px;
}
.summary-item:last-child {
margin-bottom: 0;
}
.summary-name {
color: #000000;
font-weight: 700;
}
.pilar-head {
background: #ffffff;
border: 0;
padding: 6px;
display: grid;
gap: 6px;
cursor: pointer;
border-radius: 10px;
transition: background .15s ease, border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.pilar-head:hover {
background: #f8fbff;
box-shadow: none;
transform: none;
}
.pilar-head.is-open {
background: #f4f8fd;
box-shadow: none;
}
.pilar-title-row {
display: flex;
align-items: center;
gap: 10px;
}
.pilar-state-dot {
width: 18px;
height: 18px;
border-radius: 999px;
background: #94a3b8;
border: 1px solid rgba(148, 163, 184, 0.25);
display: inline-flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 11px;
font-weight: 700;
line-height: 1;
}
.pilar-state-dot.state-pending { background: #dc2626; border-color: rgba(220, 38, 38, 0.4); }
.pilar-state-dot.state-progress { background: #d18a2f; border-color: rgba(209, 138, 47, 0.35); }
.pilar-state-dot.state-done { background: #2f9d78; border-color: rgba(47, 157, 120, 0.35); }
.pilar-title {
font-size: 13px;
font-weight: 700;
color: #000000;
letter-spacing: 0.2px;
}
.pilar-photo-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
color: #243549;
}
.pilar-photo-count {
font-size: 10px;
font-weight: 700;
color: #000000;
}
.pilar-missing {
font-size: 10px;
color: #000000;
font-weight: 600;
}
.pilar-mini-track {
height: 5px;
border-radius: 999px;
background: #e4eaf1;
overflow: hidden;
}
.pilar-mini-fill {
display: block;
width: 0%;
height: 100%;
border-radius: inherit;
background: #5d8fbe;
transition: width .18s ease;
}
.pilar-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.pilar-count {
font-size: 10px;
color: #000000;
font-weight: 600;
}
.pilar-open-cta {
font-size: 10px;
color: #000000;
font-weight: 700;
}
.pilar-body { padding: 8px 0 0 0; }
.audit-row {
border: 1px solid #d4e0ee;
border-radius: 12px;
padding: 10px;
margin-bottom: 8px;
background: #f9fcff;
}
.audit-row-main {
display: flex;
flex-direction: column;
gap: 8px;
}
.audit-row-top {
display: grid;
grid-template-columns: 1fr 1.3fr;
gap: 10px;
align-items: start;
}
.audit-row[data-max-fotos="1"] .audit-row-top,
.audit-row[data-max-fotos="2"] .audit-row-top {
grid-template-columns: 1fr;
}
.audit-info {
min-width: 0;
}
.indicador-cell { font-weight: 700; color: #143653; font-size: 15px; line-height: 1.25; }
.indicador-help {
margin-top: 5px;
font-size: 12px;
color: #5c6f82;
line-height: 1.35;
background: #f7fbff;
border: 1px solid #dce8f5;
border-radius: 8px;
padding: 7px 9px;
}
.indicador-help ul {
margin: 0;
padding-left: 18px;
}
.indicador-help li {
margin: 2px 0;
}
.indicador-help .indicador-group {
list-style: none;
margin-top: 6px;
margin-left: -18px;
font-weight: 700;
color: #36597b;
}
.meta-grid {
display: grid;
grid-template-columns: repeat(5, minmax(100px, 1fr));
gap: 10px;
}
.meta-box {
border: 1px solid #d7e2ef;
border-radius: 10px;
background: #fff;
padding: 8px 10px;
min-height: 62px;
}
.meta-label { font-size: 11px; color: #64748b; text-transform: uppercase; }
.meta-value { font-size: 20px; color: #1f2937; margin-top: 4px; font-weight: 600; }
.meta-value .form-control {
font-size: 14px;
height: 34px;
padding: 4px 10px;
}
.anexo-wrap, .resultado-wrap, .just-wrap { margin-top: 0; }
.penalidade-badge {
font-size: 10px !important;
font-weight: 700;
padding: 2px 8px !important;
border-radius: 999px;
letter-spacing: 0.2px;
text-transform: uppercase;
}
.penalidade-badge.label-danger { background: #f7dada; color: #9f2d2d; }
.penalidade-badge.label-warning { background: #fff0c7; color: #8a5a00; }
.penalidade-badge.label-info { background: #dceefe; color: #1f5f9b; }
.penalidade-badge.label-success { background: #dff5e4; color: #1f7a3d; }
.penalidade-badge.label-default { background: #eceff3; color: #5f6b7a; }
.anexos-cell {
background: #edf4fb;
border: 1px solid #cfe0f3;
border-radius: 10px;
padding: 8px;
}
.anexo-auto-hint { font-size: 11px; color: #5f7d9a; margin-top: 5px; }
.anexo-progress {
margin-top: 7px;
font-size: 12px;
color: #2d4f70;
font-weight: 600;
}
.anexo-progress-count {
display: inline-block;
min-width: 40px;
text-align: center;
border-radius: 999px;
background: #d9e9fa;
color: #1d4f7c;
padding: 1px 8px;
margin-right: 4px;
}
.anexo-multi-list {
display: grid;
grid-template-columns: repeat(2, minmax(220px, 1fr));
gap: 7px;
}
.audit-row[data-max-fotos="1"] .anexo-multi-list,
.audit-row[data-max-fotos="2"] .anexo-multi-list {
grid-template-columns: 1fr;
}
.anexo-slot {
background: #ffffff;
border: 1px solid #c9dbef;
border-radius: 8px;
padding: 5px;
}
.audit-row[data-max-fotos="1"] .anexo-slot,
.audit-row[data-max-fotos="2"] .anexo-slot {
max-width: 100%;
}
.anexo-slot.slot-filled {
border-color: #8fc1a3;
box-shadow: inset 0 0 0 1px rgba(79, 161, 109, 0.2);
}
.anexo-slot.extra-slot { display: none; }
.anexo-multi-list.expanded .anexo-slot.extra-slot { display: block; }
.anexo-slot-label {
font-size: 11px;
color: #4b5f74;
margin-bottom: 3px;
font-weight: 600;
text-transform: uppercase;
}
.anexo-toggle-wrap {
margin-top: 8px;
text-align: right;
}
.anexo-toggle {
font-size: 12px;
font-weight: 600;
color: #24557d !important;
border-color: #b5cbe3;
background: #f4f8fd;
padding: 3px 10px;
}
.anexos-cell .fluigFormAttachmentComponent {
width: 100%;
}
.anexos-cell .fluigFormAttachmentComponent input {
height: 34px;
border-color: #c7d8ec;
background: #fff;
}
.anexos-cell .fluigFormAttachmentComponent .btn {
height: 34px !important;
min-width: 40px;
}
.just-wrap input {
border-radius: 10px;
}
.audit-row-target { background: #fff8e6 !important; }
.nc-box {
border: 1px solid #f5d0d0;
background: #fff6f6;
border-radius: 10px;
padding: 10px 12px;
margin-top: 10px;
}
.nc-title {
font-weight: 700;
color: #9f2d2d;
margin-bottom: 6px;
}
.nc-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.nc-item {
background: #fff;
border: 1px solid #f1b7b7;
color: #7f1d1d;
border-radius: 999px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
}
.anexo-view { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.anexo-thumb-inline {
width: 34px;
height: 34px;
object-fit: cover;
border: 1px solid #ddd;
border-radius: 4px;
background: #fff;
}
.anexo-file {
font-size: 11px;
max-width: 170px;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 4px 6px;
background: #fafafa;
}
.anexo-text {
background: #fff;
font-size: 12px;
}
@media (max-width: 900px) {
.section-title { font-size: 22px; }
.progress-percent { font-size: 14px; min-width: 38px; }
.progress-meta { font-size: 14px; }
.instruction-title { font-size: 16px; }
.instruction-steps { grid-template-columns: 1fr; font-size: 14px; }
.audit-label { font-size: 13px; }
.audit-input { height: 40px; font-size: 15px; }
.audit-row-top { grid-template-columns: 1fr; }
.meta-grid { grid-template-columns: repeat(2, minmax(100px, 1fr)); }
.anexo-multi-list { grid-template-columns: 1fr; }
#pilaresContainer { grid-template-columns: repeat(2, minmax(180px, 1fr)); }
.pilar-panel.is-open { grid-column: auto; }
.pilar-summary-card { grid-column: span 2; }
.audit-send-wrap { padding: 8px 0 0 0; border-top: 0; }
.audit-send-btn { width: 100%; }
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,423 @@
/**
* Plugin JQuery para trabalhar com anexos nos formulários dentro do processo
*
* @author Bruno Gasparetto
* @see https://github.com/brunogasparetto/fluig-form-attachment
*/
/**
* Configurações
*
* @typedef AttachmentSettings
* @property {boolean} showActionButton Exibe o botão de upload/delete. True por padrão.
* @property {boolean} filename Nome que será salvo como descrição do Anexo.
* @property {boolean|string} prefixName Adiciona prefixo ao anexo. False por padrão, True para prefixo aleatório, String para prefixo fixo.
* @property {string} accept Tipos de arquivos aceitos. Segue a regra do accept do input tipo file.
*/
;(function ($) {
"use strict";
const pluginName = "fluigFormAttachment";
const deleteFileClassName = "BtnDeleteFile";
const uploadFileClassname = "BtnUploadFile";
const viewerFileClassname = "BtnViewerFile";
const compressedExtensions = [
'.7z', '.zip', '.rar', '.gz', '.tar', '.tbz2', '.tgz', '.bz2', '.lz', '.lz4','.txz',
'.xz', '.z', '.zst', '.zstd', '.war', '.ear', '.jar','.apk', '.arj', '.ace', '.cab',
];
const isString = item => typeof item === "string";
/**
* Procura o índice do anexo de acordo com sua descrição
*
* @param {string} filename
* @returns {number} -1 se não encontrar
*/
const attachmentFindIndex = (filename) => parent.ECM.attachmentTable.getData().findIndex(attachment => attachment.description === filename);
/**
* Configuração padrão
*
* @type {AttachmentSettings}
*/
const defaults = {
showActionButton: true,
filename: "Anexo",
prefixName: false,
accept: "*",
};
class Plugin {
/**
* @type {AttachmentSettings}
*/
#settings;
/**
* Elemento do arquivo. Pode ser um input ou span (no modo leitura).
*
* @type {JQuery<HTMLElement>}
*/
#input;
/**
* @type {JQuery<HTMLElement>}
*/
#container;
/**
* @type {string}
*/
#attachmentFilename;
/**
* @param {HTMLElement} element
* @param {AttachmentSettings} options
*/
constructor(element, options) {
// Garantir um ID para o Input
if (!element.id && element.nodeName.toLowerCase() === "input") {
element.id = FLUIGC.utilities.randomUUID();
}
this.#settings = $.extend({}, defaults, options);
this.#input = $(element);
this.#attachmentFilename = this.#input.val() || this.#input.text().trim();
this.#input
.prop("readonly", true)
.on("change", () => {
this.#attachmentFilename = this.#input.val();
this.#changeButtonsState();
})
.wrap(`<div class="${pluginName}Component"></div>`)
.after(`<div class="${pluginName}Component_buttons">${this.#getButtonsTemplate()}</div>`);
this.#container = this.#input.closest(`.${pluginName}Component`);
this.#container
.on("click", `.${pluginName}${deleteFileClassName}`, () => this.#confirmDeleteAttachment())
.on("click", `.${pluginName}${uploadFileClassname}`, () => this.#uploadAttachment())
.on("click", `.${pluginName}${viewerFileClassname}`, () => this.#viewAttachment())
;
}
/**
* Indica que o campo está válido
*
* Caso o campo possua algum valor é obrigatório que o anexo
* esteja na tabela de anexos.
*
* @returns {boolean}
*/
isValid() {
return this.#attachmentFilename.length
? this.hasAttachment()
: true
;
}
/**
* Indica se o anexo está na tabela de anexos
*
* @returns {boolean}
*/
hasAttachment() {
const filename = this.#attachmentFilename || this.#input.val() || this.#input.text().trim();
return filename.length > 0 && attachmentFindIndex(filename) !== -1;
}
/**
* Remove o anexo
*
* Método útil para excluir anexos em tabela Pai x Filho.
*/
deleteAttachment() {
const attachmentIndex = parent.ECM.attachmentTable.getData().findIndex(
attachment => attachment.description === this.#attachmentFilename
);
setTimeout(() => this.#input.val("").trigger("change"), 500);
if (attachmentIndex === -1) {
return;
}
parent.WKFViewAttachment.removeAttach([attachmentIndex]);
}
showActionButton() {
this.#settings.showActionButton = true;
this.#input.trigger("change");
}
hideActionButton() {
this.#settings.showActionButton = false;
this.#input.trigger("change");
}
filename(fileName, prefixName) {
if (fileName === undefined) {
return this.#input.data("filename") || this.#settings.filename;
}
this.#settings.filename = fileName;
this.#input.data("filename", fileName);
if (prefixName !== undefined) {
this.prefixName(prefixName);
}
}
prefixName(prefixName) {
if (prefixName === undefined) {
return this.#settings.prefixName;
}
this.#settings.prefixName = prefixName;
}
#getButtonsTemplate() {
const hasFileSelected = this.#attachmentFilename.length !== 0;
const canShowActionButton = this.#canDisplayActionButton();
return `<button type="button" class="${pluginName}BtnAction ${pluginName}${deleteFileClassName} btn btn-danger btn-sm ${(canShowActionButton && hasFileSelected) ? '' : 'hide'}" title="Remover Anexo"><i class="flaticon flaticon-trash icon-sm"></i></button>`
+ `<button type="button" class="${pluginName}BtnAction ${pluginName}${uploadFileClassname} btn btn-success btn-sm ${(canShowActionButton && !hasFileSelected) ? '' : 'hide'}" title="Enviar Anexo"><i class="flaticon flaticon-upload icon-sm"></i></button>`
+ `<button type="button" class="${pluginName}${viewerFileClassname} btn btn-info btn-sm ${hasFileSelected ? '' : 'hide'}" title="Visualizar Anexo"><i class="flaticon flaticon-view icon-sm"></i></button>`
;
}
#canDisplayActionButton() {
const element = this.#input.get(0);
return this.#settings.showActionButton
&& parent.ECM.workflowView.userPermissions.indexOf("P") >= 0
&& location.href.includes('ManagerMode')
&& !location.href.includes('token')
&& element.nodeName.toLowerCase() === "input"
&& !element.disabled
;
}
#changeButtonsState() {
const hasFileSelected = this.#attachmentFilename.length !== 0;
if (this.#canDisplayActionButton()) {
if (hasFileSelected) {
this.#container.find(`.${pluginName}${uploadFileClassname}`).addClass("hide");
this.#container.find(`.${pluginName}${deleteFileClassName}`).removeClass("hide");
} else {
this.#container.find(`.${pluginName}${deleteFileClassName}`).addClass("hide");
this.#container.find(`.${pluginName}${uploadFileClassname}`).removeClass("hide");
}
} else {
this.#container.find(`.${pluginName}BtnAction`).addClass("hide");
}
if (hasFileSelected) {
this.#container.find(`.${pluginName}${viewerFileClassname}`).removeClass("hide");
} else {
this.#container.find(`.${pluginName}${viewerFileClassname}`).addClass("hide");
}
}
#confirmDeleteAttachment() {
if (!this.#canDisplayActionButton()) {
return;
}
FLUIGC.message.confirm({
message: `Deseja remover o anexo <b>${this.#attachmentFilename}</b>?`,
title: 'Confirmação',
labelYes: 'Sim, quero remover',
labelNo: 'Não, quero cancelar',
}, result => {
if (!result) {
return;
}
this.deleteAttachment();
});
}
#uploadAttachment() {
if (!this.#canDisplayActionButton()) {
return;
}
let filename = this.#input.data("filename") || this.#settings.filename;
if (this.#settings.prefixName === true) {
filename = FLUIGC.utilities.randomUUID().substring(0, 9) + filename;
} else if (this.#settings.prefixName !== false && isString(this.#settings.prefixName)) {
filename = `${this.#settings.prefixName}-${filename}`;
}
// Evitar conflito de descrição do anexo
if (attachmentFindIndex(filename) !== -1) {
FLUIGC.toast({
title: "Atenção",
message: "Já existe um anexo com essa descrição",
type: "warning",
})
return;
}
parent.$("#ecm-navigation-inputFile-clone")
.attr({
"data-on-camera": "true",
"data-file-name-camera": filename,
"data-inputid": this.#input.attr("id"),
"data-filename": filename,
"multiple": false,
"accept": this.#input.data("accept") || this.#settings.accept,
})
.trigger("click")
;
}
#viewAttachment() {
const attachmentIndex = parent.ECM.attachmentTable.getData().findIndex(
attachment => attachment.description === this.#attachmentFilename
);
if (attachmentIndex === -1) {
FLUIGC.toast({
title: "Atenção",
message: "Anexo não encontrado",
type: "warning"
});
return;
}
const attachment = parent.ECM.attachmentTable.getRow(attachmentIndex);
const physicalFileName = String(
attachment.physicalFileName || attachment.fileName || attachment.name || ""
).toLowerCase();
const isCompressedFile = compressedExtensions.some(extension => physicalFileName.endsWith(extension));
if (attachment.documentId && !isCompressedFile) {
parent.WKFViewAttachment.openAttachmentView(parent.WCMAPI.userCode, attachment.documentId, attachment.version);
} else {
parent.WKFViewAttachment.downloadAttach([attachmentIndex]);
}
}
}
/**
* Instancia o Plugin ou executa algum método do plugin
*
* @param {AttachmentSettings|string} options
* @returns {undefined|boolean|void}
*/
$.fn[pluginName] = function (options) {
if (!parent.WKFViewAttachment || !parent.ECM || !parent.ECM.attachmentTable) {
console.error(`Plugin ${pluginName} executado fora de um processo.`)
return this;
}
// Executa o Método
if (isString(options)) {
const methodName = options;
const methodArgs = Array.prototype.slice.call(arguments, 1);
let returnedValue = undefined;
this.each(function () {
let pluginData = $.data(this, pluginName);
if (!pluginData) {
pluginData = new Plugin(this, {});
$.data(this, pluginName, pluginData);
}
if (!pluginData[methodName]) {
return;
}
returnedValue = pluginData[methodName](...methodArgs);
if (returnedValue !== undefined) {
return false;
}
});
return returnedValue !== undefined
? returnedValue
: this
;
}
return this.each(function () {
if (!$.data(this, pluginName)) {
$.data(this, pluginName, new Plugin(this, options));
}
});
};
if (!parent.WKFViewAttachment || !parent.ECM || !parent.ECM.attachmentTable) {
return;
}
const loading = FLUIGC.loading(window, {
title: "Aguarde",
textMessage: "Enviando arquivo",
})
$(() => {
// Oculta aba anexos
$("#tab-attachments", parent.document).hide();
parent.$("#ecm_navigation_fileupload")
.on(`fileuploadadd.${pluginName}`, function(e, data) {
// Impede abrir o Loading caso tenha erro no arquivo
const file = data.files[0];
if (parent.ECM.maxUploadSize > 0 && file.size >= (parent.ECM.maxUploadSize * 1024 * 1024)) {
return;
}
if (parent.ECM.newAttachmentsDocs.length
&& parent.ECM.newAttachmentsDocs.findIndex(attachment => attachment.name === file.name) !== -1
) {
return;
}
loading.show();
})
.on(`fileuploadfail.${pluginName}`, () => loading.hide())
.on(`fileuploaddone.${pluginName}`, function() {
// Atualiza o campo do arquivo caso o upload tenha ocorrido
loading.hide();
const btnUpload = parent.document.getElementById("ecm-navigation-inputFile-clone");
const filename = btnUpload.getAttribute("data-filename");
if (attachmentFindIndex(filename) === -1) {
return;
}
$(`#${btnUpload.getAttribute("data-inputid")}`).val(filename).trigger("change");
});
parent.$(document).on(`fileuploadstop.${pluginName}`, () => loading.hide());
});
$("head").append(`<style>
.${pluginName}Component { display: flex; align-items: center; flex-wrap: nowrap; }
.${pluginName}Component input { border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; }
.${pluginName}Component_buttons { display: flex; align-items: center; justify-content: flex-end; }
.${pluginName}Component_buttons .btn { outline: none !important; outline-offset: unset !important; border-radius: 0 !important; height: 32px; }
</style>`);
}(jQuery));
File diff suppressed because one or more lines are too long
@@ -0,0 +1,144 @@
<html>
<head>
<link type="text/css" rel="stylesheet" href="/style-guide/css/fluig-style-guide.min.css" />
<script type="text/javascript" src="/portal/resources/js/jquery/jquery.js"></script>
<script type="text/javascript" src="/portal/resources/js/jquery/jquery-ui.min.js"></script>
<script type="text/javascript" src="/portal/resources/js/mustache/mustache-min.js"></script>
<script type="text/javascript" src="/style-guide/js/fluig-style-guide.min.js" charset="utf-8"></script>
<script type="text/javascript" src="./fluigFormAttachment.js"></script>
<link type="text/css" rel="stylesheet" href="./assets/css/checklist.css" />
</head>
<body>
<div class="fluig-style-guide">
<form name="form" role="form">
<div class="container-fluid audit-shell">
<div class="audit-status">
<span class="status-pill" id="profileBadge">Perfil: Loja</span>
<span class="status-pill" id="progressBadge">Evidências: 0/0</span>
</div>
<h1 class="audit-main-title">Checklist de Auditoria dos Pilares</h1>
<section class="audit-section intro-data-section">
<div class="instruction-section">
<div class="instruction-copy">
<div class="instruction-head">
<div class="instruction-icon">i</div>
<div class="instruction-title">Envie as fotos dos pilares</div>
</div>
<div class="instruction-steps">
<span>1. Clique em um <strong>PILAR</strong></span>
<span>3. Clique em <strong>ANEXAR</strong> para enviar a foto solicitada</span>
<span>5. Envie para concluir</span>
<span>2. Verifique as <strong>INSTRUÇÕES</strong></span>
<span>4. Confira em <strong>VISUALIZAR</strong></span>
</div>
</div>
</div>
<div class="intro-divider"></div>
<div class="audit-context-card">
<div class="section-title">Dados da Auditoria</div>
<div class="row audit-context-row">
<div class="col-md-2 col-sm-6">
<label class="audit-label" for="loja">Loja</label>
<input type="text" class="form-control audit-input" name="loja" id="loja" />
</div>
<div class="col-md-2 col-sm-6">
<label class="audit-label" for="responsavelLoja">Responsavel da Loja</label>
<input type="text" class="form-control audit-input" name="responsavelLoja" id="responsavelLoja" />
</div>
<div class="col-md-2 col-sm-6">
<label class="audit-label" for="auditor">Analista</label>
<input type="text" class="form-control audit-input" name="auditor" id="auditor" />
</div>
<div class="col-md-2 col-sm-4">
<label class="audit-label" for="dataAuditoria">Data</label>
<input type="date" class="form-control audit-input" name="dataAuditoria" id="dataAuditoria" />
</div>
<div class="col-md-2 col-sm-4">
<label class="audit-label" for="ciclo">Ciclo</label>
<input type="text" class="form-control audit-input" name="ciclo" id="ciclo" placeholder="Ex: 2026-03" />
</div>
<div class="col-md-2 col-sm-4">
<label class="audit-label" for="regional">Regional</label>
<input type="text" class="form-control audit-input" name="regional" id="regional" />
</div>
</div>
</div>
</section>
<div class="nc-box" id="naoConformeBox" style="display:none;">
<div class="nc-title">Pendências para correção</div>
<div class="nc-list" id="naoConformeList"></div>
</div>
<section class="audit-section progress-section">
<h2 class="section-title">Progresso da Auditoria</h2>
<div class="progress-inline">
<div class="progress-track">
<div class="progress-fill" id="globalProgressFill"></div>
</div>
<div class="progress-percent" id="globalProgressPercent">0%</div>
</div>
<div class="progress-meta" id="globalProgressMeta">0 de 0 fotos enviadas</div>
</section>
<div class="row audit-card">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="scoreFinal">0%</div>
<small>Score final</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="pontosObtidos">0</div>
<small>Pontos obtidos</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="pontosPossiveis">0</div>
<small>Pontos possíveis</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="classificacao">-</div>
<small>Classificação</small>
</div>
</div>
</div>
</div>
<h2 class="section-title pilares-title">Pilares Analisados</h2>
<div class="row">
<div class="col-md-12" id="pilaresContainer"></div>
</div>
<div class="audit-send-wrap">
<button type="button" class="btn btn-primary audit-send-btn" id="btnEnviarForm">Finalizar</button>
</div>
<input type="hidden" name="auditoriaPayload" id="auditoriaPayload" />
<input type="hidden" name="temNaoConforme" id="temNaoConforme" value="false" />
<input type="hidden" name="qtdNaoConforme" id="qtdNaoConforme" value="0" />
<input type="hidden" name="listaNaoConforme" id="listaNaoConforme" value="" />
<input type="hidden" name="saidaAnalise" id="saidaAnalise" value="CONFORME" />
<input type="hidden" name="usuarioRetorno" id="usuarioRetorno" value="" />
<input type="hidden" name="destinoLoja" id="destinoLoja" value="" />
<input type="hidden" name="destinoLojaId" id="destinoLojaId" value="" />
</div>
</form>
</div>
<script type="text/javascript" src="./assets/js/checklist.js"></script>
</body>
</html>
@@ -0,0 +1,208 @@
function beforeTaskSave(colleagueId, nextSequenceId, userList) {
var FORCE_EMAIL_DESTINATION = "";
var currentState = parseInt(getValue("WKCurrentState"), 10);
var nextState = parseInt(nextSequenceId, 10);
try {
// Roteamento para loja na entrada da atividade "Validar Planograma" (task5).
// Usa prioridade: destinoLojaId (colleagueId) -> destinoLoja (login).
if (nextState === 5) {
atribuiResponsavelLoja(userList);
}
} catch (eAssign) {
log.error("[checklist][atribuicao] Falha ao atribuir responsavel da loja: " + eAssign);
throw eAssign;
}
// Notificacao por email so quando sai da atividade de analise (task5).
if (currentState !== 5) {
return;
}
try {
var saidaAnalise = (hAPI.getCardValue("saidaAnalise") || "").trim();
var usuarioRetorno = (hAPI.getCardValue("usuarioRetorno") || "").trim();
var destino = String(FORCE_EMAIL_DESTINATION || "").trim();
var usuarioDestino = "";
if (!destino) {
usuarioDestino = usuarioRetorno || String(colleagueId || getValue("WKUser") || "").trim();
destino = resolveEmailByColleagueId(usuarioDestino);
}
log.info("[checklist][notificacao] saidaAnalise=" + saidaAnalise
+ ", usuarioRetorno=" + usuarioRetorno
+ ", colleagueId=" + colleagueId
+ ", usuarioDestino=" + usuarioDestino
+ ", destinoFinal=" + destino
+ ", nextState=" + nextState);
if (!destino) {
log.warn("[checklist][notificacao] Usuario destino nao informado.");
return;
}
if (!isValidEmail(destino)) {
log.warn("[checklist][notificacao] Email de destino invalido: " + destino);
return;
}
if (nextState === 9 || saidaAnalise === "NAO_CONFORME") {
notificaPorTemplate("CHECKLIST_NAO_CONFORME", destino);
return;
}
if (nextState === 11 || saidaAnalise === "CONFORME") {
notificaPorTemplate("CHECKLIST_CONFORME", destino);
}
} catch (e) {
log.error("[checklist][notificacao] Falha ao disparar notificacao: " + e);
}
}
function atribuiResponsavelLoja(userList) {
var destinoLojaId = String(hAPI.getCardValue("destinoLojaId") || "").trim();
var destinoLojaLogin = String(hAPI.getCardValue("destinoLoja") || "").trim();
var colleagueId = "";
if (destinoLojaId && existsActiveUserByColleagueId(destinoLojaId)) {
colleagueId = destinoLojaId;
}
if (!colleagueId && destinoLojaLogin) {
colleagueId = resolveColleagueIdByLogin(destinoLojaLogin);
}
if (!colleagueId) {
throw "Preencha uma loja valida. destinoLojaId/destinoLoja nao encontrado no colleague ativo.";
}
userList.clear();
userList.add(colleagueId);
hAPI.setCardValue("destinoLojaId", colleagueId);
log.info("[checklist][atribuicao] Responsavel definido. destinoLojaId=" + destinoLojaId
+ ", destinoLoja=" + destinoLojaLogin
+ ", colleagueIdFinal=" + colleagueId);
}
function notificaPorTemplate(templateCode, destinatario) {
var NOTIFIER_SENDER_USER = "admin";
var recipients = new java.util.ArrayList();
recipients.add(destinatario);
var parametros = new java.util.HashMap();
parametros.put("numeroSolicitacao", String(getValue("WKNumProces") || ""));
parametros.put("qtdNaoConforme", String(hAPI.getCardValue("qtdNaoConforme") || "0"));
parametros.put("listaNaoConforme", String(hAPI.getCardValue("listaNaoConforme") || ""));
parametros.put("loja", String(hAPI.getCardValue("loja") || ""));
parametros.put("linkSolicitacao", montaLinkSolicitacao());
notifier.notify(
NOTIFIER_SENDER_USER,
templateCode,
parametros,
recipients,
"text/html"
);
log.info("[checklist][notificacao] Template " + templateCode + " enviado para " + destinatario);
}
function montaLinkSolicitacao() {
var server = String(getValue("WKServerURL") || "");
var companyId = String(getValue("WKCompany") || "");
var processId = String(getValue("WKNumProces") || "");
if (!server || !companyId || !processId) {
return "";
}
return server
+ "/portal/p/"
+ companyId
+ "/pageworkflowview?app_ecm_workflowview_detailsProcessInstanceID="
+ processId;
}
function usuarioPossuiEmailValido(colleagueId) {
try {
var c1 = DatasetFactory.createConstraint("colleaguePK.colleagueId", colleagueId, colleagueId, ConstraintType.MUST);
var c2 = DatasetFactory.createConstraint("active", "true", "true", ConstraintType.MUST);
var ds = DatasetFactory.getDataset("colleague", null, [c1, c2], null);
if (!ds || ds.rowsCount < 1) {
log.warn("[checklist][notificacao] Usuario nao encontrado/inativo no colleague: " + colleagueId);
return false;
}
var email = String(ds.getValue(0, "mail") || "").trim();
log.info("[checklist][notificacao] Email encontrado para " + colleagueId + ": " + email);
return email.indexOf("@") > 0;
} catch (e) {
log.error("[checklist][notificacao] Erro ao validar email do usuario " + colleagueId + ": " + e);
return false;
}
}
function resolveEmailByColleagueId(colleagueId) {
try {
if (!colleagueId) {
return "";
}
var ds = findColleagueByIdOrLogin(colleagueId);
if (!ds || ds.rowsCount < 1) {
log.warn("[checklist][notificacao] Usuario nao encontrado/inativo no colleague: " + colleagueId);
return "";
}
var email = String(ds.getValue(0, "mail") || "").trim();
log.info("[checklist][notificacao] Email encontrado para " + colleagueId + ": " + email);
return email;
} catch (e) {
log.error("[checklist][notificacao] Erro ao buscar email do usuario " + colleagueId + ": " + e);
return "";
}
}
function isValidEmail(email) {
var v = String(email || "").trim();
return v.indexOf("@") > 0 && v.indexOf(".") > v.indexOf("@") + 1;
}
function existsActiveUserByColleagueId(colleagueId) {
var ds = null;
try {
var c1 = DatasetFactory.createConstraint("colleaguePK.colleagueId", colleagueId, colleagueId, ConstraintType.MUST);
var c2 = DatasetFactory.createConstraint("active", "true", "true", ConstraintType.MUST);
ds = DatasetFactory.getDataset("colleague", null, [c1, c2], null);
return ds && ds.rowsCount > 0;
} catch (e) {
log.error("[checklist][atribuicao] Erro ao validar colleagueId=" + colleagueId + ": " + e);
return false;
}
}
function resolveColleagueIdByLogin(login) {
try {
var c1 = DatasetFactory.createConstraint("login", login, login, ConstraintType.MUST);
var c2 = DatasetFactory.createConstraint("active", "true", "true", ConstraintType.MUST);
var ds = DatasetFactory.getDataset("colleague", null, [c1, c2], null);
if (ds && ds.rowsCount > 0) {
return String(ds.getValue(0, "colleaguePK.colleagueId") || "").trim();
}
} catch (e) {
log.error("[checklist][atribuicao] Erro ao resolver colleagueId por login=" + login + ": " + e);
}
return "";
}
function findColleagueByIdOrLogin(value) {
var v = String(value || "").trim();
if (!v) return null;
var c2 = DatasetFactory.createConstraint("active", "true", "true", ConstraintType.MUST);
var cId = DatasetFactory.createConstraint("colleaguePK.colleagueId", v, v, ConstraintType.MUST);
var byId = DatasetFactory.getDataset("colleague", null, [cId, c2], null);
if (byId && byId.rowsCount > 0) return byId;
var cLogin = DatasetFactory.createConstraint("login", v, v, ConstraintType.MUST);
var byLogin = DatasetFactory.getDataset("colleague", null, [cLogin, c2], null);
if (byLogin && byLogin.rowsCount > 0) return byLogin;
return null;
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Auditoria_checklist</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
<nature>com.totvs.tds.ecm.designer.nature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,108 @@
eclipse.preferences.version=1
encoding//wcm/layout/helloLayout/src/main/resources/application.info=utf8
encoding//wcm/layout/helloLayout/src/main/resources/helloLayout.properties=utf8
encoding//wcm/layout/helloLayout/src/main/resources/helloLayout_en_US.properties=utf8
encoding//wcm/layout/helloLayout/src/main/resources/helloLayout_es.properties=utf8
encoding//wcm/layout/helloLayout/src/main/resources/helloLayout_pt_BR.properties=utf8
encoding//wcm/layout/helloLayout/src/main/resources/layout.ftl=utf8
encoding//wcm/layout/helloLayout/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/layout/helloLayout/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/layout/helloLayout/src/main/webapp/resources/css/helloLayout.css=utf8
encoding//wcm/layout/helloLayout/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/layout/helloLayout/src/main/webapp/resources/js/helloLayout.js=utf8
encoding//wcm/widget/dashconforme/src/main/resources/application.info=utf8
encoding//wcm/widget/dashconforme/src/main/resources/dashconforme.properties=utf8
encoding//wcm/widget/dashconforme/src/main/resources/dashconforme_en_US.properties=utf8
encoding//wcm/widget/dashconforme/src/main/resources/dashconforme_es.properties=utf8
encoding//wcm/widget/dashconforme/src/main/resources/dashconforme_pt_BR.properties=utf8
encoding//wcm/widget/dashconforme/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/dashconforme/src/main/resources/view.ftl=utf8
encoding//wcm/widget/dashconforme/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/dashconforme/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/dashconforme/src/main/webapp/resources/css/dashconforme.css=utf8
encoding//wcm/widget/dashconforme/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/dashconforme/src/main/webapp/resources/js/dashconforme.js=utf8
encoding//wcm/widget/dashdata/src/main/resources/application.info=utf8
encoding//wcm/widget/dashdata/src/main/resources/dashdata.properties=utf8
encoding//wcm/widget/dashdata/src/main/resources/dashdata_en_US.properties=utf8
encoding//wcm/widget/dashdata/src/main/resources/dashdata_es.properties=utf8
encoding//wcm/widget/dashdata/src/main/resources/dashdata_pt_BR.properties=utf8
encoding//wcm/widget/dashdata/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/dashdata/src/main/resources/view.ftl=utf8
encoding//wcm/widget/dashdata/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/dashdata/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/dashdata/src/main/webapp/resources/css/dashdata.css=utf8
encoding//wcm/widget/dashdata/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/dashdata/src/main/webapp/resources/js/dashdata.js=utf8
encoding//wcm/widget/dashgrafico/src/main/resources/application.info=utf8
encoding//wcm/widget/dashgrafico/src/main/resources/dashgrafico.properties=utf8
encoding//wcm/widget/dashgrafico/src/main/resources/dashgrafico_en_US.properties=utf8
encoding//wcm/widget/dashgrafico/src/main/resources/dashgrafico_es.properties=utf8
encoding//wcm/widget/dashgrafico/src/main/resources/dashgrafico_pt_BR.properties=utf8
encoding//wcm/widget/dashgrafico/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/dashgrafico/src/main/resources/view.ftl=utf8
encoding//wcm/widget/dashgrafico/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/dashgrafico/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/dashgrafico/src/main/webapp/resources/css/dashgrafico.css=utf8
encoding//wcm/widget/dashgrafico/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/dashgrafico/src/main/webapp/resources/js/dashgrafico.js=utf8
encoding//wcm/widget/dashkpi/src/main/resources/application.info=utf8
encoding//wcm/widget/dashkpi/src/main/resources/dashkpi.properties=utf8
encoding//wcm/widget/dashkpi/src/main/resources/dashkpi_en_US.properties=utf8
encoding//wcm/widget/dashkpi/src/main/resources/dashkpi_es.properties=utf8
encoding//wcm/widget/dashkpi/src/main/resources/dashkpi_pt_BR.properties=utf8
encoding//wcm/widget/dashkpi/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/dashkpi/src/main/resources/view.ftl=utf8
encoding//wcm/widget/dashkpi/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/dashkpi/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/dashkpi/src/main/webapp/resources/css/dashkpi.css=utf8
encoding//wcm/widget/dashkpi/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/dashkpi/src/main/webapp/resources/js/dashkpi.js=utf8
encoding//wcm/widget/dashregional/src/main/resources/application.info=utf8
encoding//wcm/widget/dashregional/src/main/resources/dashregional.properties=utf8
encoding//wcm/widget/dashregional/src/main/resources/dashregional_en_US.properties=utf8
encoding//wcm/widget/dashregional/src/main/resources/dashregional_es.properties=utf8
encoding//wcm/widget/dashregional/src/main/resources/dashregional_pt_BR.properties=utf8
encoding//wcm/widget/dashregional/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/dashregional/src/main/resources/view.ftl=utf8
encoding//wcm/widget/dashregional/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/dashregional/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/dashregional/src/main/webapp/resources/css/dashregional.css=utf8
encoding//wcm/widget/dashregional/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/dashregional/src/main/webapp/resources/js/dashregional.js=utf8
encoding//wcm/widget/dashtabela/src/main/resources/application.info=utf8
encoding//wcm/widget/dashtabela/src/main/resources/dashtabela.properties=utf8
encoding//wcm/widget/dashtabela/src/main/resources/dashtabela_en_US.properties=utf8
encoding//wcm/widget/dashtabela/src/main/resources/dashtabela_es.properties=utf8
encoding//wcm/widget/dashtabela/src/main/resources/dashtabela_pt_BR.properties=utf8
encoding//wcm/widget/dashtabela/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/dashtabela/src/main/resources/view.ftl=utf8
encoding//wcm/widget/dashtabela/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/dashtabela/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/dashtabela/src/main/webapp/resources/css/dashtabela.css=utf8
encoding//wcm/widget/dashtabela/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/dashtabela/src/main/webapp/resources/js/dashtabela.js=utf8
encoding//wcm/widget/filtrosDash/src/main/resources/application.info=utf8
encoding//wcm/widget/filtrosDash/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/filtrosDash/src/main/resources/filtrosDash.properties=utf8
encoding//wcm/widget/filtrosDash/src/main/resources/filtrosDash_en_US.properties=utf8
encoding//wcm/widget/filtrosDash/src/main/resources/filtrosDash_es.properties=utf8
encoding//wcm/widget/filtrosDash/src/main/resources/filtrosDash_pt_BR.properties=utf8
encoding//wcm/widget/filtrosDash/src/main/resources/view.ftl=utf8
encoding//wcm/widget/filtrosDash/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/filtrosDash/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/filtrosDash/src/main/webapp/resources/css/filtrosDash.css=utf8
encoding//wcm/widget/filtrosDash/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/filtrosDash/src/main/webapp/resources/js/filtrosDash.js=utf8
encoding//wcm/widget/helloWidget/src/main/resources/application.info=utf8
encoding//wcm/widget/helloWidget/src/main/resources/edit.ftl=utf8
encoding//wcm/widget/helloWidget/src/main/resources/helloWidget.properties=utf8
encoding//wcm/widget/helloWidget/src/main/resources/helloWidget_en_US.properties=utf8
encoding//wcm/widget/helloWidget/src/main/resources/helloWidget_es.properties=utf8
encoding//wcm/widget/helloWidget/src/main/resources/helloWidget_pt_BR.properties=utf8
encoding//wcm/widget/helloWidget/src/main/resources/view.ftl=utf8
encoding//wcm/widget/helloWidget/src/main/webapp/WEB-INF/jboss-web.xml=utf8
encoding//wcm/widget/helloWidget/src/main/webapp/WEB-INF/web.xml=utf8
encoding//wcm/widget/helloWidget/src/main/webapp/resources/css/helloWidget.css=utf8
encoding//wcm/widget/helloWidget/src/main/webapp/resources/images/icon.png=utf8
encoding//wcm/widget/helloWidget/src/main/webapp/resources/js/helloWidget.js=utf8
+4
View File
@@ -0,0 +1,4 @@
{
"version": "1.0.0",
"configurations": []
}
@@ -0,0 +1,151 @@
function createDataset(fields, constraints, sortFields) {
var ds = DatasetBuilder.newDataset();
ds.addColumn("documentid");
ds.addColumn("dataAuditoria");
ds.addColumn("dataLimite");
ds.addColumn("ciclo");
ds.addColumn("auditor");
ds.addColumn("usuarioRetorno");
ds.addColumn("responsavelLoja");
ds.addColumn("loja");
ds.addColumn("regional");
ds.addColumn("saidaAnalise");
ds.addColumn("status");
ds.addColumn("qtdNaoConforme");
ds.addColumn("listaNaoConforme");
try {
var filtros = parseConstraints(constraints);
var formDs = DatasetFactory.getDataset("checklist", null, null, null);
if (!formDs || formDs.rowsCount < 1) {
return ds;
}
var linhas = [];
for (var i = 0; i < formDs.rowsCount; i++) {
var status = val(formDs, i, "saidaAnalise").toUpperCase();
if (status !== "CONFORME" && status !== "NAO_CONFORME") {
continue;
}
var dataAuditoria = val(formDs, i, "dataAuditoria");
var dataLimite = val(formDs, i, "dataLimite");
var ciclo = val(formDs, i, "ciclo");
var regional = val(formDs, i, "regional");
var loja = val(formDs, i, "loja");
if (filtros.loja && norm(loja).indexOf(norm(filtros.loja)) === -1) {
continue;
}
if (filtros.ciclo && norm(ciclo).indexOf(norm(filtros.ciclo)) === -1) {
continue;
}
if (filtros.regional && norm(regional).indexOf(norm(filtros.regional)) === -1) {
continue;
}
if (filtros.dataInicio || filtros.dataFim) {
var faixaAuditoria = dataNaFaixa(dataAuditoria, filtros.dataInicio, filtros.dataFim);
var faixaLimite = dataNaFaixa(dataLimite, filtros.dataInicio, filtros.dataFim);
if (!faixaAuditoria && !faixaLimite) {
continue;
}
}
linhas.push({
documentid: val(formDs, i, "documentid"),
dataAuditoria: dataAuditoria,
dataLimite: dataLimite,
ciclo: ciclo,
auditor: val(formDs, i, "auditor"),
usuarioRetorno: val(formDs, i, "usuarioRetorno"),
responsavelLoja: val(formDs, i, "responsavelLoja"),
loja: loja,
regional: regional,
saidaAnalise: status,
status: status,
qtdNaoConforme: val(formDs, i, "qtdNaoConforme"),
listaNaoConforme: val(formDs, i, "listaNaoConforme")
});
}
linhas.sort(function (a, b) {
if (a.dataAuditoria < b.dataAuditoria) return 1;
if (a.dataAuditoria > b.dataAuditoria) return -1;
if (a.documentid < b.documentid) return 1;
if (a.documentid > b.documentid) return -1;
return 0;
});
for (var j = 0; j < linhas.length; j++) {
var r = linhas[j];
ds.addRow([
r.documentid,
r.dataAuditoria,
r.dataLimite,
r.ciclo,
r.auditor,
r.usuarioRetorno,
r.responsavelLoja,
r.loja,
r.regional,
r.saidaAnalise,
r.status,
r.qtdNaoConforme,
r.listaNaoConforme
]);
}
} catch (e) {
ds = DatasetBuilder.newDataset();
ds.addColumn("ERRO");
ds.addRow([String(e)]);
}
return ds;
}
function parseConstraints(constraints) {
var out = {
dataInicio: "",
dataFim: "",
loja: "",
ciclo: "",
regional: ""
};
if (!constraints) return out;
for (var i = 0; i < constraints.length; i++) {
var c = constraints[i];
if (!c) continue;
var name = String(c.fieldName || c._field || "");
var value = String(c.initialValue || c._initialValue || "").trim();
if (!name || !value) continue;
if (name === "dataInicio") out.dataInicio = value;
if (name === "dataFim") out.dataFim = value;
if (name === "loja") out.loja = value;
if (name === "ciclo") out.ciclo = value;
if (name === "regional") out.regional = value;
}
return out;
}
function val(ds, row, col) {
var v = ds.getValue(row, col);
return String(v == null ? "" : v).trim();
}
function norm(v) {
return String(v || "").toLowerCase().trim();
}
function dataNaFaixa(data, inicio, fim) {
var d = String(data || "").trim();
if (!d) return false;
if (inicio && d < inicio) return false;
if (fim && d > fim) return false;
return true;
}
@@ -0,0 +1,209 @@
function createDataset(fields, constraints, sortFields) {
var dataset = DatasetBuilder.newDataset();
dataset.addColumn("PDV");
dataset.addColumn("LOJA");
dataset.addColumn("RESPONSAVEL_LOJA");
dataset.addColumn("REGIONAL");
dataset.addColumn("UF");
dataset.addColumn("CIDADE");
dataset.addColumn("STATUS");
dataset.addColumn("CANAL");
dataset.addColumn("ID");
dataset.addColumn("LOGIN_LOJA");
dataset.addColumn("COLLEAGUE_ID");
dataset.addColumn("USER_CREATE");
try {
var clientService = fluigAPI.getAuthorizeClientService();
var data = {
companyId: String(getValue("WKCompany") || "1"),
serviceCode: "GINSENG API", // ajuste para o codigo do servico cadastrado no Fluig
endpoint: "/base_pdvs",
method: "get",
timeoutService: "60000",
params: {}
};
var vo = clientService.invoke(JSON.stringify(data));
var statusHttp = vo ? String(vo.getHttpStatusResult() || "") : "";
var retorno = vo ? String(vo.getResult() || "") : "";
if (!retorno) {
dataset.addRow(["", "Sem resposta da API (HTTP " + statusHttp + ")", "", "", "", "", "", "", "", "", "", ""]);
return dataset;
}
var objData = JSON.parse(retorno);
var success = !!objData.success;
var itens = objData.data || [];
if (!success || !itens.length) {
var msg = trim(objData.message) || trim(objData.error) || "Sem lojas retornadas";
dataset.addRow(["", msg + " (HTTP " + statusHttp + ")", "", "", "", "", "", "", "", "", "", ""]);
return dataset;
}
var filtros = parseConstraints(constraints);
var colleagueCache = {};
for (var i = 0; i < itens.length; i++) {
var item = itens[i] || {};
var pdv = trim(item["PDV"]);
var loja = trim(item["PDV DESC"]);
var responsavel = trim(item["GESTAO"]) || trim(item["GESTAO"]);
var regional = trim(item["SUPERVISOR"]) || trim(item["SUPERVISOR"]);
var uf = trim(item["UF"]);
var cidade = trim(item["CIDADE"]);
var status = trim(item["STATUS"]);
var canal = trim(item["CANAL"]);
var id = trim(item["id"]);
var loginLoja = pdv ? (pdv + ".ginseng") : "";
var colleagueId = resolveColleagueIdByLogin(loginLoja);
var userCreate = getUserCreateStatus(loginLoja, colleagueCache);
if (filtros.onlyAtivo && status.toUpperCase() !== "ATIVO") {
continue;
}
if (filtros.pdv && pdv !== filtros.pdv) {
continue;
}
if (filtros.loja && normalize(loja).indexOf(normalize(filtros.loja)) === -1) {
continue;
}
if (filtros.termoLivre) {
var blob = normalize([
pdv,
loja,
responsavel,
regional,
uf,
cidade
].join(" "));
if (blob.indexOf(normalize(filtros.termoLivre)) === -1) {
continue;
}
}
dataset.addRow([
pdv,
loja,
responsavel,
regional,
uf,
cidade,
status,
canal,
id,
loginLoja,
colleagueId,
userCreate
]);
}
if (dataset.rowsCount === 0) {
dataset.addRow(["", "Sem lojas apos filtros (HTTP " + statusHttp + ")", "", "", "", "", "", "", "", "", "", ""]);
}
} catch (e) {
dataset = DatasetBuilder.newDataset();
dataset.addColumn("ERRO");
dataset.addRow([String(e)]);
}
return dataset;
}
function parseConstraints(constraints) {
var out = {
pdv: "",
loja: "",
onlyAtivo: true,
termoLivre: ""
};
if (!constraints) return out;
for (var i = 0; i < constraints.length; i++) {
var c = constraints[i];
if (!c || !c.fieldName) continue;
var name = String(c.fieldName);
var value = trim(c.initialValue);
if (name === "PDV" && value) out.pdv = value;
if (name === "LOJA" && value) out.loja = value;
if (name === "onlyAtivo" && value.toLowerCase() === "false") out.onlyAtivo = false;
// Zoom costuma enviar constraints variadas de busca; capturamos termo livre.
if (
value &&
name !== "onlyAtivo" &&
name !== "metadata#id" &&
name !== "metadata#active" &&
name !== "sqlLimit"
) {
var cleaned = cleanSearchValue(value);
if (cleaned && (!out.termoLivre || cleaned.length > out.termoLivre.length)) {
out.termoLivre = cleaned;
}
}
}
return out;
}
function trim(v) {
return String(v == null ? "" : v).trim();
}
function normalize(v) {
return trim(v).toLowerCase();
}
function cleanSearchValue(v) {
var s = trim(v);
// remove curingas comuns de busca do Zoom/like
s = s.replace(/[%*_]/g, "");
return trim(s);
}
function getUserCreateStatus(loginLoja, cache) {
var login = trim(loginLoja);
if (!login) return "NAO";
if (cache.hasOwnProperty(login)) return cache[login];
try {
var cLogin = DatasetFactory.createConstraint("login", login, login, ConstraintType.MUST);
var cActive = DatasetFactory.createConstraint("active", "true", "true", ConstraintType.MUST);
var ds = DatasetFactory.getDataset("colleague", null, [cLogin, cActive], null);
var exists = !!(ds && ds.rowsCount > 0);
// fallback para ambientes onde o identificador esta em colleagueId
if (!exists) {
var cId = DatasetFactory.createConstraint("colleaguePK.colleagueId", login, login, ConstraintType.MUST);
ds = DatasetFactory.getDataset("colleague", null, [cId, cActive], null);
exists = !!(ds && ds.rowsCount > 0);
}
cache[login] = exists ? "OK" : "NAO";
return cache[login];
} catch (e) {
cache[login] = "NAO";
return "NAO";
}
}
function resolveColleagueIdByLogin(loginLoja) {
var login = trim(loginLoja);
if (!login) return "";
try {
var cLogin = DatasetFactory.createConstraint("login", login, login, ConstraintType.MUST);
var cActive = DatasetFactory.createConstraint("active", "true", "true", ConstraintType.MUST);
var ds = DatasetFactory.getDataset("colleague", null, [cLogin, cActive], null);
if (ds && ds.rowsCount > 0) {
return trim(ds.getValue(0, "colleaguePK.colleagueId"));
}
} catch (e) {}
return "";
}
@@ -0,0 +1,640 @@
body { background: #ffffff; }
.audit-shell {
background: #f4f8fd;
border: 1px solid #dbe4ee;
border-radius: 14px;
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.06);
padding: 10px 10px 14px 10px;
margin: 14px auto 0 auto;
max-width: 1140px;
}
.audit-section {
border: 1px solid #e2e8f0;
border-radius: 14px;
background: #ffffff;
padding: 12px;
margin-bottom: 10px;
box-shadow: 0 2px 6px rgba(15, 23, 42, 0.05);
}
h1.audit-main-title {
margin: 2px 0 10px 0;
font-size: 18px !important;
line-height: 1.2;
font-weight: 700 !important;
color: #ffffff !important;
background: #0b556b;
border-radius: 10px;
padding: 8px 12px;
}
.section-title {
margin: 0 0 10px 0;
font-size: 20px;
font-weight: 700;
color: #1e293b;
}
.audit-status {
display: flex;
gap: 8px;
align-items: center;
margin: 2px 0 10px 0;
flex-wrap: wrap;
}
.progress-section {
padding: 5px 10px 5px 10px;
margin-top: 2px;
}
.progress-section .section-title {
font-size: 16px;
margin-bottom: 6px;
}
.audit-context-card .section-title {
font-size: 17px;
}
.progress-inline {
display: grid;
grid-template-columns: 1fr auto;
gap: 16px;
align-items: center;
}
.progress-track {
width: 100%;
height: 5px;
border-radius: 999px;
background: #e6ebf2;
overflow: hidden;
}
.progress-fill {
width: 0%;
height: 100%;
background: linear-gradient(90deg, #3ea9c8 0%, #3ea9c8 100%);
transition: width .18s ease-in-out;
}
.progress-percent {
font-size: 14px;
line-height: 1;
color: #0f172a;
font-weight: 700;
min-width: 42px;
text-align: right;
}
.progress-meta {
margin-top: 3px;
font-size: 9px;
color: #52637b;
font-weight: 600;
}
.instruction-section {
display: block;
background: #ffffff;
}
.intro-data-section {
padding: 0;
overflow: hidden;
background: linear-gradient(90deg, #f4f8fd 0%, #edf3fa 100%);
}
.intro-data-section .instruction-section {
padding: 12px;
border-radius: 14px 14px 0 0;
background: #ffffff;
}
.intro-divider {
height: 1px;
background: #dfe7f0;
}
.instruction-head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 6px;
}
.instruction-icon {
width: 34px;
height: 34px;
border-radius: 999px;
background: #3ea9c8;
color: #fff;
font-size: 22px;
font-weight: 700;
line-height: 34px;
text-align: center;
}
.instruction-title {
font-size: 16px;
color: #1f2f46;
font-weight: 700;
margin-bottom: 0;
}
.instruction-steps {
display: grid;
grid-template-columns: repeat(3, minmax(160px, 1fr));
gap: 6px 16px;
font-size: 10px;
color: #334155;
line-height: 1.3;
}
.instruction-steps span {
display: inline-block;
border: 1px solid #b9d4ea;
background: #f4f9ff;
color: #0f3f66;
border-radius: 999px;
padding: 4px 9px;
width: fit-content;
}
.status-pill {
background: #eef5fb;
border: 1px solid #d9e8f6;
color: #0b556b;
border-radius: 999px;
padding: 6px 10px;
font-size: 12px;
font-weight: 600;
}
.audit-context-card {
background: #ffffff;
padding: 12px;
border-radius: 0 0 14px 14px;
}
.audit-context-row > div {
margin-bottom: 8px;
}
.audit-label {
font-size: 12px;
letter-spacing: 0.3px;
color: #16486a;
margin-bottom: 4px;
font-weight: 700;
}
.label-icon { opacity: 0.8; margin-right: 4px; }
.audit-input {
height: 32px;
font-size: 12px;
border-color: #c9d7e7;
border-radius: 7px;
padding: 5px 9px;
}
.audit-input:focus {
border-color: #74a5d3;
box-shadow: 0 0 0 2px rgba(34, 93, 143, 0.12);
}
.audit-card { margin-top: 12px; }
.audit-score { font-size: 22px; font-weight: 700; }
.audit-score small { font-size: 12px; font-weight: 400; color: #7f8c8d; display: block; }
#pilaresContainer {
display: grid;
grid-template-columns: repeat(4, minmax(220px, 1fr));
gap: 14px;
background: #f4f8fd;
border: 0;
border-radius: 10px;
padding: 8px 12px 10px 12px;
}
h2.section-title.pilares-title {
margin-top: 4px;
margin-bottom: 10px;
font-size: 17px !important;
}
.audit-send-wrap {
margin-top: 10px;
text-align: right;
padding: 10px 12px 2px 12px;
border-top: 1px solid #dbe5f0;
}
.audit-send-btn {
min-width: 120px;
height: 38px;
border-radius: 8px;
font-size: 14px;
font-weight: 700;
background: #0b556b;
border-color: #0b556b;
}
.audit-send-btn:hover,
.audit-send-btn:focus {
background: #0a4b5f;
border-color: #0a4b5f;
}
.pilar-panel {
border: 1px solid #d6e1ee;
border-radius: 12px;
background: #ffffff;
padding: 8px;
min-height: 108px;
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.08);
transition: box-shadow .18s ease, transform .18s ease;
}
.pilar-panel:hover {
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.12);
transform: translateY(-1px);
}
.pilar-panel.is-open {
grid-column: 1 / -1;
box-shadow: 0 12px 24px rgba(15, 23, 42, 0.14);
}
.pilar-summary-card {
grid-column: span 3;
border: 1px solid #d6e1ee;
border-radius: 12px;
background: #ffffff;
padding: 10px 12px;
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.08);
}
.summary-title {
font-size: 13px;
font-weight: 700;
color: #000000;
margin-bottom: 8px;
}
.summary-item {
font-size: 12px;
line-height: 1.45;
color: #000000;
margin-bottom: 6px;
}
.summary-item:last-child {
margin-bottom: 0;
}
.summary-name {
color: #000000;
font-weight: 700;
}
.pilar-head {
background: #ffffff;
border: 0;
padding: 6px;
display: grid;
gap: 6px;
cursor: pointer;
border-radius: 10px;
transition: background .15s ease, border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.pilar-head:hover {
background: #f8fbff;
box-shadow: none;
transform: none;
}
.pilar-head.is-open {
background: transparent;
box-shadow: none;
}
.pilar-title-row {
display: flex;
align-items: center;
gap: 10px;
}
.pilar-state-dot {
width: 18px;
height: 18px;
border-radius: 999px;
background: #94a3b8;
border: 1px solid rgba(148, 163, 184, 0.25);
display: inline-flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 11px;
font-weight: 700;
line-height: 1;
}
.pilar-state-dot.state-pending { background: #dc2626; border-color: rgba(220, 38, 38, 0.4); }
.pilar-state-dot.state-progress { background: #d18a2f; border-color: rgba(209, 138, 47, 0.35); }
.pilar-state-dot.state-done { background: #2f9d78; border-color: rgba(47, 157, 120, 0.35); }
.pilar-title {
font-size: 13px;
font-weight: 700;
color: #000000;
letter-spacing: 0.2px;
}
.pilar-photo-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
color: #243549;
}
.pilar-photo-count {
font-size: 10px;
font-weight: 700;
color: #000000;
}
.pilar-missing {
font-size: 10px;
color: #000000;
font-weight: 600;
}
.pilar-mini-track {
height: 5px;
border-radius: 999px;
background: #e4eaf1;
overflow: hidden;
}
.pilar-mini-fill {
display: block;
width: 0%;
height: 100%;
border-radius: inherit;
background: #5d8fbe;
transition: width .18s ease;
}
.pilar-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.pilar-count {
font-size: 10px;
color: #000000;
font-weight: 600;
}
.pilar-open-cta {
font-size: 10px;
color: #000000;
font-weight: 700;
}
.pilar-body { padding: 8px 0 0 0; }
.audit-row {
border: 0;
border-radius: 0;
padding: 0;
margin: 0 0 12px 0;
background: transparent;
}
.audit-row-main {
display: flex;
flex-direction: column;
gap: 8px;
}
.audit-row-top {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
align-items: start;
}
.audit-info {
min-width: 0;
}
.indicador-cell { font-weight: 700; color: #000000; font-size: 15px; line-height: 1.25; }
.indicador-help {
margin-top: 5px;
font-size: 12px;
color: #5c6f82;
line-height: 1.35;
background: transparent;
border: 0;
border-radius: 8px;
padding: 7px 9px;
}
.indicador-help ul {
margin: 0;
padding-left: 18px;
}
.indicador-help li {
margin: 2px 0;
}
.indicador-help .indicador-group {
list-style: none;
margin-top: 6px;
margin-left: -18px;
font-weight: 700;
color: #36597b;
}
.indicador-help-grid {
display: grid;
grid-template-columns: repeat(2, minmax(220px, 1fr));
gap: 10px 20px;
padding-left: 0;
}
.indicador-help-col .indicador-group {
margin: 0 0 4px 0;
font-size: 12px;
}
.indicador-help-col ul {
margin: 0;
padding-left: 18px;
}
.meta-grid {
display: grid;
grid-template-columns: repeat(5, minmax(100px, 1fr));
gap: 10px;
}
.meta-box {
border: 1px solid #d7e2ef;
border-radius: 10px;
background: #fff;
padding: 8px 10px;
min-height: 62px;
}
.meta-label { font-size: 11px; color: #64748b; text-transform: uppercase; }
.meta-value { font-size: 20px; color: #1f2937; margin-top: 4px; font-weight: 600; }
.meta-value .form-control {
font-size: 14px;
height: 34px;
padding: 4px 10px;
}
.anexo-wrap, .resultado-wrap, .just-wrap { margin-top: 0; }
.penalidade-badge {
font-size: 10px !important;
font-weight: 700;
padding: 2px 8px !important;
border-radius: 999px;
letter-spacing: 0.2px;
text-transform: uppercase;
}
.penalidade-badge.label-danger { background: #f7dada; color: #9f2d2d; }
.penalidade-badge.label-warning { background: #fff0c7; color: #8a5a00; }
.penalidade-badge.label-info { background: #dceefe; color: #1f5f9b; }
.penalidade-badge.label-success { background: #dff5e4; color: #1f7a3d; }
.penalidade-badge.label-default { background: #eceff3; color: #5f6b7a; }
.anexos-cell {
background: #eef3fb;
border: 1px solid #cfdaea;
border-radius: 14px;
padding: 10px;
}
.anexo-auto-hint { font-size: 12px; color: #5c7390; margin-top: 7px; }
.anexo-progress {
margin-top: 10px;
font-size: 13px;
color: #26486a;
font-weight: 600;
}
.anexo-progress-count {
display: inline-block;
min-width: 52px;
text-align: center;
border-radius: 999px;
background: #d8e6f8;
color: #204c7a;
padding: 4px 12px;
margin-right: 6px;
font-weight: 700;
}
.anexo-multi-list {
display: grid;
grid-template-columns: repeat(4, minmax(240px, 1fr));
gap: 10px;
}
.audit-row[data-max-fotos="1"] .anexo-multi-list {
grid-template-columns: 1fr;
}
.audit-row[data-max-fotos="2"] .anexo-multi-list {
grid-template-columns: repeat(2, minmax(220px, 1fr));
}
.audit-row[data-max-fotos="3"] .anexo-multi-list {
grid-template-columns: repeat(3, minmax(220px, 1fr));
}
.anexo-slot {
background: #ffffff;
border: 1px solid #bdcde1;
border-radius: 10px;
padding: 7px;
position: relative;
}
.audit-row[data-max-fotos="1"] .anexo-slot,
.audit-row[data-max-fotos="2"] .anexo-slot {
max-width: 100%;
}
.anexo-slot.slot-filled {
border-color: #8fc1a3;
box-shadow: inset 0 0 0 1px rgba(79, 161, 109, 0.2);
}
.anexo-slot.extra-slot { display: none; }
.anexo-multi-list.expanded .anexo-slot.extra-slot { display: block; }
.anexo-slot-label {
font-size: 12px;
color: #375478;
margin-bottom: 4px;
font-weight: 700;
text-transform: uppercase;
}
.audit-row[data-max-fotos]:not([data-max-fotos="1"]) .anexo-slot::after {
content: "•••";
position: absolute;
right: 12px;
top: 8px;
color: #b0c5df;
font-size: 15px;
letter-spacing: 2px;
}
.anexo-toggle-wrap {
margin-top: 12px;
text-align: right;
}
.anexo-toggle {
font-size: 16px;
font-weight: 700;
color: #2f4f74 !important;
border-color: #b7c8de;
background: #eef3fb;
border-radius: 12px;
padding: 8px 22px;
}
.anexos-cell .fluigFormAttachmentComponent {
width: 100%;
}
.anexos-cell .fluigFormAttachmentComponent input {
height: 36px;
border: 1px dashed #b6c9e1;
border-radius: 8px;
background: #eef3fb;
font-size: 14px;
color: #5e7898;
padding: 4px 10px;
}
.anexos-cell .fluigFormAttachmentComponent input::placeholder {
color: #8ea4bf;
opacity: 1;
}
.anexos-cell .fluigFormAttachmentComponent .btn {
height: 42px !important;
min-width: 52px;
border-radius: 8px;
}
.anexos-cell .fluigFormAttachmentComponent .btn-success {
background: #2f81c6 !important;
border-color: #2a75b3 !important;
color: #fff !important;
}
.anexos-cell .fluigFormAttachmentComponent .btn-default {
background: #eef2f7 !important;
border-color: #c7d1de !important;
color: #41597a !important;
}
.anexos-cell .fluigFormAttachmentComponent .btn-danger {
background: #e1574c !important;
border-color: #d04c42 !important;
color: #fff !important;
}
.just-wrap input {
border-radius: 10px;
}
.audit-row-target { background: #fff8e6 !important; }
.nc-box {
border: 1px solid #f5d0d0;
background: #fff6f6;
border-radius: 10px;
padding: 10px 12px;
margin-top: 10px;
}
.nc-title {
font-weight: 700;
color: #9f2d2d;
margin-bottom: 6px;
}
.nc-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.nc-item {
background: #fff;
border: 1px solid #f1b7b7;
color: #7f1d1d;
border-radius: 999px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
}
.anexo-view { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.anexo-thumb-inline {
width: 34px;
height: 34px;
object-fit: cover;
border: 1px solid #ddd;
border-radius: 4px;
background: #fff;
}
.anexo-file {
font-size: 11px;
max-width: 170px;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 4px 6px;
background: #fafafa;
}
.anexo-text {
background: #fff;
font-size: 12px;
}
@media (max-width: 900px) {
.section-title { font-size: 22px; }
.progress-percent { font-size: 14px; min-width: 38px; }
.progress-meta { font-size: 14px; }
.instruction-title { font-size: 16px; }
.instruction-steps { grid-template-columns: 1fr; font-size: 14px; }
.audit-label { font-size: 13px; }
.audit-input { height: 40px; font-size: 15px; }
.audit-row-top { grid-template-columns: 1fr; }
.meta-grid { grid-template-columns: repeat(2, minmax(100px, 1fr)); }
.anexo-multi-list { grid-template-columns: 1fr; }
#pilaresContainer { grid-template-columns: repeat(2, minmax(180px, 1fr)); }
.pilar-panel.is-open { grid-column: auto; }
.pilar-summary-card { grid-column: span 2; }
.audit-send-wrap { padding: 8px 0 0 0; border-top: 0; }
.audit-send-btn { width: 100%; }
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,423 @@
/**
* Plugin JQuery para trabalhar com anexos nos formulários dentro do processo
*
* @author Bruno Gasparetto
* @see https://github.com/brunogasparetto/fluig-form-attachment
*/
/**
* Configurações
*
* @typedef AttachmentSettings
* @property {boolean} showActionButton Exibe o botão de upload/delete. True por padrão.
* @property {boolean} filename Nome que será salvo como descrição do Anexo.
* @property {boolean|string} prefixName Adiciona prefixo ao anexo. False por padrão, True para prefixo aleatório, String para prefixo fixo.
* @property {string} accept Tipos de arquivos aceitos. Segue a regra do accept do input tipo file.
*/
;(function ($) {
"use strict";
const pluginName = "fluigFormAttachment";
const deleteFileClassName = "BtnDeleteFile";
const uploadFileClassname = "BtnUploadFile";
const viewerFileClassname = "BtnViewerFile";
const compressedExtensions = [
'.7z', '.zip', '.rar', '.gz', '.tar', '.tbz2', '.tgz', '.bz2', '.lz', '.lz4','.txz',
'.xz', '.z', '.zst', '.zstd', '.war', '.ear', '.jar','.apk', '.arj', '.ace', '.cab',
];
const isString = item => typeof item === "string";
/**
* Procura o índice do anexo de acordo com sua descrição
*
* @param {string} filename
* @returns {number} -1 se não encontrar
*/
const attachmentFindIndex = (filename) => parent.ECM.attachmentTable.getData().findIndex(attachment => attachment.description === filename);
/**
* Configuração padrão
*
* @type {AttachmentSettings}
*/
const defaults = {
showActionButton: true,
filename: "Anexo",
prefixName: false,
accept: "*",
};
class Plugin {
/**
* @type {AttachmentSettings}
*/
#settings;
/**
* Elemento do arquivo. Pode ser um input ou span (no modo leitura).
*
* @type {JQuery<HTMLElement>}
*/
#input;
/**
* @type {JQuery<HTMLElement>}
*/
#container;
/**
* @type {string}
*/
#attachmentFilename;
/**
* @param {HTMLElement} element
* @param {AttachmentSettings} options
*/
constructor(element, options) {
// Garantir um ID para o Input
if (!element.id && element.nodeName.toLowerCase() === "input") {
element.id = FLUIGC.utilities.randomUUID();
}
this.#settings = $.extend({}, defaults, options);
this.#input = $(element);
this.#attachmentFilename = this.#input.val() || this.#input.text().trim();
this.#input
.prop("readonly", true)
.on("change", () => {
this.#attachmentFilename = this.#input.val();
this.#changeButtonsState();
})
.wrap(`<div class="${pluginName}Component"></div>`)
.after(`<div class="${pluginName}Component_buttons">${this.#getButtonsTemplate()}</div>`);
this.#container = this.#input.closest(`.${pluginName}Component`);
this.#container
.on("click", `.${pluginName}${deleteFileClassName}`, () => this.#confirmDeleteAttachment())
.on("click", `.${pluginName}${uploadFileClassname}`, () => this.#uploadAttachment())
.on("click", `.${pluginName}${viewerFileClassname}`, () => this.#viewAttachment())
;
}
/**
* Indica que o campo está válido
*
* Caso o campo possua algum valor é obrigatório que o anexo
* esteja na tabela de anexos.
*
* @returns {boolean}
*/
isValid() {
return this.#attachmentFilename.length
? this.hasAttachment()
: true
;
}
/**
* Indica se o anexo está na tabela de anexos
*
* @returns {boolean}
*/
hasAttachment() {
const filename = this.#attachmentFilename || this.#input.val() || this.#input.text().trim();
return filename.length > 0 && attachmentFindIndex(filename) !== -1;
}
/**
* Remove o anexo
*
* Método útil para excluir anexos em tabela Pai x Filho.
*/
deleteAttachment() {
const attachmentIndex = parent.ECM.attachmentTable.getData().findIndex(
attachment => attachment.description === this.#attachmentFilename
);
setTimeout(() => this.#input.val("").trigger("change"), 500);
if (attachmentIndex === -1) {
return;
}
parent.WKFViewAttachment.removeAttach([attachmentIndex]);
}
showActionButton() {
this.#settings.showActionButton = true;
this.#input.trigger("change");
}
hideActionButton() {
this.#settings.showActionButton = false;
this.#input.trigger("change");
}
filename(fileName, prefixName) {
if (fileName === undefined) {
return this.#input.data("filename") || this.#settings.filename;
}
this.#settings.filename = fileName;
this.#input.data("filename", fileName);
if (prefixName !== undefined) {
this.prefixName(prefixName);
}
}
prefixName(prefixName) {
if (prefixName === undefined) {
return this.#settings.prefixName;
}
this.#settings.prefixName = prefixName;
}
#getButtonsTemplate() {
const hasFileSelected = this.#attachmentFilename.length !== 0;
const canShowActionButton = this.#canDisplayActionButton();
return `<button type="button" class="${pluginName}BtnAction ${pluginName}${deleteFileClassName} btn btn-danger btn-sm ${(canShowActionButton && hasFileSelected) ? '' : 'hide'}" title="Remover Anexo"><i class="flaticon flaticon-trash icon-sm"></i></button>`
+ `<button type="button" class="${pluginName}BtnAction ${pluginName}${uploadFileClassname} btn btn-success btn-sm ${(canShowActionButton && !hasFileSelected) ? '' : 'hide'}" title="Enviar Anexo"><i class="flaticon flaticon-upload icon-sm"></i></button>`
+ `<button type="button" class="${pluginName}${viewerFileClassname} btn btn-info btn-sm ${hasFileSelected ? '' : 'hide'}" title="Visualizar Anexo"><i class="flaticon flaticon-view icon-sm"></i></button>`
;
}
#canDisplayActionButton() {
const element = this.#input.get(0);
return this.#settings.showActionButton
&& parent.ECM.workflowView.userPermissions.indexOf("P") >= 0
&& location.href.includes('ManagerMode')
&& !location.href.includes('token')
&& element.nodeName.toLowerCase() === "input"
&& !element.disabled
;
}
#changeButtonsState() {
const hasFileSelected = this.#attachmentFilename.length !== 0;
if (this.#canDisplayActionButton()) {
if (hasFileSelected) {
this.#container.find(`.${pluginName}${uploadFileClassname}`).addClass("hide");
this.#container.find(`.${pluginName}${deleteFileClassName}`).removeClass("hide");
} else {
this.#container.find(`.${pluginName}${deleteFileClassName}`).addClass("hide");
this.#container.find(`.${pluginName}${uploadFileClassname}`).removeClass("hide");
}
} else {
this.#container.find(`.${pluginName}BtnAction`).addClass("hide");
}
if (hasFileSelected) {
this.#container.find(`.${pluginName}${viewerFileClassname}`).removeClass("hide");
} else {
this.#container.find(`.${pluginName}${viewerFileClassname}`).addClass("hide");
}
}
#confirmDeleteAttachment() {
if (!this.#canDisplayActionButton()) {
return;
}
FLUIGC.message.confirm({
message: `Deseja remover o anexo <b>${this.#attachmentFilename}</b>?`,
title: 'Confirmação',
labelYes: 'Sim, quero remover',
labelNo: 'Não, quero cancelar',
}, result => {
if (!result) {
return;
}
this.deleteAttachment();
});
}
#uploadAttachment() {
if (!this.#canDisplayActionButton()) {
return;
}
let filename = this.#input.data("filename") || this.#settings.filename;
if (this.#settings.prefixName === true) {
filename = FLUIGC.utilities.randomUUID().substring(0, 9) + filename;
} else if (this.#settings.prefixName !== false && isString(this.#settings.prefixName)) {
filename = `${this.#settings.prefixName}-${filename}`;
}
// Evitar conflito de descrição do anexo
if (attachmentFindIndex(filename) !== -1) {
FLUIGC.toast({
title: "Atenção",
message: "Já existe um anexo com essa descrição",
type: "warning",
})
return;
}
parent.$("#ecm-navigation-inputFile-clone")
.attr({
"data-on-camera": "true",
"data-file-name-camera": filename,
"data-inputid": this.#input.attr("id"),
"data-filename": filename,
"multiple": false,
"accept": this.#input.data("accept") || this.#settings.accept,
})
.trigger("click")
;
}
#viewAttachment() {
const attachmentIndex = parent.ECM.attachmentTable.getData().findIndex(
attachment => attachment.description === this.#attachmentFilename
);
if (attachmentIndex === -1) {
FLUIGC.toast({
title: "Atenção",
message: "Anexo não encontrado",
type: "warning"
});
return;
}
const attachment = parent.ECM.attachmentTable.getRow(attachmentIndex);
const physicalFileName = String(
attachment.physicalFileName || attachment.fileName || attachment.name || ""
).toLowerCase();
const isCompressedFile = compressedExtensions.some(extension => physicalFileName.endsWith(extension));
if (attachment.documentId && !isCompressedFile) {
parent.WKFViewAttachment.openAttachmentView(parent.WCMAPI.userCode, attachment.documentId, attachment.version);
} else {
parent.WKFViewAttachment.downloadAttach([attachmentIndex]);
}
}
}
/**
* Instancia o Plugin ou executa algum método do plugin
*
* @param {AttachmentSettings|string} options
* @returns {undefined|boolean|void}
*/
$.fn[pluginName] = function (options) {
if (!parent.WKFViewAttachment || !parent.ECM || !parent.ECM.attachmentTable) {
console.error(`Plugin ${pluginName} executado fora de um processo.`)
return this;
}
// Executa o Método
if (isString(options)) {
const methodName = options;
const methodArgs = Array.prototype.slice.call(arguments, 1);
let returnedValue = undefined;
this.each(function () {
let pluginData = $.data(this, pluginName);
if (!pluginData) {
pluginData = new Plugin(this, {});
$.data(this, pluginName, pluginData);
}
if (!pluginData[methodName]) {
return;
}
returnedValue = pluginData[methodName](...methodArgs);
if (returnedValue !== undefined) {
return false;
}
});
return returnedValue !== undefined
? returnedValue
: this
;
}
return this.each(function () {
if (!$.data(this, pluginName)) {
$.data(this, pluginName, new Plugin(this, options));
}
});
};
if (!parent.WKFViewAttachment || !parent.ECM || !parent.ECM.attachmentTable) {
return;
}
const loading = FLUIGC.loading(window, {
title: "Aguarde",
textMessage: "Enviando arquivo",
})
$(() => {
// Oculta aba anexos
$("#tab-attachments", parent.document).hide();
parent.$("#ecm_navigation_fileupload")
.on(`fileuploadadd.${pluginName}`, function(e, data) {
// Impede abrir o Loading caso tenha erro no arquivo
const file = data.files[0];
if (parent.ECM.maxUploadSize > 0 && file.size >= (parent.ECM.maxUploadSize * 1024 * 1024)) {
return;
}
if (parent.ECM.newAttachmentsDocs.length
&& parent.ECM.newAttachmentsDocs.findIndex(attachment => attachment.name === file.name) !== -1
) {
return;
}
loading.show();
})
.on(`fileuploadfail.${pluginName}`, () => loading.hide())
.on(`fileuploaddone.${pluginName}`, function() {
// Atualiza o campo do arquivo caso o upload tenha ocorrido
loading.hide();
const btnUpload = parent.document.getElementById("ecm-navigation-inputFile-clone");
const filename = btnUpload.getAttribute("data-filename");
if (attachmentFindIndex(filename) === -1) {
return;
}
$(`#${btnUpload.getAttribute("data-inputid")}`).val(filename).trigger("change");
});
parent.$(document).on(`fileuploadstop.${pluginName}`, () => loading.hide());
});
$("head").append(`<style>
.${pluginName}Component { display: flex; align-items: center; flex-wrap: nowrap; }
.${pluginName}Component input { border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; }
.${pluginName}Component_buttons { display: flex; align-items: center; justify-content: flex-end; }
.${pluginName}Component_buttons .btn { outline: none !important; outline-offset: unset !important; border-radius: 0 !important; height: 32px; }
</style>`);
}(jQuery));
File diff suppressed because one or more lines are too long
@@ -0,0 +1,166 @@
<html>
<head>
<link type="text/css" rel="stylesheet" href="/style-guide/css/fluig-style-guide.min.css" />
<script type="text/javascript" src="/portal/resources/js/jquery/jquery.js"></script>
<script type="text/javascript" src="/portal/resources/js/jquery/jquery-ui.min.js"></script>
<script type="text/javascript" src="/portal/resources/js/mustache/mustache-min.js"></script>
<script type="text/javascript" src="/style-guide/js/fluig-style-guide.min.js" charset="utf-8"></script>
<script type="text/javascript" src="./fluigFormAttachment.js"></script>
<link type="text/css" rel="stylesheet" href="./assets/css/checklist.css" />
</head>
<body>
<div class="fluig-style-guide">
<form name="form" role="form">
<div class="container-fluid audit-shell">
<div class="audit-status">
<span class="status-pill" id="profileBadge">Perfil: Loja</span>
<span class="status-pill" id="progressBadge">Evidências: 0/0</span>
</div>
<h1 class="audit-main-title">Checklist de Auditoria dos Pilares</h1>
<section class="audit-section intro-data-section">
<div class="instruction-section">
<div class="instruction-copy">
<div class="instruction-head">
<div class="instruction-icon">i</div>
<div class="instruction-title">Envie as fotos dos pilares</div>
</div>
<div class="instruction-steps">
<span>1. Clique em um <strong>PILAR</strong></span>
<span>3. Clique em <strong>ANEXAR</strong> para enviar a foto solicitada</span>
<span>5. Envie para concluir</span>
<span>2. Verifique as <strong>INSTRUÇÕES</strong></span>
<span>4. Confira em <strong>VISUALIZAR</strong></span>
</div>
</div>
</div>
<div class="intro-divider"></div>
<div class="audit-context-card">
<div class="section-title">Dados da Auditoria</div>
<div class="row audit-context-row">
<div class="col-md-6 col-sm-6">
<label class="audit-label" for="loja">Loja</label>
<input
type="zoom"
class="form-control audit-input"
name="loja"
id="loja"
data-zoom="{
'displayKey':'LOJA',
'datasetId':'ds_lojas_api',
'maximumSelectionLength':'1',
'placeholder':'Selecione a loja',
'fields':[
{'field':'LOJA','label':'Loja', 'standard':'true','search':'true'},
{'field':'RESPONSAVEL_LOJA','label':'Responsavel'},
{'field':'REGIONAL','label':'Regional'},
{'field':'COLLEAGUE_ID','label':'Colleague ID'},
{'field':'LOGIN_LOJA','label':'Login Loja'}
]
}"
/>
</div>
<div class="col-md-3 col-sm-6">
<label class="audit-label" for="responsavelLoja">Responsavel da Loja</label>
<input type="text" class="form-control audit-input" name="responsavelLoja" id="responsavelLoja" />
</div>
<div class="col-md-2 col-sm-6">
<label class="audit-label" for="auditor">Analista</label>
<input type="text" class="form-control audit-input" name="auditor" id="auditor" />
</div>
<div class="col-md-2 col-sm-4">
<label class="audit-label" for="dataAuditoria">Data</label>
<input type="date" class="form-control audit-input" name="dataAuditoria" id="dataAuditoria" readonly/>
</div>
<div class="col-md-2 col-sm-4">
<label class="audit-label" for="ciclo">Ciclo</label>
<input type="text" class="form-control audit-input" name="ciclo" id="ciclo" placeholder="Ex: 2026-03" required />
</div>
<div class="col-md-3 col-sm-4">
<label class="audit-label" for="regional">Regional</label>
<input type="text" class="form-control audit-input" name="regional" id="regional" readonly />
</div>
<div class="col-md-2 col-sm-4">
<label class="audit-label" for="dataLimite">Data limite</label>
<input type="date" class="form-control audit-input" name="dataLimite" id="dataLimite" required />
</div>
</div>
</div>
</section>
<div class="nc-box" id="naoConformeBox" style="display:none;">
<div class="nc-title">Pendências para correção</div>
<div class="nc-list" id="naoConformeList"></div>
</div>
<section class="audit-section progress-section">
<h2 class="section-title">Progresso da Auditoria</h2>
<div class="progress-inline">
<div class="progress-track">
<div class="progress-fill" id="globalProgressFill"></div>
</div>
<div class="progress-percent" id="globalProgressPercent">0%</div>
</div>
<div class="progress-meta" id="globalProgressMeta">0 de 0 fotos enviadas</div>
</section>
<div class="row audit-card">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="scoreFinal">0%</div>
<small>Score final</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="pontosObtidos">0</div>
<small>Pontos obtidos</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="pontosPossiveis">0</div>
<small>Pontos possíveis</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-body">
<div class="audit-score" id="classificacao">-</div>
<small>Classificação</small>
</div>
</div>
</div>
</div>
<h2 class="section-title pilares-title">Pilares Analisados</h2>
<div class="row">
<div class="col-md-12" id="pilaresContainer"></div>
</div>
<div class="audit-send-wrap">
<button type="button" class="btn btn-primary audit-send-btn" id="btnEnviarForm">Finalizar</button>
</div>
<input type="hidden" name="auditoriaPayload" id="auditoriaPayload" />
<input type="hidden" name="temNaoConforme" id="temNaoConforme" value="false" />
<input type="hidden" name="qtdNaoConforme" id="qtdNaoConforme" value="0" />
<input type="hidden" name="listaNaoConforme" id="listaNaoConforme" value="" />
<input type="hidden" name="saidaAnalise" id="saidaAnalise" value="CONFORME" />
<input type="hidden" name="usuarioRetorno" id="usuarioRetorno" value="" />
<input type="hidden" name="destinoLoja" id="destinoLoja" value="" />
<input type="hidden" name="destinoLojaId" id="destinoLojaId" value="" />
</div>
</form>
</div>
<script type="text/javascript" src="./assets/js/checklist.js"></script>
</body>
</html>
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Checklist Aprovado</title>
<style>
body { font-family: Arial, sans-serif; background: #f4f6f8; margin: 0; padding: 24px; color: #1f2937; }
.card { max-width: 720px; margin: 0 auto; background: #ffffff; border: 1px solid #e5e7eb; border-radius: 10px; overflow: hidden; }
.head { background: #1f7a3d; color: #fff; padding: 14px 18px; font-size: 18px; font-weight: 700; }
.body { padding: 18px; font-size: 14px; line-height: 1.5; }
.meta { margin: 10px 0; padding: 10px 12px; background: #f5fbf7; border: 1px solid #d5ecd9; border-radius: 8px; }
.label { font-weight: 700; }
.footer { margin-top: 14px; font-size: 12px; color: #6b7280; }
a.btn { display: inline-block; margin-top: 12px; background: #1369a8; color: #fff !important; text-decoration: none; padding: 9px 14px; border-radius: 6px; }
</style>
</head>
<body>
<div class="card">
<div class="head">Checklist aprovado</div>
<div class="body">
<p>O checklist <b>${numeroSolicitacao}</b> foi aprovado como conforme.</p>
<div class="meta">
<div><span class="label">Loja:</span> ${loja}</div>
<div><span class="label">Status:</span> Conforme</div>
</div>
<a class="btn" href="${linkSolicitacao}">Abrir solicitacao</a>
<div class="footer">Mensagem automatica do processo de checklist.</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Checklist com Pendencias</title>
<style>
body { font-family: Arial, sans-serif; background: #f4f6f8; margin: 0; padding: 24px; color: #1f2937; }
.card { max-width: 720px; margin: 0 auto; background: #ffffff; border: 1px solid #e5e7eb; border-radius: 10px; overflow: hidden; }
.head { background: #9f2d2d; color: #fff; padding: 14px 18px; font-size: 18px; font-weight: 700; }
.body { padding: 18px; font-size: 14px; line-height: 1.5; }
.meta { margin: 10px 0; padding: 10px 12px; background: #fff8f8; border: 1px solid #f3d3d3; border-radius: 8px; }
.label { font-weight: 700; }
.footer { margin-top: 14px; font-size: 12px; color: #6b7280; }
a.btn { display: inline-block; margin-top: 12px; background: #1369a8; color: #fff !important; text-decoration: none; padding: 9px 14px; border-radius: 6px; }
</style>
</head>
<body>
<div class="card">
<div class="head">Checklist com pendencias de correcao</div>
<div class="body">
<p>O checklist <b>${numeroSolicitacao}</b> foi analisado e retornou para correcao.</p>
<div class="meta">
<div><span class="label">Loja:</span> ${loja}</div>
<div><span class="label">Qtd. nao conforme:</span> ${qtdNaoConforme}</div>
<div><span class="label">Itens:</span> ${listaNaoConforme}</div>
</div>
<p>Revise os itens e anexe as evidencias corrigidas.</p>
<a class="btn" href="${linkSolicitacao}">Abrir solicitacao</a>
<div class="footer">Mensagem automatica do processo de checklist.</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,23 @@
application.type=layout
application.code=helloLayout
application.title=helloLayout
application.description=helloLayout
application.fluig.version=null
application.category=SYSTEM
application.renderer=freemarker
developer.code=andrey.cunha
developer.name=andrey.cunha
developer.url=http://www.fluig.com
layout.defaultSlot=SlotA
layout.file=layout.ftl
locale.file.base.name=helloLayout
slot.SlotGlobalAlert=alertpopover
slot.SlotInstantSearch=suggestsearch
slot.SlotLogin=sociallogin
slot.SlotMenu=menu
slot.SlotUsePolicy=usepolicy
application.icon=icon.png
application.resource.css.1=/portal/resources/css/wcm_responsive_layout.css
application.responsiveLayout=true
application.newBuilder=true
hash=4a16315e9e66fa7d797b3f6b1fb365b69f9a4ce2
@@ -0,0 +1 @@
application.title=applicationTitle
@@ -0,0 +1 @@
application.title=applicationTitle
@@ -0,0 +1 @@
application.title=applicationTitle
@@ -0,0 +1 @@
application.title=applicationTitle
@@ -0,0 +1,41 @@
<#import "/wcm.ftl" as wcm />
<#-- Variaveis globais para os layouts -->
<#import "/layout-globals.ftl" as globals />
<!-- layout ECM-DEFAULT-DASHBOARD -->
<#if pageRender.isPreviewMode() = true>
<@wcm.previewPageAlert />
<@wcm.deviceTogglePreview />
</#if>
<div class="wcm-wrapper-content ${wcmLayoutEditClass!""} ${pageAuthTypeClass!""}">
<#if pageRender.isEditMode() != true>
<@wcm.header />
<@wcm.menu />
</#if>
<div class="wcm-all-content">
<div id="wcm-content" class="clearfix wcm-background">
<#if pageRender.isEditMode() = true>
<@wcm.editHeader />
<@wcm.widgetsList />
</#if>
<div id="${divMasterId!""}" class="clearfix">
<!-- Conteúdo aqui -->
<#if fluigThemeCode != "responsive_theme">
<@wcm.footer layoutuserlabel="wcm.layoutdefaultecmdashboard.user" />
</#if>
</div>
</div>
</div>
</div>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/helloLayout</context-root>
<disable-cross-context>false</disable-cross-context>
</jboss-web>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
@@ -0,0 +1 @@
/*insira aqui seu código CSS*/
Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

@@ -0,0 +1,2 @@
// Insira aqui seu codigo JavaScript
@@ -0,0 +1,19 @@
application.type=widget
application.code=dashconforme
application.title=dashconforme
application.description=dashconforme
application.fluig.version=null
application.category=SYSTEM
application.renderer=freemarker
developer.code=andrey.cunha
developer.name=andrey.cunha
developer.url=http://www.fluig.com
application.uiwidget=true
application.mobileapp=false
application.version=${build.version}-${build.revision}
view.file=view.ftl
edit.file=edit.ftl
locale.file.base.name=dashconforme
application.resource.js.1=/resources/js/dashconforme.js
application.resource.css.2=/resources/css/dashconforme.css
hash=4a16315e9e66fa7d797b3f6b1fb365b69f9a4ce2
@@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>
@@ -0,0 +1,11 @@
<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>
</div>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/dashconforme</context-root>
<disable-cross-context>false</disable-cross-context>
</jboss-web>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
@@ -0,0 +1,3 @@
div[id^="dashConforme_"] canvas {
max-height: 420px;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

@@ -0,0 +1,149 @@
var DashConforme = SuperWidget.extend({
chart:null,
init:function(){
var self=this;
this.renderAmostra();
window.addEventListener("dashboardData",function(e){
self.render(e.detail || []);
});
},
render:function(dados){
var mapa = {};
dados.forEach(function(item){
var lista = String(item.listaNaoConforme || "").trim();
if(!lista){
return;
}
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]};
});
pares.sort(function(a,b){
return b.valor - a.valor;
});
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];
}
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:labels,
datasets:[{
label:"Ocorrências",
data:valores,
backgroundColor:"#f39c12"
}]
},
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] : "";
}
}
}
},
scales:{
x:{
ticks:{
autoSkip:false,
maxRotation:35,
minRotation:0,
callback:function(value,index){
var txt = labels[index] || "";
return txt.length > 38 ? txt.substring(0,38) + "..." : txt;
}
}
},
y:{
beginAtZero:true,
precision:0
}
}
}
});
},
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}
}
}
});
}
});
@@ -0,0 +1,19 @@
application.type=widget
application.code=dashdata
application.title=dashdata
application.description=dashdata
application.fluig.version=null
application.category=SYSTEM
application.renderer=freemarker
developer.code=andrey.cunha
developer.name=andrey.cunha
developer.url=http://www.fluig.com
application.uiwidget=true
application.mobileapp=false
application.version=${build.version}-${build.revision}
view.file=view.ftl
edit.file=edit.ftl
locale.file.base.name=dashdata
application.resource.js.1=/resources/js/dashdata.js
application.resource.css.2=/resources/css/dashdata.css
hash=4a16315e9e66fa7d797b3f6b1fb365b69f9a4ce2
@@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>
@@ -0,0 +1,4 @@
<div id="dashdata_${instanceId}"
class="super-widget wcm-widget-class"
data-params="DashData.instance()">
</div>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/dashdata</context-root>
<disable-cross-context>false</disable-cross-context>
</jboss-web>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
@@ -0,0 +1 @@
/* Coloque aqui seu codigo CSS */
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

@@ -0,0 +1,123 @@
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);
});
},
carregar:function(filtros){
console.log("Consultando dataset...", filtros);
var self=this;
$.ajax({
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){
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){
var da = normalizaData(a.dataAuditoria);
var db = normalizaData(b.dataAuditoria);
if(da < db) return 1;
if(da > db) return -1;
return 0;
});
console.log("Registros filtrados:", dados);
window.dispatchEvent(
new CustomEvent("dashboardData",{detail:dados})
);
}
});
}
});
function normalizaTexto(valor){
return String(valor || "").trim().toUpperCase();
}
function normalizaData(valor){
var v = String(valor || "").trim();
return v;
}
function dataNaFaixa(data, inicio, fim){
if(!data){
return false;
}
if(inicio && data < inicio){
return false;
}
if(fim && data > fim){
return false;
}
return true;
}
@@ -0,0 +1,19 @@
application.type=widget
application.code=dashgrafico
application.title=dashgrafico
application.description=dashgrafico
application.fluig.version=null
application.category=SYSTEM
application.renderer=freemarker
developer.code=G-ALES1NT-TEC09
developer.name=andrey.cunha
developer.url=http://www.fluig.com
application.uiwidget=true
application.mobileapp=false
application.version=${build.version}-${build.revision}
view.file=view.ftl
edit.file=edit.ftl
locale.file.base.name=dashgrafico
application.resource.js.1=/resources/js/dashgrafico.js
application.resource.css.2=/resources/css/dashgrafico.css
hash=4a16315e9e66fa7d797b3f6b1fb365b69f9a4ce2
@@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>
@@ -0,0 +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>
</div>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/dashgrafico</context-root>
<disable-cross-context>false</disable-cross-context>
</jboss-web>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
@@ -0,0 +1 @@
/* Coloque aqui seu codigo CSS */
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

@@ -0,0 +1,91 @@
var DashGrafico = SuperWidget.extend({
chart:null,
init:function(){
var self=this;
this.renderAmostra();
window.addEventListener("dashboardData",function(e){
self.render(e.detail);
});
},
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();
}
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}
}
}
});
}
});
@@ -0,0 +1,19 @@
application.type=widget
application.code=dashkpi
application.title=dashkpi
application.description=dashkpi
application.fluig.version=null
application.category=SYSTEM
application.renderer=freemarker
developer.code=andrey.cunha
developer.name=andrey.cunha
developer.url=http://www.fluig.com
application.uiwidget=true
application.mobileapp=false
application.version=${build.version}-${build.revision}
view.file=view.ftl
edit.file=edit.ftl
locale.file.base.name=dashkpi
application.resource.js.1=/resources/js/dashkpi.js
application.resource.css.2=/resources/css/dashkpi.css
hash=4a16315e9e66fa7d797b3f6b1fb365b69f9a4ce2
@@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>
@@ -0,0 +1,44 @@
<div id="dashKPI_${instanceId}"
class="super-widget wcm-widget-class fluig-style-guide"
data-params="DashKPI.instance()">
<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>
</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>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/dashkpi</context-root>
<disable-cross-context>false</disable-cross-context>
</jboss-web>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
@@ -0,0 +1 @@
/* Coloque aqui seu codigo CSS */
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

@@ -0,0 +1,41 @@
var DashKPI = SuperWidget.extend({
init:function(){
var self=this;
window.addEventListener("dashboardData",function(e){
self.calcular(e.detail);
});
},
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();
}

Some files were not shown because too many files have changed in this diff Show More