This commit is contained in:
Andrey Cunha
2026-05-22 02:11:03 -03:00
parent 9ec8d6e70b
commit 328de3abd2
53 changed files with 28306 additions and 1062 deletions
Binary file not shown.
+160
View File
@@ -0,0 +1,160 @@
<!-- ✅ IMPORTANTE: o link do estilo sempre fora das divs -->
<link rel="stylesheet" type="text/css" href="/style-guide/css/fluig-style-guide.min.css">
<div id="cardsProcessos" class="fluig-style-guide">
<h3 style="color:#04506b; font-weight:600; margin-bottom:15px;">
Iniciar Solicitações
</h3>
<!-- 🔍 Campo de Pesquisa -->
<div class="form-group" style="max-width: 300px; margin-bottom: 20px;">
<div class="input-group">
<span class="input-group-addon">
<i class="fluigicon fluigicon-search"></i>
</span>
<input id="filtroProcessos" type="text" class="form-control" placeholder="Pesquisar processo...">
</div>
</div>
<!-- 🔹 Lista de Cards -->
<div class="cards-container">
<div class="card-process" data-proc="Solicitação de compras - Integração teste">
<i class="flaticon flaticon-product"></i>
<span>Solicitação de Compras</span>
</div>
<div class="card-process" data-proc="FlowEssentials_AberturaDeChamado">
<i class="flaticon flaticon-message-broker"></i>
<span>Tecnologia</span>
</div>
<div class="card-process" data-proc="Transferência Ginseng">
<i class="flaticon flaticon-transfer"></i>
<span>Transferência de Mercadorias</span>
</div>
<div class="card-process" data-proc="Recrutamento e Seleção">
<i class="flaticon flaticon-file-person"></i>
<span>Solicitação de Vaga</span>
</div>
<div class="card-process" data-proc="desligamentoColaborador">
<i class="flaticon flaticon-blocked"></i>
<span>Desligamento de Colaborador</span>
</div>
<div class="card-process" data-proc="checklist">
<i class="flaticon flaticon-check-square"></i>
<span>Auditoria de Lojas</span>
</div>
<div class="card-process" data-proc="Abertura de chamado Manutenção">
<i class="flaticon flaticon-build"></i>
<span>Obras e Manutenção</span>
</div>
</div>
</div>
<script>
$(document).ready(function() {
// 🔍 Filtro de pesquisa
$("#filtroProcessos").on("keyup", function() {
const termo = $(this).val().toLowerCase().trim();
$(".card-process").each(function() {
const nome = $(this).find("span").text().toLowerCase();
$(this).toggle(nome.includes(termo));
});
});
// 🔹 Clique em card → abre o processo
$(".card-process").on("click", function() {
const processId = $(this).data("proc");
const nome = $(this).find("span").text();
FLUIGC.toast({
title: "Abrindo formulário: ",
message: nome,
type: "info"
});
const url = `/portal/p/1/pageworkflowview?processID=${processId}`;
window.location.href = url;
});
});
</script>
<style>
/* 🔹 Garante que os ícones usem a fonte certa */
.fluigicon:before {
font-family: Fluigicon !important;
}
/* 🔹 Layout geral */
.cards-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: flex-start;
}
/* 🔹 Card menor */
.card-process {
background-color: #f8f9fa;
border: 1px solid #04506b33;
border-radius: 12px;
padding: 15px 10px;
text-align: center;
width: 125px; /* 🔹 ligeiramente menor */
height: 115px; /* 🔹 reduzido para equilíbrio */
cursor: pointer;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.06);
transition: all 0.25s ease;
color: #04506b;
font-family: "Segoe UI", Arial, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 3px; /* 🔹 pequeno respiro entre eles */
}
.card-process:hover {
background-color: #04506b;
color: white;
transform: scale(1.03); /* 🔹 efeito mais suave */
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
z-index: 2; /* 🔹 evita sobreposição visual */
}
/* 🔹 Ícones menores */
.card-process i {
font-size: 28px;
margin-bottom: 8px;
}
.card-process span {
font-weight: 600;
font-size: 12px;
display: block;
line-height: 1.2em;
text-align: center;
}
/* 🔹 Campo de busca */
#filtroProcessos {
border-radius: 4px;
}
.input-group-addon {
background-color: #04506b;
color: white;
border: none;
}
.icon-process{
width:32px;
height:32px;
margin-bottom:8px;
object-fit:contain;
}
</style>
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Bruno Gasparetto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,212 @@
# Fluig Form Attachment
Plugin JQuery para auxiliar no tratamento de anexos em formulários
de processo.
A gestão dos anexos de processo no Fluig não é uma tarefa fácil. Além da aba anexos não
permitir ordenação, o Fluig não permite, de maneira simples, vincular os anexos a campos
do formulário do processo, assim devemos confiar que os usuários vão colocar os anexos
corretamente na aba anexos e depois gastar tempo identificando cada anexo, pois usuários
dificilmente respeitarão alguma regra de nomenclatura.
Para permitir esse vínculo entre os campos do formulário e os anexos, o
[Sérgio Machado](https://www.linkedin.com/in/sergio-machado-analista-fluig/)
criou o [ComponenteAnexos](https://github.com/sergiomachadosilva/fluig-utils/tree/main/projetos/ComponenteAnexos),
que é uma biblioteca JS/CSS/HTML, permitindo um maior controle dos
anexos. Esse projeto do Sérgio serviu como base para a construção desse Plugin JQuery.
A intenção desse plugin é simplificar ainda mais o tratamento dos anexos em formulários
que estão abertos em um Processo.
## Atenção
Esse plugin não funciona na versão mobile dentro do aplicativo My Fluig.
Testado no Fluig 1.8.1 e 1.8.2. Quando lançar o Fluig 2.0 teremos que
rever o funcionamento do plugin.
Esse plugin está usando somente a "descrição" do anexo como vínculo entre
formulário e anexo. Ainda está em testes se isso é o suficiente ou se
o ideal seria ter o nome físico do arquivo salvo em algum lugar, assim
como o ComponenteAnexos do Sérgio Machado.
No Fluig 1.8.1 identifiquei um bug quando insere um anexo por vez e então remove
algum anexo. Nessa situação a tabela de anexos é esvaziada. Porém esse bug ocorre
mesmo quando usando a Aba de Anexos, sendo um bug do próprio Fluig e não do Plugin.
Esse bug ocorre somente na primeira atividade do processo, quando ainda não há ID
da Solicitação.
## Instalação
Basta adicionar o script `fluigFormAttachment.js` ou `fluigformAttachment.min.js` ao
seu formulário.
## Modo de usar
Ao carregar o plugin no seu formulário ele ocultará a Aba Anexos do Fluig, evitando que
insiram documentos não solicitados e, mais importante, removam anexos que já estão
vinculados a um campo do formulário.
### Básico
Basta ter um campo do tipo texto e instanciar o plugin para esse campo.
```html
<div class="form-group">
<label for="cnh">Teste 001</label>
<input type="text" class="form-control" readonly
name="cnh" id="cnh" data-filename="CNH" data-accept="image/*,.pdf"
>
</div>
```
É recomendável deixar o campo como `readonly`. O plugin fará isso automaticamente quando
instanciado no elemento, mas pode ser que algum usuário consiga editar o formulário fora
do processo e nesse caso o plugin não funcionará.
O atributo `data-filename` indica a "descrição" do anexo, que é o valor que aparece na Aba
Anexos. Esse nome será utilizado para vincular o campo do formulário ao anexo.
O atributo `data-accept` indica o tipo de arquivo permitido. Funciona exatamente como
o atributo `accept` do campo input file ([documentação](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept)).
Então no JavaScript basta executar:
```javascript
$("#cnh").fluigFormAttachment();
```
### Parâmetros
Ao instanciar o plugin para os elementos é possível passar um objeto com as seguintes
propriedades de configuração:
| Parâmetro | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| **showActionButton** | boolean | `true` | Indica se deve exibir os botões de Ação (Upload ou Delete) |
| **filename** | string | `"Anexo"` | Nome/Descrição do Anexo. Caso exista o atributo `data-filename` no campo do formulário ele terá preferência. **Cuidado**: Não pode ter mais de um anexo com o mesmo Nome/Descrição. |
| **prefixName** | boolean\|string | `false` | Adiciona prefixo à descrição do anexo. Caso `true` criará um prefixo aleatório usando parte de um UUID. No caso de `string` a usará como prefixo fixo, adicionando `-` como separador. |
| **accept** | string | `"*"` | Funciona igual ao atributo accept do input file ([documentação](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept)). Caso exista o atributo `data-accept` no campo do formulário ele terá preferência. |
Se ao instanciar o Plugin o campo estiver **Desabilitado** ou o usuário não possuir permissão
de complemento no processo, o botão de Upload/Delete não será exibido, mesmo se o parâmetro
`showActionButton` estiver como `true`. Caso deseje exibir/ocultar o botão de ação dinamicamente
utilize os métodos `showActionButton` e `hideActionButton`.
A intenção da configuração `prefixName` é auxiliar nos casos de Tabela Pai Filho.
Lembre-se de tratar as situações nas quais o processo estiver somente em modo de
visualização e nos casos que os campos não devem permitir upload/delete dos
anexos.
Exemplos:
```javascript
$("#cnh").fluigFormAttachment({
showActionButton: false,
});
// Exibe os botões de visualização em todos os filhos já existentes da Pai Filho
// Pulamos a primeira TR por ser a base para gerar as demais filhas
$("#tabelaPaiFilho tbody tr:not(:first-child) .anexo")
.fluigFormAttachment({ showActionButton: false })
;
// Tabela Pai Filho - adiciona com prefixo automático para cada arquivo
$("#adicionar").on("click", function () {
const index = wdkAddChild("tabelaPaiFilho");
$(`#cnh___${index}`).fluigFormAttachment({
prefixName: true,
});
});
// Tabela Pai Filho com vários campos de anexo
$("#adicionar").on("click", function () {
const index = wdkAddChild("tabelaPaiFilho");
const tr = $(`#cnh___${index}`).closest("tr");
// A cada upload será criado um prefixo aleatório pra cada anexo
$(".anexos", tr).fluigFormAttachment({
prefixName: true,
});
});
// Tabela Pai Filho com vários campos de anexo e prefixo único pra linha
$("#adicionar").on("click", function () {
const index = wdkAddChild("tabelaPaiFilho");
const tr = $(`#cnh___${index}`).closest("tr");
// A cada upload usará o mesmo prefixo para todos os anexos da linha
$(".anexos", tr).fluigFormAttachment({
prefixName: FLUIGC.utilities.randomUUID().substring(0, 8),
});
});
```
### Métodos
É possível executar alguns métodos do Plugin para manipular os anexos. Para
executar um método basta chamar o plugin no campo indicando passando o nome
do método ao invés do objeto de parâmetros.
Os seguintes métodos estão disponíveis:
| Método | Executa em | Parâmetros | Retorno | Descrição |
| --- | --- | --- | --- | --- |
| **hasAttachment** | Primeiro Elemento | - | `boolean` | Indica se o campo possuí anexo (tem descrição e o anexo está na tabela de anexos) |
| **isValid** | Primeiro Elemento | - | `boolean` | Indica se o campo está válido. Caso ele possua um valor (foi feito upload do anexo), mas o anexo não está na tabela de anexos, indicará campo inválido. Este método é executado em um único campo |
| **deleteAttachment** | Todos Elementos | - | `JQuery` | Remove o anexo do campo. Útil para quando excluir uma linha de uma tabela Pai Filho |
| **showActionButton** | Todos Elementos | - | `JQuery` | Exibe o botão de ação |
| **hideActionButton** | Todos Elementos | - | `JQuery` | Oculta o botão de ação |
| **prefixName** | Todos Elementos | string\|boolean | `JQuery` | Altera o prefixName para os elementos |
| **filename** | Todos Elementos | string, string\|boolean | `JQuery` | Altera o filename configurado para os elementos. Pode passar, como segundo parâmetro, o prefixName |
Exemplos:
```javascript
if ($("#cnh").fluigFormAttachment("hasAttachment")) {
// Exemplo: habilitar outro campo de preenchimento
}
function beforeSendValidate(numState, nextState) {
if (!$("#cnh").fluigFormAttachment("isValid")) {
throw "O Anexo da CNH não foi enviado corretamente. Remova-o e envie novamente";
}
}
// Removendo anexos da Pai Filho ao excluir linha
$("#tabelaPaiFilho").on("click", ".removeItem", function() {
$(".anexos", $(this).closest("tr")).fluigFormAttachment("deleteAttachment");
fnWdkRemoveChild(this);
});
// Habilitando/Desabilitando a ação de acordo com o preenchimento de um campo
$("#descricao").on("change", function () {
this.value = this.value.trim();
if (!this.value.length) {
$("#anexo").fluigFormAttachment("hideActionButton");
return;
}
// Trocando o filename pra ser dinâmico com a descrição
// Caso exista uma descrição igual nos anexos exibirá erro
// no momento de selecionar o arquivo.
$("#anexo")
.data("filename", this.value)
.fluigFormAttachment("showActionButton")
;
// Altera o filename igual ao exemplo anterior
$("#anexo").fluigFormAttachment("filename", this.value).fluigFormAttachment("showActionButton");
// Altera prefixName no componente
$("#anexo").fluigFormAttachment("prefixName", true);
// ou
$("#anexo").fluigFormAttachment("prefixName", "novoprefixo");
});
```
## Contribuições
Sinta-se à vontade para indicar bugs e sugestões abrindo issues.
@@ -0,0 +1,138 @@
<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-3 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-3 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="" />
</div>
</form>
</div>
<script type="text/javascript" src="./assets/js/checklist.js"></script>
</body>
</html>
+585
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%; }
}
+859
View File
@@ -0,0 +1,859 @@
var INDICADORES = [
{ pilar: "PILAR ARQUITETURA", indicador: "Fotos da Fachada (letreiro luminoso com acessibilidade)", penalidade: "GRAVISSIMA", peso: 4, maxFotos: 1 },
{ pilar: "PILAR ARQUITETURA", indicador: "Fotos de todos equipamentos", detalhe: "Mobiliario, gondolas, tela digital, caixa, mesas, cavalete, baianas e coringa.", penalidade: "GRAVE", peso: 3, maxFotos: 8 },
{ pilar: "PILAR ARQUITETURA", indicador: "Fotos da iluminacao (luzes da vitrine e interior da loja)", penalidade: "GRAVE", peso: 3, maxFotos: 2 },
{ pilar: "PILAR ARQUITETURA", indicador: "Foto do coletor de embalagem (Botirecicla)", penalidade: "MEDIA", peso: 2, maxFotos: 1 },
{ pilar: "PILAR ARQUITETURA", indicador: "Fotos dos mobshops da loja", penalidade: "MEDIA", peso: 2, maxFotos: 2 },
{ pilar: "PILAR VISUAL MERCHANDISING", indicador: "Foto da vitrine (identificando todas comunicacoes)", penalidade: "GRAVE", peso: 3, maxFotos: 1 },
{ pilar: "PILAR VISUAL MERCHANDISING", indicador: "Fotos enquadrando cada sessao", detalhe: "Feminina, masculina, infantil, cuidados, cabelos, presentes e maquiagem.", penalidade: "GRAVE", peso: 3, maxFotos: 7 },
{ pilar: "PILAR VISUAL MERCHANDISING", indicador: "Fotos das comunicacoes destaques e storytelling", detalhe: "Feminina, masculina e cuidados.", penalidade: "GRAVE", peso: 3, maxFotos: 3 },
{ pilar: "PILAR VISUAL MERCHANDISING", indicador: "Foto das embalagens", penalidade: "MEDIA", peso: 2, maxFotos: 1 },
{ pilar: "PILAR VISUAL MERCHANDISING", indicador: "Foto do QR-Code de politica de privacidade", penalidade: "GRAVISSIMA", peso: 4, maxFotos: 1 },
{ pilar: "PILAR VISUAL MERCHANDISING", indicador: "Foto do codigo de defesa do consumidor", penalidade: "GRAVISSIMA", peso: 4, maxFotos: 1 },
{ pilar: "PILAR VISUAL MERCHANDISING", indicador: "Foto do verso da tela digital", penalidade: "GRAVE", peso: 3, maxFotos: 1 },
{ pilar: "PILAR EXPOSIÇÃO", indicador: "Fotos das datas de validade dos produtos", detalheBullets: [
"# Produtos O Boticario",
"Cooffe Woman",
"Linha Casa 214",
"5 itens da linha Botik",
"Dr. Botica",
"Portinari",
"Celebre",
"Floratta",
"Arbo",
"Shampoo e Condicionador - Linha Match",
"5 provadores de Make B",
"5 provadores da linha Intense",
"# Produtos QDB?",
"2 itens da linha Instamatte",
"Protetor solar facial",
"Corretivo liquido",
"Mascara para cilios",
"10 batons hidratantes",
"Shampoo e Condicionador Au.migos"
], penalidade: "GRAVISSIMA", peso: 4, maxFotos: 22 },
{ pilar: "PILAR EXPOSIÇÃO", indicador: "Foto dos aplicadores descartaveis", penalidade: "MEDIA", peso: 2, maxFotos: 1 },
{ pilar: "PILAR EXPOSIÇÃO", indicador: "Foto da exposição da sessão Make.B", penalidade: "GRAVE", peso: 3, maxFotos: 1 },
{ pilar: "PILAR EXPOSIÇÃO", indicador: "Foto da exposição da sessão Intense", penalidade: "GRAVE", peso: 3, maxFotos: 1 },
{ pilar: "PILAR UNIFORME", indicador: "Fotos dos uniformes (do pescoco para baixo)", penalidade: "GRAVE", peso: 3, maxFotos: 2 },
{ pilar: "PILAR INFORMATIVA", indicador: "Foto do espaco onde ficam os produtos destinados a logistica", penalidade: "NAO PONTUA", peso: 0, maxFotos: 1 }
];
var FATORES = {
"CONFORME": 1,
"NAO_CONFORME": 0
};
var ALLOWED_EXT = { "pdf": true, "jpg": true, "jpeg": true, "png": true };
var REQUIRE_ATTACHMENT = false; // mude para true para voltar a exigir anexo
var CURRENT_PROFILE = "LOJA";
var KNOWN_ATTACHMENT_KEYS = {};
// Ajuste os codigos de atividade do seu processo aqui.
var FLOW_CONFIG = {
lojaStates: [0, 4],
analistaStates: [5]
};
function badgeClass(penalidade) {
if (penalidade === "GRAVISSIMA") return "danger";
if (penalidade === "GRAVE") return "warning";
if (penalidade === "MEDIA") return "info";
if (penalidade === "LEVE") return "default";
return "primary";
}
function getClassificacao(score) {
if (score >= 90) return "Excelente";
if (score >= 80) return "Bom";
if (score >= 70) return "Regular";
return "Critico";
}
function toTitleCase(text) {
var s = String(text || "").toLowerCase().trim();
if (!s) return "";
var words = s.split(/\s+/);
for (var i = 0; i < words.length; i++) {
var w = words[i];
if (!w) continue;
words[i] = w.charAt(0).toUpperCase() + w.slice(1);
}
return words.join(" ");
}
function indicadorCountLabel(n) {
return n === 1 ? "1 indicador" : (n + " indicadores");
}
function renderDetalhe(item) {
if (Array.isArray(item.detalheBullets) && item.detalheBullets.length) {
var html = "<div class='indicador-help'><ul>";
for (var i = 0; i < item.detalheBullets.length; i++) {
var txt = String(item.detalheBullets[i] || "");
if (txt.indexOf("# ") === 0) {
html += "<li class='indicador-group'>" + escapeHtml(txt.substring(2)) + "</li>";
} else {
html += "<li>" + escapeHtml(txt) + "</li>";
}
}
html += "</ul></div>";
return html;
}
if (item.detalhe) {
return "<div class='indicador-help'>" + escapeHtml(item.detalhe) + "</div>";
}
return "";
}
function getResumoPilaresHtml() {
var rows = [
{ nome: "Arquitetura", texto: "Fachada da loja e estrutura física, evidenciando marca, iluminação, acessibilidade, equipamentos e estado geral do ambiente." },
{ nome: "Visual Merchandising", texto: "Vitrine e área interna com comunicação vigente, padrão visual, organização, embalagens corretas e bom estado dos materiais." },
{ nome: "Exposição", texto: "Exposição de produtos em vitrine e interior, com precificação, validade visível, testes, preenchimento de prateleiras e uso de aplicadores." },
{ nome: "Uniforme", texto: "Equipe em atendimento com uso correto de uniforme, calçados e acessórios conforme padrão estabelecido." },
{ nome: "Informativa", texto: "Ambiente com foco em informações operacionais, logística reversa, armazenamento de materiais e comunicação de serviços." }
];
var html = "<div class='pilar-summary-card'><div class='summary-title'>Guia dos Pilares</div>";
for (var i = 0; i < rows.length; i++) {
html += "<div class='summary-item'><span class='summary-name'>" + escapeHtml(rows[i].nome) + ":</span> " + escapeHtml(rows[i].texto) + "</div>";
}
html += "</div>";
return html;
}
function buildAnexoInputs(index, maxFotos) {
var total = parseInt(maxFotos, 10);
if (isNaN(total) || total < 1) total = 1;
var html = "<div class='anexo-multi-list'>";
for (var i = 1; i <= total; i++) {
var anexoId = "anexo_" + index + "_" + i;
var anexoFileName = "EVID_" + index + "_F" + i;
var extraClass = i > 2 ? " extra-slot" : "";
html += ""
+ "<div class='anexo-slot" + extraClass + "'>"
+ "<div class='anexo-slot-label'>Foto " + i + " de " + total + "</div>"
+ "<input type='text' class='form-control anexo-plugin' readonly id='" + anexoId + "' name='" + anexoId + "' data-filename='" + anexoFileName + "' data-accept='image/*,.pdf' data-required='true' />"
+ "</div>";
}
html += "</div>";
if (total > 2) {
html += "<div class='anexo-toggle-wrap'><button type='button' class='btn btn-default btn-sm anexo-toggle' data-expanded='false' data-total='" + total + "'>Mostrar todas (" + total + ")</button></div>";
}
return html;
}
function buildAuditRow(item, index) {
var selectResultado = ""
+ "<select class='form-control resultado'>"
+ "<option value=''>Selecione</option>"
+ "<option value='CONFORME'>Conforme</option>"
+ "<option value='NAO_CONFORME'>Nao Conforme</option>"
+ "</select>";
var maxFotos = parseInt(item.maxFotos, 10);
if (isNaN(maxFotos) || maxFotos < 1) maxFotos = 1;
var anexosHtml = buildAnexoInputs(index, maxFotos);
return ""
+ "<div class='audit-row' data-pilar='" + escapeHtml(item.pilar) + "' data-indicador='" + escapeHtml(item.indicador) + "' data-penalidade='" + escapeHtml(item.penalidade) + "' data-max-fotos='" + maxFotos + "'>"
+ "<div class='audit-row-main'>"
+ "<div class='audit-row-top'>"
+ "<div class='audit-info'>"
+ "<div class='indicador-cell'>" + escapeHtml(item.indicador) + "</div>"
+ renderDetalhe(item)
+ "</div>"
+ "<div class='anexos-cell anexo-wrap'>"
+ anexosHtml
+ "<div class='anexo-progress'><span class='anexo-progress-count'>0/" + maxFotos + "</span> fotos enviadas</div>"
+ "<div class='anexo-auto-hint'>Use os botoes de upload/visualizar em cada foto.</div>"
+ "</div>"
+ "</div>"
+ "<div class='meta-grid col-analista'>"
+ "<div class='meta-box'><div class='meta-label'>Penalidade</div><div class='meta-value'><span class='label label-default penalidade-badge penalidade-status'>Aguardando</span></div></div>"
+ "<div class='meta-box'><div class='meta-label'>Peso</div><div class='meta-value'><span class='peso'>" + item.peso + "</span></div></div>"
+ "<div class='meta-box'><div class='meta-label'>Fator</div><div class='meta-value fator'>-</div></div>"
+ "<div class='meta-box'><div class='meta-label'>Pontuacao</div><div class='meta-value pontuacao'>0</div></div>"
+ "<div class='meta-box resultado-wrap'><div class='meta-label'>Resultado</div><div class='meta-value'>" + selectResultado + "</div></div>"
+ "</div>"
+ "<div class='just-wrap col-analista'><input type='text' class='form-control justificativa' placeholder='Obrigatoria se Nao Conforme' /></div>"
+ "</div>"
+ "</div>";
}
function montaTabela() {
var grupos = {};
for (var i = 0; i < INDICADORES.length; i++) {
var item = INDICADORES[i];
if (!grupos[item.pilar]) grupos[item.pilar] = [];
grupos[item.pilar].push(item);
}
var pilares = Object.keys(grupos);
var html = "";
var rowIndex = 1;
for (var p = 0; p < pilares.length; p++) {
var nomePilar = pilares[p];
var linhas = grupos[nomePilar];
var openStyle = " style='display:none;'";
var totalFotosPilar = 0;
for (var t = 0; t < linhas.length; t++) {
totalFotosPilar += parseInt(linhas[t].maxFotos, 10) || 0;
}
html += "<div class='pilar-panel'>";
html += "<div class='pilar-head' data-target='pilarBody" + p + "' data-total-fotos='" + totalFotosPilar + "'>";
html += "<div class='pilar-title-row'><span class='pilar-state-dot state-pending' title='Nao iniciado'>✕</span><span class='pilar-title'>" + escapeHtml(toTitleCase(nomePilar.replace("PILAR ", ""))) + "</span></div>";
html += "<div class='pilar-photo-row'><span class='pilar-photo-count'>0/" + totalFotosPilar + " fotos</span><span class='pilar-missing'>" + totalFotosPilar + " faltando</span></div>";
html += "<div class='pilar-mini-track'><span class='pilar-mini-fill'></span></div>";
html += "<div class='pilar-foot'><span class='pilar-count'>" + indicadorCountLabel(linhas.length) + "</span><span class='pilar-open-cta'>Abrir Pilar →</span></div>";
html += "</div>";
html += "<div class='pilar-body' id='pilarBody" + p + "'" + openStyle + ">";
for (var r = 0; r < linhas.length; r++) {
html += buildAuditRow(linhas[r], rowIndex);
rowIndex++;
}
html += "</div></div>";
}
html += getResumoPilaresHtml();
$("#pilaresContainer").html(html);
}
function getExt(fileName) {
var n = (fileName || "").toLowerCase();
var pos = n.lastIndexOf(".");
return pos > -1 ? n.substring(pos + 1) : "";
}
function escapeHtml(text) {
return String(text || "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function isImageExt(fileName) {
var ext = getExt(fileName);
return ext === "jpg" || ext === "jpeg" || ext === "png";
}
function getStateFromUrl(url) {
var u = String(url || "");
if (!u) return "";
var patterns = [
/(?:\?|&)WKNumState=(\d+)/i,
/(?:\?|&)state=(\d+)/i,
/(?:\?|&)stateSequence=(\d+)/i,
/(?:\?|&)choosedState=(\d+)/i,
/(?:\?|&)numState=(\d+)/i
];
for (var i = 0; i < patterns.length; i++) {
var m = u.match(patterns[i]);
if (m && m[1]) return m[1];
}
return "";
}
function getCurrentState() {
var candidates = [];
try {
candidates.push(window.WKNumState);
candidates.push(window.WKCurrentState);
candidates.push($("input[name='WKNumState']").val());
candidates.push($("input[name='WKCurrentState']").val());
candidates.push($("#WKNumState").val());
candidates.push($("#WKCurrentState").val());
candidates.push(getStateFromUrl(window.location && window.location.href));
} catch (e) {}
try {
if (window.parent) {
candidates.push(window.parent.WKNumState);
candidates.push(window.parent.WKCurrentState);
if (window.parent.$) {
candidates.push(window.parent.$("input[name='WKNumState']").val());
candidates.push(window.parent.$("input[name='WKCurrentState']").val());
candidates.push(window.parent.$("#WKNumState").val());
candidates.push(window.parent.$("#WKCurrentState").val());
}
candidates.push(getStateFromUrl(window.parent.location && window.parent.location.href));
}
} catch (e2) {}
for (var j = 0; j < candidates.length; j++) {
var parsed = parseInt(String(candidates[j] || "").trim(), 10);
if (!isNaN(parsed) && parsed > 0) return parsed;
}
return 0;
}
function resolveProfileByState(state) {
for (var i = 0; i < FLOW_CONFIG.analistaStates.length; i++) {
if (FLOW_CONFIG.analistaStates[i] === state) return "ANALISTA";
}
for (var j = 0; j < FLOW_CONFIG.lojaStates.length; j++) {
if (FLOW_CONFIG.lojaStates[j] === state) return "LOJA";
}
try {
var pageText = (
$("h1").first().text()
|| $(".page-header h1").first().text()
|| (window.parent && window.parent.$ ? window.parent.$("h1").first().text() : "")
|| ""
).toLowerCase();
if (pageText.indexOf("validar planograma") > -1) return "ANALISTA";
} catch (e) {}
return "LOJA";
}
function getCurrentUserCode() {
try {
if (window.WCMAPI && window.WCMAPI.userCode) return String(window.WCMAPI.userCode);
} catch (e) {}
try {
if (window.parent && window.parent.WCMAPI && window.parent.WCMAPI.userCode) {
return String(window.parent.WCMAPI.userCode);
}
} catch (e2) {}
return "";
}
function getNaoConformes() {
var list = [];
$(".audit-row").each(function () {
var $tr = $(this);
if ($tr.find(".resultado").val() === "NAO_CONFORME") {
list.push({
pilar: $tr.attr("data-pilar") || "",
indicador: $tr.attr("data-indicador") || ""
});
}
});
return list;
}
function renderNaoConformeResumo() {
var list = getNaoConformes();
var $box = $("#naoConformeBox");
var $list = $("#naoConformeList");
$list.empty();
if (!list.length) {
$box.hide();
return;
}
for (var i = 0; i < list.length; i++) {
var item = list[i] || {};
var pilar = item.pilar || "";
var indicador = item.indicador || "";
var texto = pilar ? (pilar + " - " + indicador) : indicador;
$list.append(
"<button type='button' class='nc-item' data-pilar='" + escapeHtml(pilar) + "' data-indicador='" + escapeHtml(indicador) + "'>"
+ escapeHtml(texto)
+ "</button>"
);
}
$box.show();
}
function applyProfileUI() {
var isAnalista = CURRENT_PROFILE === "ANALISTA";
$(".col-analista").toggle(isAnalista);
$(".audit-card").toggle(isAnalista);
$(".resultado").prop("disabled", !isAnalista);
$(".justificativa").prop("disabled", !isAnalista);
if (isAnalista) {
$(".anexo-auto-hint").hide();
} else {
$(".anexo-auto-hint").show();
}
applyAttachmentActionVisibility();
$("#profileBadge").text("Perfil: " + (isAnalista ? "Analista" : "Loja") + " (atividade " + getCurrentState() + ")");
}
function updateCompletionStatus() {
var total = 0;
var preenchidos = 0;
$(".anexo-plugin").each(function () {
total++;
var nome = ($(this).val() || "").trim();
if (nome) preenchidos++;
});
$("#progressBadge").text("Evidencias: " + preenchidos + "/" + total);
var perc = total > 0 ? Math.round((preenchidos / total) * 100) : 0;
$("#globalProgressFill").css("width", perc + "%");
$("#globalProgressPercent").text(perc + "%");
$("#globalProgressMeta").text(preenchidos + " de " + total + " fotos enviadas");
updatePilarProgress();
}
function updateRowAttachmentProgress($row) {
if (!$row || !$row.length) return;
var total = 0;
var preenchidos = 0;
$row.find(".anexo-slot").each(function () {
var $slot = $(this);
var nome = ($slot.find(".anexo-plugin").val() || "").trim();
total++;
var temAnexo = !!nome;
if (temAnexo) preenchidos++;
$slot.toggleClass("slot-filled", temAnexo);
});
$row.find(".anexo-progress-count").text(preenchidos + "/" + total);
}
function updatePilarProgress() {
$(".pilar-panel").each(function () {
var $panel = $(this);
var total = 0;
var preenchidos = 0;
$panel.find(".anexo-plugin").each(function () {
total++;
if (($(this).val() || "").trim()) preenchidos++;
});
var perc = total > 0 ? Math.round((preenchidos / total) * 100) : 0;
var faltantes = Math.max(0, total - preenchidos);
$panel.find(".pilar-photo-count").text(preenchidos + "/" + total + " fotos");
$panel.find(".pilar-missing").text(faltantes > 0 ? (faltantes + " faltando") : "100% concluido");
$panel.find(".pilar-mini-fill").css("width", perc + "%");
var $dot = $panel.find(".pilar-state-dot");
var cls = "state-pending";
var fillColor = "#9fb2c6";
var icon = "✕";
var dotTitle = "Nao iniciado";
if (total > 0 && preenchidos === total) {
cls = "state-done";
fillColor = "#2f9d78";
icon = "✓";
dotTitle = "Concluido";
} else if (preenchidos > 0) {
cls = "state-progress";
fillColor = "#d18a2f";
icon = "⚠";
dotTitle = "Em andamento";
}
$dot.removeClass("state-pending state-progress state-done").addClass(cls).text(icon).attr("title", dotTitle);
$panel.find(".pilar-mini-fill").css("background", fillColor);
});
}
function initAttachmentPlugin() {
if (typeof $.fn.fluigFormAttachment !== "function") {
FLUIGC.toast({
title: "Plugin:",
message: "fluigFormAttachment.js nao foi carregado.",
type: "warning"
});
return;
}
$(".anexo-plugin").each(function () {
var $f = $(this);
if ($f.data("ffaInit")) return;
$f.fluigFormAttachment();
$f.data("ffaInit", true);
});
}
function applyAttachmentActionVisibility() {
if (typeof $.fn.fluigFormAttachment !== "function") return;
var isAnalista = CURRENT_PROFILE === "ANALISTA";
$(".anexo-plugin").each(function () {
var $f = $(this);
if (!$f.data("ffaInit")) return;
if (isAnalista) {
$f.fluigFormAttachment("hideActionButton");
} else {
$f.fluigFormAttachment("showActionButton");
}
});
}
function findNativeSendButtons($root) {
var $all = $root.find("button, input[type='button'], input[type='submit'], a");
return $all.filter(function () {
var $el = $(this);
var txt = ($el.is("input") ? ($el.val() || "") : ($el.text() || "")).trim().toLowerCase();
if (!txt) return false;
return txt === "enviar" || txt.indexOf(" enviar") > -1 || txt.indexOf("enviar ") > -1;
});
}
function hideNativeSendButtons() {
try {
var $local = findNativeSendButtons($(document));
$local.not("#btnEnviarForm").css("display", "none");
$local.each(function () {
var $group = $(this).closest(".btn-group, .btn-toolbar, .btn-group-vertical, .dropup, .dropdown");
if ($group.length) {
$group.find(".dropdown-toggle, .btn.dropdown-toggle").css("display", "none");
}
});
} catch (e) {}
try {
if (window.parent && window.parent.document) {
var $parentDoc = $(window.parent.document);
var $parentBtns = findNativeSendButtons($parentDoc);
$parentBtns.css("display", "none");
$parentBtns.each(function () {
var $group = $(this).closest(".btn-group, .btn-toolbar, .btn-group-vertical, .dropup, .dropdown");
if ($group.length) {
$group.find(".dropdown-toggle, .btn.dropdown-toggle").css("display", "none");
}
});
// Fallback: oculta toggles "soltos" que sobram no split button.
$parentDoc.find(".dropdown-toggle, .btn.dropdown-toggle").filter(function () {
var txt = ($(this).text() || "").trim();
return txt === "" || txt === "▼" || txt === "▾" || txt === "v";
}).each(function () {
var $toggle = $(this);
var $group = $toggle.closest(".btn-group, .btn-toolbar, .btn-group-vertical, .dropup, .dropdown");
if (!$group.length) return;
var hasSendRef = findNativeSendButtons($group).length > 0;
if (hasSendRef) {
$toggle.css("display", "none");
}
});
}
} catch (e2) {}
}
function clickNativeSendButton() {
try {
var $parentDoc = window.parent && window.parent.document ? $(window.parent.document) : $();
var $native = findNativeSendButtons($parentDoc).first();
if ($native.length) {
$native.trigger("click");
return true;
}
} catch (e) {}
try {
var $local = findNativeSendButtons($(document)).not("#btnEnviarForm").first();
if ($local.length) {
$local.trigger("click");
return true;
}
} catch (e2) {}
return false;
}
function syncPayload() {
var rows = [];
var naoConformeList = [];
$(".audit-row").each(function () {
var $tr = $(this);
var peso = parseFloat($tr.find(".peso").text()) || 0;
var resultado = $tr.find(".resultado").val();
var fator = FATORES[resultado];
var pontuacao = (typeof fator === "number") ? (peso * fator) : null;
var anexos = [];
$tr.find(".anexo-plugin").each(function () {
var nome = ($(this).val() || "").trim();
anexos.push(nome);
});
var anexosPreenchidos = [];
for (var a = 0; a < anexos.length; a++) {
if (anexos[a]) anexosPreenchidos.push(anexos[a]);
}
var indicador = $tr.attr("data-indicador") || "";
if (resultado === "NAO_CONFORME") {
naoConformeList.push(indicador);
}
rows.push({
pilar: $tr.attr("data-pilar") || "",
indicador: indicador,
penalidade: $tr.attr("data-penalidade") || "",
peso: peso,
resultado: resultado || "",
fator: (typeof fator === "number") ? fator : null,
pontuacao: pontuacao,
justificativa: ($tr.find(".justificativa").val() || "").trim(),
anexoSequence: "",
anexoKey: "",
anexoNome: anexosPreenchidos.length ? anexosPreenchidos[0] : "",
anexos: anexos,
qtdAnexosPreenchidos: anexosPreenchidos.length
});
updateRowAttachmentProgress($tr);
});
$("#qtdNaoConforme").val(String(naoConformeList.length));
$("#temNaoConforme").val(naoConformeList.length > 0 ? "true" : "false");
$("#listaNaoConforme").val(naoConformeList.join(" | "));
$("#saidaAnalise").val(naoConformeList.length > 0 ? "NAO_CONFORME" : "CONFORME");
$("#auditoriaPayload").val(JSON.stringify(rows));
updateCompletionStatus();
renderNaoConformeResumo();
}
function findRowByIndicador(indicador) {
var $found = $();
$(".audit-row").each(function () {
if ($(this).attr("data-indicador") === indicador) {
$found = $(this);
return false;
}
});
return $found;
}
function updatePenalidadeVisual($tr) {
var resultado = $tr.find(".resultado").val();
var penalidadeBase = ($tr.attr("data-penalidade") || "").trim();
var $badge = $tr.find(".penalidade-status");
var $justWrap = $tr.find(".just-wrap");
if (resultado === "CONFORME") {
$badge
.removeClass("label-danger label-warning label-info label-primary label-default")
.addClass("label-success")
.text("Conforme");
$justWrap.hide();
return;
}
if (resultado === "NAO_CONFORME") {
$badge
.removeClass("label-success label-danger label-warning label-info label-primary label-default")
.addClass("label-" + badgeClass(penalidadeBase))
.text(penalidadeBase || "Nao Conforme");
$justWrap.show();
return;
}
$badge
.removeClass("label-success label-danger label-warning label-info label-primary")
.addClass("label-default")
.text("Aguardando");
$justWrap.hide();
}
function restorePayload() {
var raw = ($("#auditoriaPayload").val() || "").trim();
if (!raw) return;
var data = [];
try {
data = JSON.parse(raw);
if (!Array.isArray(data)) return;
} catch (e) {
return;
}
for (var i = 0; i < data.length; i++) {
var saved = data[i];
var $tr = findRowByIndicador(saved.indicador);
if (!$tr.length) continue;
if (saved.resultado) $tr.find(".resultado").val(saved.resultado);
if (saved.justificativa) $tr.find(".justificativa").val(saved.justificativa);
if (Array.isArray(saved.anexos)) {
var $inputs = $tr.find(".anexo-plugin");
for (var k = 0; k < saved.anexos.length; k++) {
if (k >= $inputs.length) break;
if (saved.anexos[k]) {
$($inputs.get(k)).val(saved.anexos[k]).trigger("change");
}
}
} else if (saved.anexoNome) {
$tr.find(".anexo-plugin").first().val(saved.anexoNome).trigger("change");
}
updatePenalidadeVisual($tr);
}
}
function recalc() {
var pontosObtidos = 0;
var pontosPossiveis = 0;
$(".audit-row").each(function () {
var $tr = $(this);
var peso = parseFloat($tr.find(".peso").text()) || 0;
var resultado = $tr.find(".resultado").val();
var fator = FATORES[resultado];
var pontuacao = 0;
updatePenalidadeVisual($tr);
if (typeof fator === "number") {
pontuacao = peso * fator;
$tr.find(".fator").text(fator.toFixed(2));
$tr.find(".pontuacao").text(pontuacao.toFixed(2));
if (peso > 0) {
pontosPossiveis += peso;
pontosObtidos += pontuacao;
}
} else {
$tr.find(".fator").text("-");
$tr.find(".pontuacao").text("0");
}
});
var score = pontosPossiveis > 0 ? (pontosObtidos / pontosPossiveis) * 100 : 0;
$("#pontosObtidos").text(pontosObtidos.toFixed(2));
$("#pontosPossiveis").text(pontosPossiveis.toFixed(2));
$("#scoreFinal").text(score.toFixed(2) + "%");
$("#classificacao").text(getClassificacao(score));
}
function validarFormulario() {
var erros = [];
$(".audit-row").each(function (idx) {
var $tr = $(this);
var resultado = $tr.find(".resultado").val();
var justificativa = ($tr.find(".justificativa").val() || "").trim();
var $anexoInputs = $tr.find(".anexo-plugin");
var indicador = $tr.attr("data-indicador") || "";
var $resultadoCell = $tr.find(".resultado-wrap");
var $anexoCell = $tr.find(".anexo-wrap");
if (resultado === "NAO_CONFORME" && justificativa.length === 0) {
erros.push("Linha " + (idx + 1) + " (" + indicador + "): justificativa obrigatoria.");
$tr.find(".just-wrap").addClass("has-error");
} else {
$tr.find(".just-wrap").removeClass("has-error");
}
if (CURRENT_PROFILE === "ANALISTA") {
if (!resultado) {
erros.push("Linha " + (idx + 1) + " (" + indicador + "): selecione o resultado.");
$resultadoCell.addClass("has-error");
} else {
$resultadoCell.removeClass("has-error");
}
}
if (REQUIRE_ATTACHMENT) {
var hasErrorAnexo = false;
$anexoInputs.each(function () {
var $anexoInput = $(this);
var anexoNome = ($anexoInput.val() || "").trim();
var hasAttachment = anexoNome.length > 0;
var isValidAttachment = true;
if (typeof $.fn.fluigFormAttachment === "function" && $anexoInput.data("ffaInit")) {
try {
hasAttachment = $anexoInput.fluigFormAttachment("hasAttachment");
isValidAttachment = $anexoInput.fluigFormAttachment("isValid");
} catch (e) {}
}
if (!hasAttachment) {
hasErrorAnexo = true;
return false;
}
if (!isValidAttachment) {
hasErrorAnexo = true;
return false;
}
if (!ALLOWED_EXT[getExt(anexoNome)]) {
hasErrorAnexo = true;
return false;
}
});
if (hasErrorAnexo) {
erros.push("Linha " + (idx + 1) + " (" + indicador + "): revise os anexos obrigatorios (PDF/JPG/PNG).");
$anexoCell.addClass("has-error");
} else {
$anexoCell.removeClass("has-error");
}
} else {
$anexoCell.removeClass("has-error");
}
});
syncPayload();
if (erros.length > 0) {
FLUIGC.toast({
title: "Validacao:",
message: erros[0],
type: "warning"
});
return false;
}
return true;
}
$(document).ready(function () {
var wkState = getCurrentState();
CURRENT_PROFILE = resolveProfileByState(wkState);
if (!($("#usuarioRetorno").val() || "").trim()) {
$("#usuarioRetorno").val(getCurrentUserCode());
}
montaTabela();
initAttachmentPlugin();
restorePayload();
applyProfileUI();
recalc();
syncPayload();
$(".audit-row").each(function () {
updatePenalidadeVisual($(this));
});
$("#pilaresContainer").on("click", ".pilar-head", function () {
var target = $(this).attr("data-target");
var $head = $(this);
var $panel = $head.closest(".pilar-panel");
$("#" + target).stop(true, true).slideToggle(120, function () {
var isOpen = $(this).is(":visible");
$head.toggleClass("is-open", isOpen);
$panel.toggleClass("is-open", isOpen);
$head.find(".pilar-open-cta").text(isOpen ? "Fechar Pilar ↑" : "Abrir Pilar →");
});
});
$("#pilaresContainer").on("change", ".resultado", function () {
recalc();
syncPayload();
});
$("#pilaresContainer").on("change keyup", ".justificativa", function () {
syncPayload();
});
$("#pilaresContainer").on("change", ".anexo-plugin", function () {
syncPayload();
});
$("#pilaresContainer").on("click", ".anexo-toggle", function () {
var $btn = $(this);
var expanded = $btn.attr("data-expanded") === "true";
var total = parseInt($btn.attr("data-total"), 10) || 0;
var $wrap = $btn.closest(".anexos-cell");
var $list = $wrap.find(".anexo-multi-list").first();
if (expanded) {
$list.removeClass("expanded");
$btn.attr("data-expanded", "false").text("Mostrar todas (" + total + ")");
} else {
$list.addClass("expanded");
$btn.attr("data-expanded", "true").text("Mostrar menos");
}
});
$(document).on("click", ".nc-item", function () {
var pilar = $(this).attr("data-pilar") || "";
var indicador = $(this).attr("data-indicador") || "";
var $tr = $(".audit-row[data-pilar='" + pilar.replace(/'/g, "\\'") + "'][data-indicador='" + indicador.replace(/'/g, "\\'") + "']").first();
if (!$tr.length) {
$tr = findRowByIndicador(indicador);
}
if (!$tr.length) return;
$tr.addClass("audit-row-target");
setTimeout(function () { $tr.removeClass("audit-row-target"); }, 1600);
$("html, body").animate({ scrollTop: $tr.offset().top - 120 }, 250);
});
hideNativeSendButtons();
setTimeout(hideNativeSendButtons, 600);
$("#btnEnviarForm").on("click", function () {
if (!validarFormulario()) return;
var clicked = clickNativeSendButton();
if (!clicked) {
FLUIGC.toast({
title: "Envio:",
message: "Nao foi possivel acionar o botao nativo de Enviar.",
type: "warning"
});
}
});
window.beforeSendValidate = function () {
return validarFormulario();
};
});
@@ -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