Compare commits
No commits in common. "65118f28386eb75010a88868085c9033c12e5aa0" and "0d4ab67e0f0a1065577475d42259f3ba83185683" have entirely different histories.
65118f2838
...
0d4ab67e0f
7 changed files with 11 additions and 285 deletions
|
|
@ -3,7 +3,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
@ -24,128 +23,12 @@ func main() {
|
||||||
js.Global().Set("__eli_nps_wasm_decidir", js.FuncOf(decidir))
|
js.Global().Set("__eli_nps_wasm_decidir", js.FuncOf(decidir))
|
||||||
js.Global().Set("__eli_nps_wasm_cooldown_ativo", js.FuncOf(cooldownAtivo))
|
js.Global().Set("__eli_nps_wasm_cooldown_ativo", js.FuncOf(cooldownAtivo))
|
||||||
js.Global().Set("__eli_nps_wasm_set_cooldown", js.FuncOf(setCooldown))
|
js.Global().Set("__eli_nps_wasm_set_cooldown", js.FuncOf(setCooldown))
|
||||||
js.Global().Set("__eli_nps_wasm_painel_init", js.FuncOf(painelInit))
|
|
||||||
js.Global().Set("__eli_nps_wasm_ready", true)
|
js.Global().Set("__eli_nps_wasm_ready", true)
|
||||||
|
|
||||||
// Mantém o módulo vivo.
|
// Mantém o módulo vivo.
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
||||||
func painelInit(this js.Value, args []js.Value) any {
|
|
||||||
// Painel: persistência de filtros no localStorage.
|
|
||||||
// Regras (.agent): lógica no WASM (Go). Aqui é best-effort e fail-open.
|
|
||||||
//
|
|
||||||
// - Salva: produto selecionado + checkbox "baixas".
|
|
||||||
// - Restaura: se a URL não tiver parâmetros e houver valor salvo,
|
|
||||||
// redireciona para /painel?produto=...&baixas=1.
|
|
||||||
//
|
|
||||||
// Importante: não roda fora do /painel.
|
|
||||||
loc := js.Global().Get("location")
|
|
||||||
if loc.IsUndefined() || loc.IsNull() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
path := loc.Get("pathname").String()
|
|
||||||
if path == "" || !strings.HasPrefix(path, "/painel") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
storage := js.Global().Get("localStorage")
|
|
||||||
if storage.IsUndefined() || storage.IsNull() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyProd = "eli_nps_painel_produto"
|
|
||||||
const keyBaixas = "eli_nps_painel_baixas"
|
|
||||||
|
|
||||||
// Aguarda o DOM estar pronto para conseguirmos acessar o form/option list.
|
|
||||||
doc := js.Global().Get("document")
|
|
||||||
if doc.IsUndefined() || doc.IsNull() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := js.FuncOf(func(this js.Value, args []js.Value) any {
|
|
||||||
// Best-effort: evita exceptions se APIs não existirem.
|
|
||||||
defer func() { _ = recover() }()
|
|
||||||
|
|
||||||
form := doc.Call("querySelector", `form[action="/painel"]`)
|
|
||||||
if form.IsUndefined() || form.IsNull() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sel := form.Call("querySelector", `select[name="produto"]`)
|
|
||||||
chk := form.Call("querySelector", `input[name="baixas"]`)
|
|
||||||
|
|
||||||
// Helper: verifica se option existe.
|
|
||||||
optionExists := func(selectEl js.Value, value string) bool {
|
|
||||||
if selectEl.IsUndefined() || selectEl.IsNull() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
opts := selectEl.Get("options")
|
|
||||||
ln := opts.Get("length").Int()
|
|
||||||
for i := 0; i < ln; i++ {
|
|
||||||
opt := opts.Index(i)
|
|
||||||
if opt.Get("value").String() == value {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
persist := func() {
|
|
||||||
if !sel.IsUndefined() && !sel.IsNull() {
|
|
||||||
v := strings.TrimSpace(sel.Get("value").String())
|
|
||||||
if v != "" {
|
|
||||||
storage.Call("setItem", keyProd, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
baixas := "0"
|
|
||||||
if !chk.IsUndefined() && !chk.IsNull() && chk.Get("checked").Truthy() {
|
|
||||||
baixas = "1"
|
|
||||||
}
|
|
||||||
storage.Call("setItem", keyBaixas, baixas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restaura/redirect apenas se não há query string.
|
|
||||||
s := loc.Get("search").String()
|
|
||||||
if strings.TrimSpace(s) == "" {
|
|
||||||
storedProd := strings.TrimSpace(storage.Call("getItem", keyProd).String())
|
|
||||||
storedBaixas := strings.TrimSpace(storage.Call("getItem", keyBaixas).String())
|
|
||||||
if storedProd != "" && optionExists(sel, storedProd) {
|
|
||||||
q := "?produto=" + url.QueryEscape(storedProd)
|
|
||||||
if storedBaixas == "1" {
|
|
||||||
q += "&baixas=1"
|
|
||||||
}
|
|
||||||
loc.Call("replace", path+q)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Se só baixas estiver setado, aplica também.
|
|
||||||
if storedBaixas == "1" {
|
|
||||||
loc.Call("replace", path+"?baixas=1")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Liga listeners para persistência.
|
|
||||||
onSubmit := js.FuncOf(func(this js.Value, args []js.Value) any { persist(); return nil })
|
|
||||||
onChange := js.FuncOf(func(this js.Value, args []js.Value) any { persist(); return nil })
|
|
||||||
form.Call("addEventListener", "submit", onSubmit)
|
|
||||||
form.Call("addEventListener", "change", onChange)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// DOMContentLoaded
|
|
||||||
readyState := doc.Get("readyState").String()
|
|
||||||
if readyState != "loading" {
|
|
||||||
// Se o WASM foi carregado depois do DOM pronto (comum no painel),
|
|
||||||
// rodamos imediatamente.
|
|
||||||
handler.Invoke()
|
|
||||||
} else {
|
|
||||||
doc.Call("addEventListener", "DOMContentLoaded", handler)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cooldownAtivo(this js.Value, args []js.Value) any {
|
func cooldownAtivo(this js.Value, args []js.Value) any {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,9 @@ type RespostaPainel struct {
|
||||||
ID string
|
ID string
|
||||||
RespondidoEm *time.Time
|
RespondidoEm *time.Time
|
||||||
PedidoCriadoEm time.Time
|
PedidoCriadoEm time.Time
|
||||||
InquilinoCodigo string
|
|
||||||
InquilinoNome string
|
|
||||||
UsuarioCodigo *string
|
UsuarioCodigo *string
|
||||||
UsuarioNome string
|
UsuarioNome string
|
||||||
UsuarioEmail *string
|
UsuarioEmail *string
|
||||||
UsuarioTelefone *string
|
|
||||||
Nota *int
|
Nota *int
|
||||||
Justificativa *string
|
Justificativa *string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -134,62 +133,6 @@ func (a AuthPainel) handlerPainel(w http.ResponseWriter, r *http.Request, store
|
||||||
func (a AuthPainel) renderPainelHTML(w http.ResponseWriter, d PainelDados) {
|
func (a AuthPainel) renderPainelHTML(w http.ResponseWriter, d PainelDados) {
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
|
||||||
formatarDataPainel := func(t time.Time) string {
|
|
||||||
// O banco armazena timestamptz (UTC normalizado). O requisito do painel é
|
|
||||||
// exibir no fuso de Brasília.
|
|
||||||
loc, err := time.LoadLocation("America/Sao_Paulo")
|
|
||||||
if err != nil {
|
|
||||||
// Fallback best-effort (sem regras de DST antigas, mas suficiente para
|
|
||||||
// ambientes sem tzdata).
|
|
||||||
loc = time.FixedZone("America/Sao_Paulo", -3*60*60)
|
|
||||||
}
|
|
||||||
return t.In(loc).Format("02/01/2006 15:04")
|
|
||||||
}
|
|
||||||
|
|
||||||
soDigitos := func(s string) string {
|
|
||||||
// Normalização best-effort para criar links tel/WhatsApp.
|
|
||||||
// Mantemos apenas dígitos e descartamos qualquer outro caractere.
|
|
||||||
re := regexp.MustCompile(`\D`)
|
|
||||||
return re.ReplaceAllString(s, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
linkEmail := func(email string) string {
|
|
||||||
email = strings.TrimSpace(email)
|
|
||||||
if email == "" {
|
|
||||||
return "-"
|
|
||||||
}
|
|
||||||
// mailto aceita percent-encoding; usar PathEscape evita injeção no href.
|
|
||||||
href := "mailto:" + url.PathEscape(email)
|
|
||||||
txt := template.HTMLEscapeString(email)
|
|
||||||
return "<a href=\"" + href + "\">" + txt + "</a>"
|
|
||||||
}
|
|
||||||
|
|
||||||
linkTelefone := func(telOriginal string) (hrefTel string, hrefWA string, htmlTel string) {
|
|
||||||
telOriginal = strings.TrimSpace(telOriginal)
|
|
||||||
if telOriginal == "" {
|
|
||||||
return "", "", "-"
|
|
||||||
}
|
|
||||||
dig := soDigitos(telOriginal)
|
|
||||||
if dig == "" {
|
|
||||||
// Se não há dígitos, exibimos apenas como texto.
|
|
||||||
return "", "", template.HTMLEscapeString(telOriginal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tel: — preferimos com '+' quando possível.
|
|
||||||
hrefTel = "tel:+" + dig
|
|
||||||
// WhatsApp wa.me exige número em formato internacional com dígitos.
|
|
||||||
waNum := dig
|
|
||||||
// Heurística: se parece número BR (10/11 dígitos) e não tem DDI, prefixa 55.
|
|
||||||
if (len(waNum) == 10 || len(waNum) == 11) && !strings.HasPrefix(waNum, "55") {
|
|
||||||
waNum = "55" + waNum
|
|
||||||
}
|
|
||||||
hrefWA = "https://wa.me/" + waNum
|
|
||||||
|
|
||||||
// Exibição do número: mantém o original (mais amigável), mas escapado.
|
|
||||||
htmlTel = "<a href=\"" + hrefTel + "\">" + template.HTMLEscapeString(telOriginal) + "</a>"
|
|
||||||
return hrefTel, hrefWA, htmlTel
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTML propositalmente simples (sem template engine) para manter isolado.
|
// HTML propositalmente simples (sem template engine) para manter isolado.
|
||||||
// Se quiser evoluir, dá pra migrar para templates.
|
// Se quiser evoluir, dá pra migrar para templates.
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|
@ -205,9 +148,6 @@ table{width:100%;border-collapse:collapse;font-size:13px;}
|
||||||
th,td{padding:8px;border-bottom:1px solid #eee;text-align:left;vertical-align:top;}
|
th,td{padding:8px;border-bottom:1px solid #eee;text-align:left;vertical-align:top;}
|
||||||
.muted{color:#666;font-size:12px;}
|
.muted{color:#666;font-size:12px;}
|
||||||
.badge{display:inline-block;padding:2px 8px;border-radius:999px;background:#f2f2f2;border:1px solid #e5e5e5;font-size:12px;}
|
.badge{display:inline-block;padding:2px 8px;border-radius:999px;background:#f2f2f2;border:1px solid #e5e5e5;font-size:12px;}
|
||||||
.iconbtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:10px;border:1px solid #e5e5e5;background:#fff;margin-right:6px;text-decoration:none;}
|
|
||||||
.iconbtn:hover{border-color:#bbb;background:#fafafa;}
|
|
||||||
.icon{width:16px;height:16px;display:block;}
|
|
||||||
</style></head><body>`)
|
</style></head><body>`)
|
||||||
|
|
||||||
b.WriteString("<div class=\"top\">")
|
b.WriteString("<div class=\"top\">")
|
||||||
|
|
@ -246,14 +186,11 @@ th,td{padding:8px;border-bottom:1px solid #eee;text-align:left;vertical-align:to
|
||||||
|
|
||||||
// Respostas
|
// Respostas
|
||||||
b.WriteString("<div class=\"card\" style=\"margin-top:12px\"><h2 style=\"margin:0 0 8px\">Respostas</h2>")
|
b.WriteString("<div class=\"card\" style=\"margin-top:12px\"><h2 style=\"margin:0 0 8px\">Respostas</h2>")
|
||||||
b.WriteString("<table><thead><tr><th>Data</th><th>Nota</th><th>Usuário</th><th>Inquilino</th><th>Email</th><th>Telefone</th><th>Comentário</th><th>Ações</th></tr></thead><tbody>")
|
b.WriteString("<table><thead><tr><th>Data</th><th>Nota</th><th>Usuário</th><th>Comentário</th></tr></thead><tbody>")
|
||||||
for _, r := range d.Respostas {
|
for _, r := range d.Respostas {
|
||||||
data := "-"
|
data := "-"
|
||||||
if r.RespondidoEm != nil {
|
if r.RespondidoEm != nil {
|
||||||
data = formatarDataPainel(*r.RespondidoEm)
|
data = r.RespondidoEm.Format("2006-01-02 15:04")
|
||||||
} else {
|
|
||||||
// fallback: apesar do painel listar "respondido", mantemos robustez.
|
|
||||||
data = formatarDataPainel(r.PedidoCriadoEm)
|
|
||||||
}
|
}
|
||||||
nota := "-"
|
nota := "-"
|
||||||
if r.Nota != nil {
|
if r.Nota != nil {
|
||||||
|
|
@ -263,43 +200,11 @@ th,td{padding:8px;border-bottom:1px solid #eee;text-align:left;vertical-align:to
|
||||||
if r.UsuarioCodigo != nil {
|
if r.UsuarioCodigo != nil {
|
||||||
usuario += " <span class=\"muted\">(" + template.HTMLEscapeString(*r.UsuarioCodigo) + ")</span>"
|
usuario += " <span class=\"muted\">(" + template.HTMLEscapeString(*r.UsuarioCodigo) + ")</span>"
|
||||||
}
|
}
|
||||||
inquilino := template.HTMLEscapeString(r.InquilinoNome) + " <span class=\"muted\">(" + template.HTMLEscapeString(r.InquilinoCodigo) + ")</span>"
|
|
||||||
emailHTML := "-"
|
|
||||||
emailHref := ""
|
|
||||||
if r.UsuarioEmail != nil && strings.TrimSpace(*r.UsuarioEmail) != "" {
|
|
||||||
emailHTML = linkEmail(*r.UsuarioEmail)
|
|
||||||
emailHref = "mailto:" + url.PathEscape(strings.TrimSpace(*r.UsuarioEmail))
|
|
||||||
}
|
|
||||||
telHref := ""
|
|
||||||
waHref := ""
|
|
||||||
telefoneHTML := "-"
|
|
||||||
if r.UsuarioTelefone != nil && strings.TrimSpace(*r.UsuarioTelefone) != "" {
|
|
||||||
telHref, waHref, telefoneHTML = linkTelefone(*r.UsuarioTelefone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ícones (inline SVG) — simples e sem dependências.
|
|
||||||
iconMail := `<svg class="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6h16v12H4V6Z" stroke="currentColor" stroke-width="2"/><path d="m4 7 8 6 8-6" stroke="currentColor" stroke-width="2"/></svg>`
|
|
||||||
iconPhone := `<svg class="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 3h4l2 6-3 2c1.5 3 4 5.5 7 7l2-3 6 2v4c0 1-1 2-2 2C11 23 1 13 2 5c0-1 1-2 2-2Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>`
|
|
||||||
iconWA := `<svg class="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M20 11.5c0 4.142-3.582 7.5-8 7.5-1.403 0-2.722-.339-3.87-.937L4 19l1.05-3.322A7.08 7.08 0 0 1 4 11.5C4 7.358 7.582 4 12 4s8 3.358 8 7.5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>`
|
|
||||||
|
|
||||||
acoes := ""
|
|
||||||
if emailHref != "" {
|
|
||||||
acoes += "<a class=\"iconbtn\" href=\"" + emailHref + "\" title=\"Enviar email\">" + iconMail + "</a>"
|
|
||||||
}
|
|
||||||
if waHref != "" {
|
|
||||||
acoes += "<a class=\"iconbtn\" href=\"" + waHref + "\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Abrir WhatsApp\">" + iconWA + "</a>"
|
|
||||||
}
|
|
||||||
if telHref != "" {
|
|
||||||
acoes += "<a class=\"iconbtn\" href=\"" + telHref + "\" title=\"Ligar\">" + iconPhone + "</a>"
|
|
||||||
}
|
|
||||||
if acoes == "" {
|
|
||||||
acoes = "-"
|
|
||||||
}
|
|
||||||
coment := ""
|
coment := ""
|
||||||
if r.Justificativa != nil {
|
if r.Justificativa != nil {
|
||||||
coment = template.HTMLEscapeString(*r.Justificativa)
|
coment = template.HTMLEscapeString(*r.Justificativa)
|
||||||
}
|
}
|
||||||
b.WriteString("<tr><td>" + template.HTMLEscapeString(data) + "</td><td><b>" + template.HTMLEscapeString(nota) + "</b></td><td>" + usuario + "</td><td>" + inquilino + "</td><td>" + emailHTML + "</td><td>" + telefoneHTML + "</td><td>" + coment + "</td><td>" + acoes + "</td></tr>")
|
b.WriteString("<tr><td>" + template.HTMLEscapeString(data) + "</td><td><b>" + template.HTMLEscapeString(nota) + "</b></td><td>" + usuario + "</td><td>" + coment + "</td></tr>")
|
||||||
}
|
}
|
||||||
b.WriteString("</tbody></table>")
|
b.WriteString("</tbody></table>")
|
||||||
|
|
||||||
|
|
@ -321,10 +226,6 @@ th,td{padding:8px;border-bottom:1px solid #eee;text-align:left;vertical-align:to
|
||||||
|
|
||||||
b.WriteString("</div>")
|
b.WriteString("</div>")
|
||||||
|
|
||||||
// JS do painel: apenas bootstrap para executar a lógica no WASM.
|
|
||||||
// Regras (.agent): sem dependências externas. A lógica fica no WASM.
|
|
||||||
b.WriteString(`<script src="/static/wasm_exec.js"></script><script src="/static/painel.js"></script>`)
|
|
||||||
|
|
||||||
b.WriteString("</body></html>")
|
b.WriteString("</body></html>")
|
||||||
w.Write([]byte(b.String()))
|
w.Write([]byte(b.String()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,12 +123,9 @@ SELECT
|
||||||
id,
|
id,
|
||||||
respondido_em,
|
respondido_em,
|
||||||
pedido_criado_em,
|
pedido_criado_em,
|
||||||
inquilino_codigo,
|
|
||||||
inquilino_nome,
|
|
||||||
usuario_codigo,
|
usuario_codigo,
|
||||||
usuario_nome,
|
usuario_nome,
|
||||||
usuario_email,
|
usuario_email,
|
||||||
usuario_telefone,
|
|
||||||
nota,
|
nota,
|
||||||
justificativa
|
justificativa
|
||||||
FROM %s
|
FROM %s
|
||||||
|
|
@ -149,12 +146,9 @@ LIMIT $1 OFFSET $2`, tabela, cond)
|
||||||
&r.ID,
|
&r.ID,
|
||||||
&r.RespondidoEm,
|
&r.RespondidoEm,
|
||||||
&r.PedidoCriadoEm,
|
&r.PedidoCriadoEm,
|
||||||
&r.InquilinoCodigo,
|
|
||||||
&r.InquilinoNome,
|
|
||||||
&r.UsuarioCodigo,
|
&r.UsuarioCodigo,
|
||||||
&r.UsuarioNome,
|
&r.UsuarioNome,
|
||||||
&r.UsuarioEmail,
|
&r.UsuarioEmail,
|
||||||
&r.UsuarioTelefone,
|
|
||||||
&r.Nota,
|
&r.Nota,
|
||||||
&r.Justificativa,
|
&r.Justificativa,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|
|
||||||
BIN
server
BIN
server
Binary file not shown.
Binary file not shown.
|
|
@ -1,49 +0,0 @@
|
||||||
(function(){
|
|
||||||
// Bootstrap mínimo do painel.
|
|
||||||
// Regra (.agent): a lógica fica no WASM; aqui apenas garantimos que o WASM
|
|
||||||
// esteja carregado/executando.
|
|
||||||
//
|
|
||||||
// Este arquivo existe para evitar JS inline no HTML do painel.
|
|
||||||
|
|
||||||
async function carregarWasmPainel(){
|
|
||||||
try{
|
|
||||||
if(window.__eli_nps_wasm_ready) return true;
|
|
||||||
if(window.__eli_nps_wasm_loading) return window.__eli_nps_wasm_loading;
|
|
||||||
|
|
||||||
window.__eli_nps_wasm_loading = (async function(){
|
|
||||||
try{
|
|
||||||
if(!window.Go){
|
|
||||||
// wasm_exec.js deve ter sido carregado pelo HTML.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const go = new Go();
|
|
||||||
const res = await fetch('/static/e-li.nps.wasm', {cache: 'no-cache'});
|
|
||||||
if(!res.ok) return false;
|
|
||||||
const bytes = await res.arrayBuffer();
|
|
||||||
const {instance} = await WebAssembly.instantiate(bytes, go.importObject);
|
|
||||||
go.run(instance);
|
|
||||||
return !!window.__eli_nps_wasm_ready;
|
|
||||||
}catch(e){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return window.__eli_nps_wasm_loading;
|
|
||||||
}catch(e){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
|
||||||
carregarWasmPainel().then(function(ok){
|
|
||||||
if(!ok) return;
|
|
||||||
// Executa o init do painel no WASM (best-effort).
|
|
||||||
try{
|
|
||||||
if(typeof window.__eli_nps_wasm_painel_init === 'function'){
|
|
||||||
window.__eli_nps_wasm_painel_init();
|
|
||||||
}
|
|
||||||
}catch(e){}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue