[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M714FFDM2FKJK5BW2E2VRNXV":3,"skill-same:01M714FFDM2FKJK5BW2E2VRNXV":88},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":34,"publishedAt":55,"linkOnly":56,"markdownContent":57,"markdownBody":58,"frontMatter":59,"refName":62,"commitSha":63,"skillDirectory":64,"forksCount":14,"repositoryHtmlUrl":65,"repositoryHomepageUrl":6,"ownerHtmlUrl":66,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":67,"install":68,"resources":81},"01M714FFDM2FKJK5BW2E2VRNXV","sankhya-dashboard-html-jsp-custom-best-pratices",null,"This skill should be used when the user asks for patterns, best practices, creation, or fixing of Sankhya dashboards using HTML, JSP, Java, and SQL.","sapsapshen\u002Fai-memory-router","sapsapshen","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F20347959?v=4","01M6XDQEF1DP50DKF37H81J3J5","shenmian","01M6XDQEJPE41Y3DT2763X8ZJN",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcopilot\u002Fsankhya-dashboard-html-jsp-custom-best-pratices\u002FSKILL.md",[18,23,26,29],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":27,"categoryName":28,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000013","分析与可视化",{"categoryNo":30,"categoryName":31,"taxonomyType":32,"taxonomyTypeDesc":33},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54],"dashboard","custom","sankhya dashboard html jsp custom best pratices","html","jsp","sankhya","2026","safe","when","for","creation","patterns","community","best-practices","user","dashboards","java","best practices","code","sql","2026-11-10 10:12:30",false,"---\nname: sankhya-dashboard-html-jsp-custom-best-pratices\ndescription: \"This skill should be used when the user asks for patterns, best practices, creation, or fixing of Sankhya dashboards using HTML, JSP, Java, and SQL.\"\ncategory: code\nrisk: safe\nsource: community\ntags: [sankhya, dashboard, jsp, html, sql, best-practices]\ndate_added: \"2026-03-10\"\n---\n\n# sankhya-dashboard-html-jsp-custom-best-pratices\n\n## Purpose\n\nTo provide a consolidated guide of patterns and best practices for creating and maintaining dashboards, SQL queries, BI parameterization, and UI\u002FUX within the Sankhya ecosystem (JSP\u002FHTML\u002FJava).\n\n## When to Use This Skill\n\nThis skill should be used when:\n- The user asks about \"boas praticas do sankhya\" or \"Sankhya best practices\".\n- The user mentions \"dashboard sankhya\" or is working on a Sankhya BI dashboard.\n- The user asks for anything related to the word \"Sankhya\".\n- The user wants to create or modify code files for Sankhya dashboards.\n\n## Core Capabilities\n\n1. **Code Generation & Review**: Apply JSP\u002FJSTL patterns and server-side organization to reduce compilation errors and rendering failures.\n2. **Visual Consistency**: Standardize visual identity in BI components using predefined CSS tokens.\n3. **Database Exploration**: Structure data exploration queries for performance and correct mapping of Sankhya entities.\n4. **BI Construction Guide**: Use the HTML5 component flow in BI to ensure correct rendering, reactivity, and navigation.\n\n## Patterns\n\n### Melhores Práticas de Código\nAplicar padrões de JSP\u002FJSTL e organização server-side para reduzir erros de compilação, falhas de renderização e regressões em dashboards\u002Ftelas.\n\n**Diretrizes de implementação**\n- Declarar diretivas JSP e taglibs obrigatórias no topo do arquivo.\n- Forçar `isELIgnored=\"false\"` para habilitar `${...}` em tempo de renderização.\n- Preferir `core_rt` para JSTL core no ecossistema Sankhya.\n- Evitar scriptlets Java em JSP; usar JSTL (`c:if`, `c:choose`, `c:forEach`).\n- Modularizar lógica de negócio (camadas\u002Fserviços), evitando acoplamento em arquivo único.\n- Evitar hardcode de credenciais, URLs sensíveis e tokens.\n- Modelar estado global da UI (dados, filtros, ordenação, aba ativa) e resetar estado antes de novo carregamento.\n- Persistir preferências de visualização no `localStorage` (ordem de colunas e ordenação).\n- Implementar carregamento sob demanda para abas\u002Fmodais pesados (lazy-load) para reduzir tempo inicial.\n- **Blindagem de Parâmetros**: Sempre definir um valor padrão (fallback) para parâmetros de URL via `c:set` para evitar Erro 500 no servidor Java do Sankhya.\n- **Separação de Camadas (JSP vs JS)**: Evitar injetar tags JSP diretamente dentro de blocos `\u003Cscript>`. Utilizar containers HTML ocultos para passar dados ao JavaScript, mantendo a saúde do editor de código (IDE Linting).\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```jsp\n\u003C%@ page language=\"java\" contentType=\"text\u002Fhtml; charset=UTF-8\" pageEncoding=\"UTF-8\" isELIgnored=\"false\" %>\n\u003C%@ taglib prefix=\"snk\" uri=\"\u002FWEB-INF\u002Ftld\u002FsankhyaUtil.tld\" %>\n\u003C%@ taglib uri=\"http:\u002F\u002Fjava.sun.com\u002Fjstl\u002Fcore_rt\" prefix=\"c\" %>\n\u003C%@ taglib uri=\"http:\u002F\u002Fjava.sun.com\u002Fjsp\u002Fjstl\u002Ffunctions\" prefix=\"fn\" %>\n\u003Csnk:load \u002F>\n```\n\n**Carregamento de assets em dashboard\u002Fgadget**\n- Referenciar arquivos com `contextPath` + `BASE_FOLDER`.\n- Em níveis secundários (`openLevel`), manter caminho absoluto para evitar quebra de resolução.\n\n```html\n\u003Cscript src=\"${pageContext.request.contextPath}\u002F${BASE_FOLDER}\u002Fjs\u002Fapp.js\">\u003C\u002Fscript>\n\u003Clink rel=\"stylesheet\" href=\"${pageContext.request.contextPath}\u002F${BASE_FOLDER}\u002Fcss\u002Fstyle.css\" \u002F>\n```\n\n**Consumo seguro de `snk:query`**\n- Iterar em `query.rows` (não no objeto raiz).\n- Testar vazio com `empty query.rows`.\n\n```jsp\n\u003Csnk:query var=\"qDados\">\n    SELECT CAB.NUNOTA, CAB.CODPARC\n      FROM TGFCAB CAB\n\u003C\u002Fsnk:query>\n\n\u003Cc:choose>\n    \u003Cc:when test=\"${empty qDados.rows}\">\n        \u003Cspan>Sem resultados\u003C\u002Fspan>\n    \u003C\u002Fc:when>\n    \u003Cc:otherwise>\n        \u003Cc:forEach var=\"linha\" items=\"${qDados.rows}\">\n            ${linha.NUNOTA}\n        \u003C\u002Fc:forEach>\n    \u003C\u002Fc:otherwise>\n\u003C\u002Fc:choose>\n```\n\n**Sanitização de parâmetros antes da SQL**\n- Normalizar valor de entrada.\n- Remover aspas (`\"` e `&quot;`) antes de injetar em query.\n- Definir fallback seguro para evitar SQL inválida.\n\n```jsp\n\u003Cc:set var=\"raw_codusu\" value=\"${empty param.P_CODUSU ? '0' : param.P_CODUSU}\" \u002F>\n\u003Cc:set var=\"codusu_limpo\" value=\"${fn:replace(raw_codusu, '\\\"', '')}\" \u002F>\n\u003Cc:set var=\"codusu_limpo\" value=\"${fn:replace(codusu_limpo, '&quot;', '')}\" \u002F>\n\u003Cc:set var=\"codusu_seguro\" value=\"${empty codusu_limpo ? '0' : codusu_limpo}\" \u002F>\n\n\u003Csnk:query var=\"qAcessos\">\n    SELECT CODUSU, NOMEUSU\n      FROM TSIUSU\n     WHERE CODUSU = :codusu_seguro\n\u003C\u002Fsnk:query>\n```\n\n**Estado de tela e lazy-load em dashboard único**\n- Definir listas globais para reutilização em KPI, gráfico, tabela e modais.\n- Guardar flag de carregamento por aba para evitar reconsultas desnecessárias.\n- Recarregar dados e reabrir o contexto (produto\u002Faba) após atualização transacional.\n\n```js\nvar dadosGlobais = [];\nvar produtoAtual = null;\nvar abaCarregada = {};\n\nfunction abrirDetalhe(dado) {\n  produtoAtual = dado;\n  abaCarregada = {};\n  trocarAba(\"estoque\");\n}\n\nfunction trocarAba(aba) {\n  if (aba === \"estoque\" && !abaCarregada.estoque) carregarAbaEstoque(produtoAtual.CODPROD);\n  if (aba === \"pedidos\" && !abaCarregada.pedidos) carregarAbaPedidos(produtoAtual.CODPROD);\n  if (aba === \"parceiros\" && !abaCarregada.parceiros) carregarAbaParceiros(produtoAtual.CODPROD);\n}\n```\n**Exemplo de Blindagem e Separação de Camadas**\n\n```jsp\n\u003C%-- 1. Blindagem no topo do arquivo --%>\n\u003Cc:set var=\"v_salesagent\" value=\"${empty param.SALESAGENT ? '0' : param.SALESAGENT}\" \u002F>\n\n\u003C%-- 2. Container oculto para dados (Separação JSP vs JS) --%>\n\u003Cdiv id=\"data-container\" style=\"display:none;\">\n    [\n    \u003Cc:forEach var=\"row\" items=\"${qDados.rows}\" varStatus=\"loop\">\n        { \"id\": ${row.ID}, \"nome\": \"${fn:replace(row.NOME, '\"', '\\\\\"')}\" }${!loop.last ? ',' : ''}\n    \u003C\u002Fc:forEach>\n    ]\n\u003C\u002Fdiv>\n\n\u003Cscript>\n    \u002F\u002F 3. JS apenas lê os dados do container\n    const rawData = document.getElementById('data-container').textContent.trim();\n    const myData = rawData ? JSON.parse(rawData) : [];\n\u003C\u002Fscript>\n```\n\n### Identidade Visual (Colors)\nPadronizar identidade visual em componentes BI para consistência entre gadgets HTML5, tabelas e indicadores.\n\n**Diretrizes de UI\u002FUX**\n- Definir paleta via tokens (`--color-*`) para evitar valores espalhados.\n- Priorizar contraste mínimo entre texto\u002Ffundo (legibilidade operacional).\n- Manter semântica visual consistente: sucesso, alerta, erro, neutro.\n- Permitir sobrescrita por dados vindos do SQL (`BKCOLOR`, `FGCOLOR`) quando necessário.\n- Usar cabeçalho sticky e colunas fixas para tabelas largas com alto volume de leitura.\n- Diferenciar status de linha via classes CSS (aprovado, parcial, histórico, crítico) para leitura operacional rápida.\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```html\n\u003Cstyle>\n  :root {\n    --color-bg: #F5F7FA;\n    --color-surface: #FFFFFF;\n    --color-text: #1F2937;\n    --color-success: #1A7F37;\n    --color-warning: #B26A00;\n    --color-danger: #B42318;\n    --color-accent: #0E5A8A;\n  }\n\n  .card {\n    background: var(--color-surface);\n    color: var(--color-text);\n    border-radius: 8px;\n    padding: 12px;\n  }\n\u003C\u002Fstyle>\n```\n\n```sql\nSELECT\n    V.CODMETA,\n    V.VALOR_ATUAL,\n    V.VALOR_META,\n    CASE WHEN V.VALOR_ATUAL >= V.VALOR_META THEN '#1A7F37' ELSE '#B42318' END AS BKCOLOR,\n    '#FFFFFF' AS FGCOLOR\nFROM AD_DADOS_VENDA V\n```\n\n```html\n\u003Cstyle>\n  #tblDados thead th { position: sticky; top: 0; z-index: 4; }\n  #tblDados .col-fixa-1 { position: sticky; left: 0; z-index: 3; }\n  #tblDados .col-fixa-2 { position: sticky; left: var(--fix-col-1-width); z-index: 2; }\n  .row-aprovacao td { background: #ffe8cc; color: #7a3a00; }\n  .row-parcial td { background: #fff4c4; color: #5e4c00; }\n\u003C\u002Fstyle>\n```\n\n### Consultas e Exploração de Banco\nEstruturar exploração de dados com foco em performance, legibilidade e mapeamento correto de entidades Sankhya.\n\n**Boas práticas de exploração (DBExplorer)**\n- Usar DBExplorer para inspeção de tabelas, campos, índices, views e procedures.\n- Respeitar limite de retorno configurado (ex.: `DBEXPMAXROW`) para evitar carga excessiva.\n- Evitar `SELECT *` em tabelas com campos volumosos (BLOB\u002FCLOB).\n\n**Mapas essenciais do ecossistema**\n- Dicionário: `TDDTAB`, `TDDCAM`, `TDDOPC`, `TDDINS`, `TDDLIG`.\n- Comercial\u002Ffinanceiro: `TGFCAB`, `TGFITE`, `TGFTOP`, `TGFPAR`, `TGFPRO`, `TGFEST`, `TGFVAR`.\n- Segurança\u002Facesso: `TSIUSU`, `TSIGRU`, `TSIACI`, `TSIIMP`.\n\n**Padrões de SQL recomendados**\n- Em TOP versionada, relacionar `CODTIPOPER` + data de alteração (`DHTIPOPER`\u002F`DHALTER`).\n- Em filtros opcionais, usar padrão `(... = :P_PARAM OR :P_PARAM IS NULL)`.\n- Parametrizar sempre (evitar literals de usuário).\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```sql\nSELECT\n    CAB.NUNOTA,\n    CAB.CODPARC,\n    CAB.DTNEG,\n    ITE.SEQUENCIA,\n    ITE.CODPROD,\n    (ITE.VLRTOT - ITE.VLRDESC) AS VLR_LIQUIDO\nFROM TGFCAB CAB\nJOIN TGFITE ITE\n  ON ITE.NUNOTA = CAB.NUNOTA\nJOIN TGFTOP TOP\n  ON TOP.CODTIPOPER = CAB.CODTIPOPER\n AND TOP.DHALTER   = CAB.DHTIPOPER\nWHERE (CAB.CODPARC = :P_CODPARC OR :P_CODPARC IS NULL)\n  AND (CAB.CODVEND = :P_CODVEND OR :P_CODVEND IS NULL)\n```\n\n```sql\nSELECT\n    U.CODUSU,\n    U.NOMEUSU,\n    G.NOMEGRUPO,\n    A.CODREL,\n    I.NOME AS DESCRICAO_RECURSO,\n    A.CONS,\n    A.ALTERA\nFROM TSIUSU U\nJOIN TSIGRU G ON G.CODGRUPO = U.CODGRUPO\nJOIN TSIACI A ON A.CODGRUPO = U.CODGRUPO\nJOIN TSIIMP I ON I.CODREL = A.CODREL\nWHERE U.CODUSU = :P_CODUSU\nORDER BY I.NOME\n```\n\n### Guia do Construtor de BI\nAplicar fluxo de desenvolvimento de componentes HTML5 no BI para garantir renderização, reatividade e navegação entre níveis.\n\n**Estrutura e publicação**\n- Empacotar componente em `.zip` com `index.html` como entrada principal.\n- Organizar recursos estáticos em `assets\u002F` (CSS, JS, libs, imagens).\n- Usar XML\u002Fdesign conforme necessidade; considerar JSP de entrada quando houver pré-processamento server-side.\n\n**Fluxo de dados e parâmetros**\n- Definir variáveis SQL ou BeanShell conforme complexidade.\n- Usar prefixos de tradução de parâmetro:\n  - `:` para bind padrão.\n  - `:#` para substituição literal (avaliar com cautela e validação).\n  - `:@` para literal textual em cenários como `LIKE`.\n- Em parâmetros multi-list extensos, usar `\u002F*inCollection*\u002F`.\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```sql\nSELECT\n    C.CODCID,\n    C.NOMECID,\n    C.UF\nFROM AD_TABELA_EXEMPLO C\nWHERE \u002F*inCollection*\u002F C.CODCID IN :P_CODCID \u002F*inCollection*\u002F\n```\n\n**Reatividade e ciclo de vida**\n- Programar re-render quando filtros globais mudarem.\n- Evitar dependência exclusiva de `DOMContentLoaded` em conteúdo injetado.\n- Aplicar inicialização assíncrona para garantir elementos disponíveis.\n\n```html\n\u003Cscript>\n  function renderizarComponente(dados) {\n    \u002F\u002F Atualizar DOM, gráficos e KPIs com os dados recebidos\n  }\n\n  function iniciar() {\n    const dadosIniciais = window.snkBIData || [];\n    renderizarComponente(dadosIniciais);\n  }\n\n  setTimeout(iniciar, 300);\n\u003C\u002Fscript>\n```\n\n**Drill-down e eventos**\n- Modelar níveis independentes (macro → micro) com argumentos explícitos.\n- Evitar contêiner vazio em níveis subsequentes.\n- Usar herança de contexto entre níveis para preservar filtros e navegação.\n- Implementar ações de clique para atualizar detalhes e abrir telas nativas com chave de contexto.\n\n**Navegação multi-nível (openLevel e contrato de contexto)**\n- Definir constantes de nível em configuração (`NIVEL_RESUMO`, `NIVEL_DETALHE`, `NIVEL_ITEM`) para evitar acoplamento em string solta.\n- Encapsular `openLevel` em funções dedicadas por rota de navegação (ex.: abrir detalhe por vendedor, abrir itens por parceiro).\n- Repassar parâmetros de contexto entre níveis com contrato explícito (`ARG_*` para chaves e `P_*` para filtros\u002Fperíodo).\n- Validar disponibilidade de `openLevel` e parâmetros obrigatórios antes de navegar.\n- Aplicar fallback de erro no console\u002FUI quando o contexto não permitir abertura de nível.\n\n```js\nvar cfg = window.DASH_CONFIG || {};\nvar NIVEL_DETALHE = cfg.NIVEL_DETALHE || \"NIVEL_B\";\nvar NIVEL_ITEM = cfg.NIVEL_ITEM || \"NIVEL_C\";\n\nfunction abrirNivelDetalhe(codigoEntidade) {\n  if (!codigoEntidade || typeof openLevel !== \"function\") return;\n  openLevel(NIVEL_DETALHE, {\n    ARG_CODENT: parseInt(codigoEntidade, 10),\n    P_PERIODO_INI: cfg.P_PERIODO_INI || \"\",\n    P_PERIODO_FIN: cfg.P_PERIODO_FIN || \"\",\n    P_CODMETA: cfg.P_CODMETA || \"\"\n  });\n}\n\nfunction abrirNivelItem(codigoEntidadeFilha) {\n  if (!codigoEntidadeFilha || typeof openLevel !== \"function\") return;\n  openLevel(NIVEL_ITEM, {\n    ARG_CODENT_FILHA: parseInt(codigoEntidadeFilha, 10),\n    P_PERIODO_INI: cfg.P_PERIODO_INI || \"\",\n    P_PERIODO_FIN: cfg.P_PERIODO_FIN || \"\",\n    P_CODMETA: cfg.P_CODMETA || \"\"\n  });\n}\n```\n\n**Segurança e bloqueio de acesso por escopo**\n- Restringir qualquer consulta de nível pela relação usuário-meta\u002Fescopo antes de agregar dados.\n- Centralizar o predicado de segurança em função de montagem de `WHERE` para reaproveitamento em KPIs, grids e gráficos.\n- Preferir variáveis de sessão (`CODUSU_LOG` ou função equivalente de usuário logado) para evitar spoof de parâmetro de usuário.\n- Bloquear carga quando parâmetros críticos estiverem ausentes (ex.: período, meta, entidade de drill-down).\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```sql\nSELECT\n    M.CODMETA,\n    M.CODENTIDADE,\n    SUM(M.VLRPREV) AS VLR_PREV,\n    SUM(M.VLRREAL) AS VLR_REAL\nFROM AD_DADOS_META M\nWHERE M.CODMETA = :P_CODMETA\n  AND M.DTREF BETWEEN TO_DATE(:P_PERIODO_INI, 'DD\u002FMM\u002FYYYY')\n                  AND TO_DATE(:P_PERIODO_FIN, 'DD\u002FMM\u002FYYYY')\n  AND EXISTS (\n      SELECT 1\n      FROM AD_META_USUARIO_LIB L\n      WHERE L.CODMETA = M.CODMETA\n        AND L.CODUSU = STP_GET_CODUSULOGADO\n  )\nGROUP BY M.CODMETA, M.CODENTIDADE\n```\n\n**Grid hierárquica com expansão\u002Fcolapso**\n- Estruturar mapa `filhosPorPai` e estado `nosExpandidos` para renderização incremental da árvore.\n- Inicializar nós não analíticos de níveis superiores como expandidos para melhorar leitura inicial.\n- Em nós colapsados, exibir agregados de descendentes analíticos para manter contexto sem abrir toda árvore.\n- Fornecer ações rápidas de “Expandir tudo” e “Recolher tudo” no cabeçalho.\n- Em filtros de texto, incluir ancestrais dos nós encontrados para preservar rastreabilidade hierárquica.\n\n```js\nvar filhosPorPai = {};\nvar nosExpandidos = {};\n\nfunction alternarNo(codNo) {\n  var id = String(codNo);\n  nosExpandidos[id] = !nosExpandidos[id];\n  renderizarGrid();\n}\n\nfunction obterVisiveis(raiz) {\n  var lista = [];\n  function visitar(pai) {\n    (filhosPorPai[pai] || []).forEach(function (no) {\n      lista.push(no);\n      if (nosExpandidos[String(no.CODNO)]) visitar(String(no.CODNO));\n    });\n  }\n  visitar(String(raiz || \"\"));\n  return lista;\n}\n```\n\n**Resiliência de carregamento**\n- Separar a carga principal da carga complementar (ex.: realizado mensal) e não bloquear a visualização principal por falha secundária.\n- Tratar ausência de dados por componente (`vazio`) sem derrubar o layout inteiro.\n- Destruir instâncias de gráfico antes de recriar para evitar vazamento e sobreposição visual.\n- Carregar painéis secundários somente ao abrir aba\u002Fvisão correspondente (on-demand).\n\n**Navegação intra-nível (single JSP)**\n- Tratar o JSP único como shell de navegação: tabela principal + modal de detalhe + abas internas + modais auxiliares.\n- Encadear cliques sem trocar de nível Sankhya: KPI → lista modal, gráfico → filtro de tabela, linha da tabela → detalhe.\n- Aplicar atalhos de ação no detalhe para abrir cadastro nativo no contexto da chave primária.\n- Fechar modal por clique no overlay para reduzir atrito de uso.\n\n```js\nfunction abrirTelaNativa(resourceIdBase64, pkObj) {\n  var pk = btoa(JSON.stringify(pkObj));\n  top.location.href = \"\u002Fmge\u002Fsystem.jsp#app\u002F\" + resourceIdBase64 + \"\u002F\" + pk + \"&pk-refresh=\" + Date.now();\n}\n\nfunction onKpiClick(lista) {\n  abrirModalLista(\"Itens selecionados\", \"Navegação por atalho\", lista);\n}\n\nfunction onGraficoClick(grupo) {\n  filtrarTabelaPorGrupo(grupo);\n}\n```\n\n**Feedback operacional de interface**\n- Exibir estados explícitos de carregamento, vazio e erro em cada painel.\n- Em ações de atualização, desabilitar botão de confirmação até o retorno do `executeQuery`.\n- Após sucesso, recarregar dados e restaurar contexto anterior (produto e aba ativa).\n\n**Variáveis internas de segurança**\n- Aproveitar variáveis de sessão para segurança em nível de linha (`CODUSU_LOG`, `CODGRU_LOG`, `CODVEN_LOG`).\n- Restringir dados por contexto do usuário antes de montar visualizações.\n","# sankhya-dashboard-html-jsp-custom-best-pratices\n\n## Purpose\n\nTo provide a consolidated guide of patterns and best practices for creating and maintaining dashboards, SQL queries, BI parameterization, and UI\u002FUX within the Sankhya ecosystem (JSP\u002FHTML\u002FJava).\n\n## When to Use This Skill\n\nThis skill should be used when:\n- The user asks about \"boas praticas do sankhya\" or \"Sankhya best practices\".\n- The user mentions \"dashboard sankhya\" or is working on a Sankhya BI dashboard.\n- The user asks for anything related to the word \"Sankhya\".\n- The user wants to create or modify code files for Sankhya dashboards.\n\n## Core Capabilities\n\n1. **Code Generation & Review**: Apply JSP\u002FJSTL patterns and server-side organization to reduce compilation errors and rendering failures.\n2. **Visual Consistency**: Standardize visual identity in BI components using predefined CSS tokens.\n3. **Database Exploration**: Structure data exploration queries for performance and correct mapping of Sankhya entities.\n4. **BI Construction Guide**: Use the HTML5 component flow in BI to ensure correct rendering, reactivity, and navigation.\n\n## Patterns\n\n### Melhores Práticas de Código\nAplicar padrões de JSP\u002FJSTL e organização server-side para reduzir erros de compilação, falhas de renderização e regressões em dashboards\u002Ftelas.\n\n**Diretrizes de implementação**\n- Declarar diretivas JSP e taglibs obrigatórias no topo do arquivo.\n- Forçar `isELIgnored=\"false\"` para habilitar `${...}` em tempo de renderização.\n- Preferir `core_rt` para JSTL core no ecossistema Sankhya.\n- Evitar scriptlets Java em JSP; usar JSTL (`c:if`, `c:choose`, `c:forEach`).\n- Modularizar lógica de negócio (camadas\u002Fserviços), evitando acoplamento em arquivo único.\n- Evitar hardcode de credenciais, URLs sensíveis e tokens.\n- Modelar estado global da UI (dados, filtros, ordenação, aba ativa) e resetar estado antes de novo carregamento.\n- Persistir preferências de visualização no `localStorage` (ordem de colunas e ordenação).\n- Implementar carregamento sob demanda para abas\u002Fmodais pesados (lazy-load) para reduzir tempo inicial.\n- **Blindagem de Parâmetros**: Sempre definir um valor padrão (fallback) para parâmetros de URL via `c:set` para evitar Erro 500 no servidor Java do Sankhya.\n- **Separação de Camadas (JSP vs JS)**: Evitar injetar tags JSP diretamente dentro de blocos `\u003Cscript>`. Utilizar containers HTML ocultos para passar dados ao JavaScript, mantendo a saúde do editor de código (IDE Linting).\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```jsp\n\u003C%@ page language=\"java\" contentType=\"text\u002Fhtml; charset=UTF-8\" pageEncoding=\"UTF-8\" isELIgnored=\"false\" %>\n\u003C%@ taglib prefix=\"snk\" uri=\"\u002FWEB-INF\u002Ftld\u002FsankhyaUtil.tld\" %>\n\u003C%@ taglib uri=\"http:\u002F\u002Fjava.sun.com\u002Fjstl\u002Fcore_rt\" prefix=\"c\" %>\n\u003C%@ taglib uri=\"http:\u002F\u002Fjava.sun.com\u002Fjsp\u002Fjstl\u002Ffunctions\" prefix=\"fn\" %>\n\u003Csnk:load \u002F>\n```\n\n**Carregamento de assets em dashboard\u002Fgadget**\n- Referenciar arquivos com `contextPath` + `BASE_FOLDER`.\n- Em níveis secundários (`openLevel`), manter caminho absoluto para evitar quebra de resolução.\n\n```html\n\u003Cscript src=\"${pageContext.request.contextPath}\u002F${BASE_FOLDER}\u002Fjs\u002Fapp.js\">\u003C\u002Fscript>\n\u003Clink rel=\"stylesheet\" href=\"${pageContext.request.contextPath}\u002F${BASE_FOLDER}\u002Fcss\u002Fstyle.css\" \u002F>\n```\n\n**Consumo seguro de `snk:query`**\n- Iterar em `query.rows` (não no objeto raiz).\n- Testar vazio com `empty query.rows`.\n\n```jsp\n\u003Csnk:query var=\"qDados\">\n    SELECT CAB.NUNOTA, CAB.CODPARC\n      FROM TGFCAB CAB\n\u003C\u002Fsnk:query>\n\n\u003Cc:choose>\n    \u003Cc:when test=\"${empty qDados.rows}\">\n        \u003Cspan>Sem resultados\u003C\u002Fspan>\n    \u003C\u002Fc:when>\n    \u003Cc:otherwise>\n        \u003Cc:forEach var=\"linha\" items=\"${qDados.rows}\">\n            ${linha.NUNOTA}\n        \u003C\u002Fc:forEach>\n    \u003C\u002Fc:otherwise>\n\u003C\u002Fc:choose>\n```\n\n**Sanitização de parâmetros antes da SQL**\n- Normalizar valor de entrada.\n- Remover aspas (`\"` e `&quot;`) antes de injetar em query.\n- Definir fallback seguro para evitar SQL inválida.\n\n```jsp\n\u003Cc:set var=\"raw_codusu\" value=\"${empty param.P_CODUSU ? '0' : param.P_CODUSU}\" \u002F>\n\u003Cc:set var=\"codusu_limpo\" value=\"${fn:replace(raw_codusu, '\\\"', '')}\" \u002F>\n\u003Cc:set var=\"codusu_limpo\" value=\"${fn:replace(codusu_limpo, '&quot;', '')}\" \u002F>\n\u003Cc:set var=\"codusu_seguro\" value=\"${empty codusu_limpo ? '0' : codusu_limpo}\" \u002F>\n\n\u003Csnk:query var=\"qAcessos\">\n    SELECT CODUSU, NOMEUSU\n      FROM TSIUSU\n     WHERE CODUSU = :codusu_seguro\n\u003C\u002Fsnk:query>\n```\n\n**Estado de tela e lazy-load em dashboard único**\n- Definir listas globais para reutilização em KPI, gráfico, tabela e modais.\n- Guardar flag de carregamento por aba para evitar reconsultas desnecessárias.\n- Recarregar dados e reabrir o contexto (produto\u002Faba) após atualização transacional.\n\n```js\nvar dadosGlobais = [];\nvar produtoAtual = null;\nvar abaCarregada = {};\n\nfunction abrirDetalhe(dado) {\n  produtoAtual = dado;\n  abaCarregada = {};\n  trocarAba(\"estoque\");\n}\n\nfunction trocarAba(aba) {\n  if (aba === \"estoque\" && !abaCarregada.estoque) carregarAbaEstoque(produtoAtual.CODPROD);\n  if (aba === \"pedidos\" && !abaCarregada.pedidos) carregarAbaPedidos(produtoAtual.CODPROD);\n  if (aba === \"parceiros\" && !abaCarregada.parceiros) carregarAbaParceiros(produtoAtual.CODPROD);\n}\n```\n**Exemplo de Blindagem e Separação de Camadas**\n\n```jsp\n\u003C%-- 1. Blindagem no topo do arquivo --%>\n\u003Cc:set var=\"v_salesagent\" value=\"${empty param.SALESAGENT ? '0' : param.SALESAGENT}\" \u002F>\n\n\u003C%-- 2. Container oculto para dados (Separação JSP vs JS) --%>\n\u003Cdiv id=\"data-container\" style=\"display:none;\">\n    [\n    \u003Cc:forEach var=\"row\" items=\"${qDados.rows}\" varStatus=\"loop\">\n        { \"id\": ${row.ID}, \"nome\": \"${fn:replace(row.NOME, '\"', '\\\\\"')}\" }${!loop.last ? ',' : ''}\n    \u003C\u002Fc:forEach>\n    ]\n\u003C\u002Fdiv>\n\n\u003Cscript>\n    \u002F\u002F 3. JS apenas lê os dados do container\n    const rawData = document.getElementById('data-container').textContent.trim();\n    const myData = rawData ? JSON.parse(rawData) : [];\n\u003C\u002Fscript>\n```\n\n### Identidade Visual (Colors)\nPadronizar identidade visual em componentes BI para consistência entre gadgets HTML5, tabelas e indicadores.\n\n**Diretrizes de UI\u002FUX**\n- Definir paleta via tokens (`--color-*`) para evitar valores espalhados.\n- Priorizar contraste mínimo entre texto\u002Ffundo (legibilidade operacional).\n- Manter semântica visual consistente: sucesso, alerta, erro, neutro.\n- Permitir sobrescrita por dados vindos do SQL (`BKCOLOR`, `FGCOLOR`) quando necessário.\n- Usar cabeçalho sticky e colunas fixas para tabelas largas com alto volume de leitura.\n- Diferenciar status de linha via classes CSS (aprovado, parcial, histórico, crítico) para leitura operacional rápida.\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```html\n\u003Cstyle>\n  :root {\n    --color-bg: #F5F7FA;\n    --color-surface: #FFFFFF;\n    --color-text: #1F2937;\n    --color-success: #1A7F37;\n    --color-warning: #B26A00;\n    --color-danger: #B42318;\n    --color-accent: #0E5A8A;\n  }\n\n  .card {\n    background: var(--color-surface);\n    color: var(--color-text);\n    border-radius: 8px;\n    padding: 12px;\n  }\n\u003C\u002Fstyle>\n```\n\n```sql\nSELECT\n    V.CODMETA,\n    V.VALOR_ATUAL,\n    V.VALOR_META,\n    CASE WHEN V.VALOR_ATUAL >= V.VALOR_META THEN '#1A7F37' ELSE '#B42318' END AS BKCOLOR,\n    '#FFFFFF' AS FGCOLOR\nFROM AD_DADOS_VENDA V\n```\n\n```html\n\u003Cstyle>\n  #tblDados thead th { position: sticky; top: 0; z-index: 4; }\n  #tblDados .col-fixa-1 { position: sticky; left: 0; z-index: 3; }\n  #tblDados .col-fixa-2 { position: sticky; left: var(--fix-col-1-width); z-index: 2; }\n  .row-aprovacao td { background: #ffe8cc; color: #7a3a00; }\n  .row-parcial td { background: #fff4c4; color: #5e4c00; }\n\u003C\u002Fstyle>\n```\n\n### Consultas e Exploração de Banco\nEstruturar exploração de dados com foco em performance, legibilidade e mapeamento correto de entidades Sankhya.\n\n**Boas práticas de exploração (DBExplorer)**\n- Usar DBExplorer para inspeção de tabelas, campos, índices, views e procedures.\n- Respeitar limite de retorno configurado (ex.: `DBEXPMAXROW`) para evitar carga excessiva.\n- Evitar `SELECT *` em tabelas com campos volumosos (BLOB\u002FCLOB).\n\n**Mapas essenciais do ecossistema**\n- Dicionário: `TDDTAB`, `TDDCAM`, `TDDOPC`, `TDDINS`, `TDDLIG`.\n- Comercial\u002Ffinanceiro: `TGFCAB`, `TGFITE`, `TGFTOP`, `TGFPAR`, `TGFPRO`, `TGFEST`, `TGFVAR`.\n- Segurança\u002Facesso: `TSIUSU`, `TSIGRU`, `TSIACI`, `TSIIMP`.\n\n**Padrões de SQL recomendados**\n- Em TOP versionada, relacionar `CODTIPOPER` + data de alteração (`DHTIPOPER`\u002F`DHALTER`).\n- Em filtros opcionais, usar padrão `(... = :P_PARAM OR :P_PARAM IS NULL)`.\n- Parametrizar sempre (evitar literals de usuário).\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```sql\nSELECT\n    CAB.NUNOTA,\n    CAB.CODPARC,\n    CAB.DTNEG,\n    ITE.SEQUENCIA,\n    ITE.CODPROD,\n    (ITE.VLRTOT - ITE.VLRDESC) AS VLR_LIQUIDO\nFROM TGFCAB CAB\nJOIN TGFITE ITE\n  ON ITE.NUNOTA = CAB.NUNOTA\nJOIN TGFTOP TOP\n  ON TOP.CODTIPOPER = CAB.CODTIPOPER\n AND TOP.DHALTER   = CAB.DHTIPOPER\nWHERE (CAB.CODPARC = :P_CODPARC OR :P_CODPARC IS NULL)\n  AND (CAB.CODVEND = :P_CODVEND OR :P_CODVEND IS NULL)\n```\n\n```sql\nSELECT\n    U.CODUSU,\n    U.NOMEUSU,\n    G.NOMEGRUPO,\n    A.CODREL,\n    I.NOME AS DESCRICAO_RECURSO,\n    A.CONS,\n    A.ALTERA\nFROM TSIUSU U\nJOIN TSIGRU G ON G.CODGRUPO = U.CODGRUPO\nJOIN TSIACI A ON A.CODGRUPO = U.CODGRUPO\nJOIN TSIIMP I ON I.CODREL = A.CODREL\nWHERE U.CODUSU = :P_CODUSU\nORDER BY I.NOME\n```\n\n### Guia do Construtor de BI\nAplicar fluxo de desenvolvimento de componentes HTML5 no BI para garantir renderização, reatividade e navegação entre níveis.\n\n**Estrutura e publicação**\n- Empacotar componente em `.zip` com `index.html` como entrada principal.\n- Organizar recursos estáticos em `assets\u002F` (CSS, JS, libs, imagens).\n- Usar XML\u002Fdesign conforme necessidade; considerar JSP de entrada quando houver pré-processamento server-side.\n\n**Fluxo de dados e parâmetros**\n- Definir variáveis SQL ou BeanShell conforme complexidade.\n- Usar prefixos de tradução de parâmetro:\n  - `:` para bind padrão.\n  - `:#` para substituição literal (avaliar com cautela e validação).\n  - `:@` para literal textual em cenários como `LIKE`.\n- Em parâmetros multi-list extensos, usar `\u002F*inCollection*\u002F`.\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```sql\nSELECT\n    C.CODCID,\n    C.NOMECID,\n    C.UF\nFROM AD_TABELA_EXEMPLO C\nWHERE \u002F*inCollection*\u002F C.CODCID IN :P_CODCID \u002F*inCollection*\u002F\n```\n\n**Reatividade e ciclo de vida**\n- Programar re-render quando filtros globais mudarem.\n- Evitar dependência exclusiva de `DOMContentLoaded` em conteúdo injetado.\n- Aplicar inicialização assíncrona para garantir elementos disponíveis.\n\n```html\n\u003Cscript>\n  function renderizarComponente(dados) {\n    \u002F\u002F Atualizar DOM, gráficos e KPIs com os dados recebidos\n  }\n\n  function iniciar() {\n    const dadosIniciais = window.snkBIData || [];\n    renderizarComponente(dadosIniciais);\n  }\n\n  setTimeout(iniciar, 300);\n\u003C\u002Fscript>\n```\n\n**Drill-down e eventos**\n- Modelar níveis independentes (macro → micro) com argumentos explícitos.\n- Evitar contêiner vazio em níveis subsequentes.\n- Usar herança de contexto entre níveis para preservar filtros e navegação.\n- Implementar ações de clique para atualizar detalhes e abrir telas nativas com chave de contexto.\n\n**Navegação multi-nível (openLevel e contrato de contexto)**\n- Definir constantes de nível em configuração (`NIVEL_RESUMO`, `NIVEL_DETALHE`, `NIVEL_ITEM`) para evitar acoplamento em string solta.\n- Encapsular `openLevel` em funções dedicadas por rota de navegação (ex.: abrir detalhe por vendedor, abrir itens por parceiro).\n- Repassar parâmetros de contexto entre níveis com contrato explícito (`ARG_*` para chaves e `P_*` para filtros\u002Fperíodo).\n- Validar disponibilidade de `openLevel` e parâmetros obrigatórios antes de navegar.\n- Aplicar fallback de erro no console\u002FUI quando o contexto não permitir abertura de nível.\n\n```js\nvar cfg = window.DASH_CONFIG || {};\nvar NIVEL_DETALHE = cfg.NIVEL_DETALHE || \"NIVEL_B\";\nvar NIVEL_ITEM = cfg.NIVEL_ITEM || \"NIVEL_C\";\n\nfunction abrirNivelDetalhe(codigoEntidade) {\n  if (!codigoEntidade || typeof openLevel !== \"function\") return;\n  openLevel(NIVEL_DETALHE, {\n    ARG_CODENT: parseInt(codigoEntidade, 10),\n    P_PERIODO_INI: cfg.P_PERIODO_INI || \"\",\n    P_PERIODO_FIN: cfg.P_PERIODO_FIN || \"\",\n    P_CODMETA: cfg.P_CODMETA || \"\"\n  });\n}\n\nfunction abrirNivelItem(codigoEntidadeFilha) {\n  if (!codigoEntidadeFilha || typeof openLevel !== \"function\") return;\n  openLevel(NIVEL_ITEM, {\n    ARG_CODENT_FILHA: parseInt(codigoEntidadeFilha, 10),\n    P_PERIODO_INI: cfg.P_PERIODO_INI || \"\",\n    P_PERIODO_FIN: cfg.P_PERIODO_FIN || \"\",\n    P_CODMETA: cfg.P_CODMETA || \"\"\n  });\n}\n```\n\n**Segurança e bloqueio de acesso por escopo**\n- Restringir qualquer consulta de nível pela relação usuário-meta\u002Fescopo antes de agregar dados.\n- Centralizar o predicado de segurança em função de montagem de `WHERE` para reaproveitamento em KPIs, grids e gráficos.\n- Preferir variáveis de sessão (`CODUSU_LOG` ou função equivalente de usuário logado) para evitar spoof de parâmetro de usuário.\n- Bloquear carga quando parâmetros críticos estiverem ausentes (ex.: período, meta, entidade de drill-down).\n\n> Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.\n\n```sql\nSELECT\n    M.CODMETA,\n    M.CODENTIDADE,\n    SUM(M.VLRPREV) AS VLR_PREV,\n    SUM(M.VLRREAL) AS VLR_REAL\nFROM AD_DADOS_META M\nWHERE M.CODMETA = :P_CODMETA\n  AND M.DTREF BETWEEN TO_DATE(:P_PERIODO_INI, 'DD\u002FMM\u002FYYYY')\n                  AND TO_DATE(:P_PERIODO_FIN, 'DD\u002FMM\u002FYYYY')\n  AND EXISTS (\n      SELECT 1\n      FROM AD_META_USUARIO_LIB L\n      WHERE L.CODMETA = M.CODMETA\n        AND L.CODUSU = STP_GET_CODUSULOGADO\n  )\nGROUP BY M.CODMETA, M.CODENTIDADE\n```\n\n**Grid hierárquica com expansão\u002Fcolapso**\n- Estruturar mapa `filhosPorPai` e estado `nosExpandidos` para renderização incremental da árvore.\n- Inicializar nós não analíticos de níveis superiores como expandidos para melhorar leitura inicial.\n- Em nós colapsados, exibir agregados de descendentes analíticos para manter contexto sem abrir toda árvore.\n- Fornecer ações rápidas de “Expandir tudo” e “Recolher tudo” no cabeçalho.\n- Em filtros de texto, incluir ancestrais dos nós encontrados para preservar rastreabilidade hierárquica.\n\n```js\nvar filhosPorPai = {};\nvar nosExpandidos = {};\n\nfunction alternarNo(codNo) {\n  var id = String(codNo);\n  nosExpandidos[id] = !nosExpandidos[id];\n  renderizarGrid();\n}\n\nfunction obterVisiveis(raiz) {\n  var lista = [];\n  function visitar(pai) {\n    (filhosPorPai[pai] || []).forEach(function (no) {\n      lista.push(no);\n      if (nosExpandidos[String(no.CODNO)]) visitar(String(no.CODNO));\n    });\n  }\n  visitar(String(raiz || \"\"));\n  return lista;\n}\n```\n\n**Resiliência de carregamento**\n- Separar a carga principal da carga complementar (ex.: realizado mensal) e não bloquear a visualização principal por falha secundária.\n- Tratar ausência de dados por componente (`vazio`) sem derrubar o layout inteiro.\n- Destruir instâncias de gráfico antes de recriar para evitar vazamento e sobreposição visual.\n- Carregar painéis secundários somente ao abrir aba\u002Fvisão correspondente (on-demand).\n\n**Navegação intra-nível (single JSP)**\n- Tratar o JSP único como shell de navegação: tabela principal + modal de detalhe + abas internas + modais auxiliares.\n- Encadear cliques sem trocar de nível Sankhya: KPI → lista modal, gráfico → filtro de tabela, linha da tabela → detalhe.\n- Aplicar atalhos de ação no detalhe para abrir cadastro nativo no contexto da chave primária.\n- Fechar modal por clique no overlay para reduzir atrito de uso.\n\n```js\nfunction abrirTelaNativa(resourceIdBase64, pkObj) {\n  var pk = btoa(JSON.stringify(pkObj));\n  top.location.href = \"\u002Fmge\u002Fsystem.jsp#app\u002F\" + resourceIdBase64 + \"\u002F\" + pk + \"&pk-refresh=\" + Date.now();\n}\n\nfunction onKpiClick(lista) {\n  abrirModalLista(\"Itens selecionados\", \"Navegação por atalho\", lista);\n}\n\nfunction onGraficoClick(grupo) {\n  filtrarTabelaPorGrupo(grupo);\n}\n```\n\n**Feedback operacional de interface**\n- Exibir estados explícitos de carregamento, vazio e erro em cada painel.\n- Em ações de atualização, desabilitar botão de confirmação até o retorno do `executeQuery`.\n- Após sucesso, recarregar dados e restaurar contexto anterior (produto e aba ativa).\n\n**Variáveis internas de segurança**\n- Aproveitar variáveis de sessão para segurança em nível de linha (`CODUSU_LOG`, `CODGRU_LOG`, `CODVEN_LOG`).\n- Restringir dados por contexto do usuário antes de montar visualizações.\n",{"name":5,"risk":42,"tags":60,"source":47,"category":53,"date_added":61,"description":7},[40,35,39,38,54,48],"2026-03-10","main","7a38d8e7073e61c16eda85681cb32fb68a8657a5","skills-store\u002Fcopilot\u002Fsankhya-dashboard-html-jsp-custom-best-pratices","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router","https:\u002F\u002Fgithub.com\u002Fsapsapshen","2026-04-14 21:12:12",{"source":8,"skillName":5,"commands":69,"prompt":79,"zipAvailable":80},[70,73,76],{"tool":71,"command":72},"npx","npx skills add sapsapshen\u002Fai-memory-router --skill sankhya-dashboard-html-jsp-custom-best-pratices",{"tool":74,"command":75},"bunx","bunx skills add sapsapshen\u002Fai-memory-router --skill sankhya-dashboard-html-jsp-custom-best-pratices",{"tool":77,"command":78},"pnpm","pnpm dlx skills add sapsapshen\u002Fai-memory-router --skill sankhya-dashboard-html-jsp-custom-best-pratices","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcopilot\u002Fsankhya-dashboard-html-jsp-custom-best-pratices\u002FSKILL.md",true,[82],{"path":83,"resourceType":84,"resourceTypeDesc":85,"sizeBytes":86,"sourceHtmlUrl":87},"README.md","REFERENCE","参考文档",3275,"https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcopilot\u002Fsankhya-dashboard-html-jsp-custom-best-pratices\u002FREADME.md",[89,111,136,146,159,176],{"skillNo":90,"name":91,"nameZh":6,"description":92,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":98,"publishedAt":55},"01M71ATQRKG48SH06FQ697T5G9","wechat-search","Search WeChat Official Account articles using OpenClaw's web search and fetch capabilities with compliance-focused design.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fwechat-search-release\u002FSKILL.md",[95],{"categoryNo":96,"categoryName":97,"taxonomyType":32,"taxonomyTypeDesc":33},"01JBSEEDCAT000000000000037","律师与法务",[99,100,101,102,103,104,105,106,107,108,109,110],"wechat search","wechat","search","capabilities","openclaw","design","web search","compliance","web","official","fetch","with",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":117,"publishedAt":55},"01M71ATNAVGZB35PFYM2GC5N00","trend-tap","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend tap', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-tap\u002FSKILL.md",[],[118,119,120,121,122,123,101,124,125,126,127,43,128,129,130,131,132,133,134,135],"trend","trend tap","tap","api keys","Bash","time","trending","hacker","aggregator","do","brew","live","data","google","install","read","across","api",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":142,"publishedAt":55},"01M71ATJ9AM5KM15AA8PS302EB","trend-radar","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend pulse', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-radar\u002FSKILL.md",[],[143,144,118,128,129,130,131,122,101,133,145,134,135,43,126,125,124,132,123,121],"trend radar","radar","pulse",{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":152,"publishedAt":55},"01M71ATGF095N3EJM9G8SD0BB5","tech-news-daily","每日科技新闻汇——从全球多个 RSS 源抓取最新科技新闻，翻译（英→中）、去重、排版成每日简报。当用户询问科技新闻、科技新鲜事、每日科技、技术动态、Daily Tech News 或类似表达时触发此技能。","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftech-news-daily\u002FSKILL.md",[],[153,154,155,156,157,158],"news","tech","tech news daily","daily","rss","翻译",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":168,"publishedAt":55},"01M71ATFAEAMQ17M70NQNY7SR8","summarize","Summarize URLs or files with the summarize CLI (web, PDFs, images, audio, YouTube).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsummarize\u002FSKILL.md",[165],{"categoryNo":166,"categoryName":167,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000021","资料检索与研究",[161,107,169,170,171,110,128,172,173,174,120,175,132],"Audio","cli","youtube","images","https","files","pdfs",{"skillNo":177,"name":178,"nameZh":6,"description":179,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":180,"categories":181,"keywords":182,"publishedAt":55},"01M71ATDNS3R0M4H936PACQAWZ","sonoscli","Control Sonos speakers (discover\u002Fstatus\u002Fplay\u002Fvolume\u002Fgroup).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsonoscli\u002FSKILL.md",[],[178,183,184,185,132,186,173,187,188,189,190,191],"status","play","go","latest","github","discover","control","cmd","volume"]