This commit is contained in:
Cunha 2026-03-10 18:34:47 -03:00
parent 0754466359
commit b32415212f
165 changed files with 14613 additions and 2352 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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|

View File

@ -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

View File

@ -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.

View File

@ -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
Auditoria/.project Normal file
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>

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.

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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;
}

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>

View File

@ -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

View File

@ -0,0 +1,4 @@
{
"version": "1.0.0",
"configurations": []
}

View File

@ -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;
}

View File

@ -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 "";
}

Binary file not shown.

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -0,0 +1 @@
application.title=applicationTitle

View File

@ -0,0 +1 @@
application.title=applicationTitle

View File

@ -0,0 +1 @@
application.title=applicationTitle

View File

@ -0,0 +1 @@
application.title=applicationTitle

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1 @@
/*insira aqui seu código CSS*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

View File

@ -0,0 +1,2 @@
// Insira aqui seu codigo JavaScript

View File

@ -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

View File

@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,3 @@
div[id^="dashConforme_"] canvas {
max-height: 420px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -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}
}
}
});
}
});

View File

@ -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

View File

@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>

View File

@ -0,0 +1,4 @@
<div id="dashdata_${instanceId}"
class="super-widget wcm-widget-class"
data-params="DashData.instance()">
</div>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1 @@
/* Coloque aqui seu codigo CSS */

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1 @@
/* Coloque aqui seu codigo CSS */

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -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}
}
}
});
}
});

View File

@ -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

View File

@ -0,0 +1,4 @@
<div id="MyWidget_${instanceId}" class="super-widget wcm-widget-class fluig-style-guide" data-params="MyWidget.instance()">
</div>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1 @@
/* Coloque aqui seu codigo CSS */

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -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