@creative-ia/cortex 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/dist/config/cloud-proxy.d.ts +15 -0
- package/dist/config/cloud-proxy.js +63 -0
- package/dist/config/cloudwatch-store.d.ts +13 -0
- package/dist/config/cloudwatch-store.js +66 -0
- package/dist/config/license.d.ts +29 -0
- package/dist/config/license.js +165 -0
- package/dist/config/ssm-store.d.ts +2 -0
- package/dist/config/ssm-store.js +38 -0
- package/dist/config/telemetry.d.ts +17 -0
- package/dist/config/telemetry.js +93 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +460 -0
- package/dist/knowledge/dynamo-store.d.ts +17 -0
- package/dist/knowledge/dynamo-store.js +85 -0
- package/dist/knowledge/embeddings.d.ts +2 -0
- package/dist/knowledge/embeddings.js +36 -0
- package/dist/knowledge/loader.d.ts +8 -0
- package/dist/knowledge/loader.js +57 -0
- package/dist/lambda-package.zip +0 -0
- package/dist/lambda.d.ts +22 -0
- package/dist/lambda.js +496 -0
- package/dist/package.json +1 -0
- package/dist/tools/advance-process.d.ts +7 -0
- package/dist/tools/advance-process.js +128 -0
- package/dist/tools/analyze-code.d.ts +7 -0
- package/dist/tools/analyze-code.js +131 -0
- package/dist/tools/analyze-docs.d.ts +8 -0
- package/dist/tools/analyze-docs.js +147 -0
- package/dist/tools/config-registry.d.ts +3 -0
- package/dist/tools/config-registry.js +20 -0
- package/dist/tools/create-process.d.ts +6 -0
- package/dist/tools/create-process.js +257 -0
- package/dist/tools/decompose-epic.d.ts +7 -0
- package/dist/tools/decompose-epic.js +603 -0
- package/dist/tools/diagrams.d.ts +51 -0
- package/dist/tools/diagrams.js +304 -0
- package/dist/tools/generate-report.d.ts +9 -0
- package/dist/tools/generate-report.js +891 -0
- package/dist/tools/generate-wiki.d.ts +10 -0
- package/dist/tools/generate-wiki.js +700 -0
- package/dist/tools/get-architecture.d.ts +6 -0
- package/dist/tools/get-architecture.js +78 -0
- package/dist/tools/get-code-standards.d.ts +7 -0
- package/dist/tools/get-code-standards.js +52 -0
- package/dist/tools/init-process.d.ts +7 -0
- package/dist/tools/init-process.js +82 -0
- package/dist/tools/knowledge-crud.d.ts +26 -0
- package/dist/tools/knowledge-crud.js +142 -0
- package/dist/tools/logo-base64.d.ts +1 -0
- package/dist/tools/logo-base64.js +1 -0
- package/dist/tools/logs-query.d.ts +15 -0
- package/dist/tools/logs-query.js +46 -0
- package/dist/tools/reverse-engineer.d.ts +13 -0
- package/dist/tools/reverse-engineer.js +956 -0
- package/dist/tools/semantic-search.d.ts +7 -0
- package/dist/tools/semantic-search.js +68 -0
- package/dist/tools/update-process.d.ts +17 -0
- package/dist/tools/update-process.js +195 -0
- package/dist/tools/validate-idea.d.ts +7 -0
- package/dist/tools/validate-idea.js +339 -0
- package/dist/tools/validate-process.d.ts +6 -0
- package/dist/tools/validate-process.js +102 -0
- package/package.json +31 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geradores de Diagramas — CSS puro usando classes bd-* do _base-styles.css
|
|
3
|
+
* Gera diagramas visuais de workflow, arquitetura, dominios, riscos, integracoes e benchmark.
|
|
4
|
+
* IP protegido: os templates de diagrama nunca sao expostos ao cliente.
|
|
5
|
+
*/
|
|
6
|
+
// SVG seta para baixo
|
|
7
|
+
const SETA_BAIXO = `<svg class="bd-svg" viewBox="0 0 600 28" style="max-width:320px;display:block;margin:0 auto;">
|
|
8
|
+
<line x1="300" y1="0" x2="300" y2="20" stroke="var(--accent)" stroke-width="2" stroke-dasharray="4,3"/>
|
|
9
|
+
<polygon points="293,18 300,28 307,18" fill="var(--accent)"/>
|
|
10
|
+
</svg>`;
|
|
11
|
+
// SVG seta para direita (fluxo de integracoes)
|
|
12
|
+
const SETA_DIREITA = `<svg viewBox="0 0 32 24" style="width:32px;height:24px;flex-shrink:0;">
|
|
13
|
+
<line x1="0" y1="12" x2="24" y2="12" stroke="var(--accent)" stroke-width="2" stroke-dasharray="4,3"/>
|
|
14
|
+
<polygon points="22,6 32,12 22,18" fill="var(--accent)"/>
|
|
15
|
+
</svg>`;
|
|
16
|
+
export function renderWorkflowDiagram(phases) {
|
|
17
|
+
const nodes = phases.map((p) => {
|
|
18
|
+
const cls = p.status === "concluido" ? "bd-global" : p.status === "ativo" ? "bd-root bd-active" : "bd-local";
|
|
19
|
+
const icon = p.status === "concluido" ? "check_circle" : p.status === "ativo" ? "pending" : "schedule";
|
|
20
|
+
return `<div class="bd-node ${cls}" style="flex:1;min-width:140px;font-size:1rem;padding:16px 12px;">
|
|
21
|
+
<span class="material-symbols-outlined" style="font-size:1.4rem;vertical-align:middle;margin-right:4px;">${icon}</span>
|
|
22
|
+
${p.name}
|
|
23
|
+
${p.subtitle ? `<span class="bd-sub" style="font-size:.85rem;">${p.subtitle}</span>` : ""}
|
|
24
|
+
</div>`;
|
|
25
|
+
}).join(`<div style="display:flex;align-items:center;"><span class="material-symbols-outlined" style="color:var(--accent);font-size:1.8rem;">arrow_forward</span></div>`);
|
|
26
|
+
return `<div class="bd-wrap" style="padding:24px;">
|
|
27
|
+
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:center;">
|
|
28
|
+
${nodes}
|
|
29
|
+
</div>
|
|
30
|
+
</div>`;
|
|
31
|
+
}
|
|
32
|
+
// ============================================================
|
|
33
|
+
// 2. DIAGRAMA DE ARQUITETURA — Camadas com regra de dependencia
|
|
34
|
+
// ============================================================
|
|
35
|
+
export function renderArchitectureDiagram() {
|
|
36
|
+
return `<div class="bd-wrap" style="padding:24px;">
|
|
37
|
+
<div class="bd-node bd-root" style="max-width:540px;width:100%;font-size:1.05rem;padding:18px;">
|
|
38
|
+
<span class="material-symbols-outlined" style="font-size:1.3rem;vertical-align:middle;margin-right:6px;">devices</span>
|
|
39
|
+
Apresentacao
|
|
40
|
+
<span class="bd-sub" style="font-size:.9rem;">Componentes UI, Paginas, Hooks (React/RN)</span>
|
|
41
|
+
</div>
|
|
42
|
+
${SETA_BAIXO}
|
|
43
|
+
<div class="bd-node bd-global" style="max-width:540px;width:100%;font-size:1.05rem;padding:18px;">
|
|
44
|
+
<span class="material-symbols-outlined" style="font-size:1.3rem;vertical-align:middle;margin-right:6px;">hub</span>
|
|
45
|
+
Aplicacao
|
|
46
|
+
<span class="bd-sub" style="font-size:.9rem;">Casos de Uso, Servicos, Orquestracao (BFF Resolvers, Controllers)</span>
|
|
47
|
+
</div>
|
|
48
|
+
${SETA_BAIXO}
|
|
49
|
+
<div class="bd-node bd-dev" style="max-width:540px;width:100%;background:linear-gradient(135deg,#1B3B6F,#0F1C2E);color:#fff;border:2px solid var(--accent);font-size:1.05rem;padding:18px;">
|
|
50
|
+
<span class="material-symbols-outlined" style="font-size:1.3rem;vertical-align:middle;margin-right:6px;">diamond</span>
|
|
51
|
+
Dominio
|
|
52
|
+
<span class="bd-sub" style="font-size:.9rem;">Entidades, Value Objects, Regras de Negocio, Interfaces/Portas</span>
|
|
53
|
+
</div>
|
|
54
|
+
${SETA_BAIXO}
|
|
55
|
+
<div class="bd-row" style="display:flex;gap:14px;justify-content:center;flex-wrap:wrap;">
|
|
56
|
+
<div class="bd-node bd-local" style="flex:1;min-width:220px;font-size:1.05rem;padding:18px;">
|
|
57
|
+
<span class="material-symbols-outlined" style="font-size:1.3rem;vertical-align:middle;margin-right:6px;">storage</span>
|
|
58
|
+
Infraestrutura
|
|
59
|
+
<span class="bd-sub" style="font-size:.9rem;">Clientes HTTP, Adaptadores BD, APIs Externas</span>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="bd-node bd-local" style="flex:1;min-width:220px;font-size:1.05rem;padding:18px;">
|
|
62
|
+
<span class="material-symbols-outlined" style="font-size:1.3rem;vertical-align:middle;margin-right:6px;">share</span>
|
|
63
|
+
Compartilhado
|
|
64
|
+
<span class="bd-sub" style="font-size:.9rem;">Tipos, Utilitarios, Constantes, Erros</span>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
<div style="margin-top:14px;font-size:.9rem;color:var(--muted);text-align:center;">
|
|
68
|
+
<span class="material-symbols-outlined" style="font-size:1rem;vertical-align:middle;">info</span>
|
|
69
|
+
Regra de Dependencia: camadas internas nunca dependem de externas. Inversao via interfaces.
|
|
70
|
+
</div>
|
|
71
|
+
</div>`;
|
|
72
|
+
}
|
|
73
|
+
export function renderDomainMap(domains) {
|
|
74
|
+
const colorMap = {
|
|
75
|
+
core: "background:linear-gradient(135deg,#1B3B6F,#2F6DB3);color:#fff;",
|
|
76
|
+
supporting: "background:var(--line);color:var(--ink);border:1px solid var(--brand);",
|
|
77
|
+
generic: "background:var(--ok-bg);color:var(--ok);border:1px solid rgba(34,197,94,.3);",
|
|
78
|
+
};
|
|
79
|
+
const iconMap = { core: "star", supporting: "support", generic: "settings" };
|
|
80
|
+
const labelMap = { core: "Principal", supporting: "Suporte", generic: "Generico" };
|
|
81
|
+
const nodes = domains.map((d) => `
|
|
82
|
+
<div class="bd-node" style="${colorMap[d.type]}border-radius:12px;padding:18px;text-align:center;min-width:155px;">
|
|
83
|
+
<span class="material-symbols-outlined" style="font-size:1.4rem;display:block;margin-bottom:6px;">${iconMap[d.type]}</span>
|
|
84
|
+
<strong style="font-size:1.05rem;">${d.name}</strong>
|
|
85
|
+
<span class="bd-sub" style="font-size:.85rem;display:block;margin-top:4px;">${labelMap[d.type].toUpperCase()} — ${d.squad}</span>
|
|
86
|
+
</div>
|
|
87
|
+
`).join("");
|
|
88
|
+
return `<div class="bd-wrap" style="padding:24px;">
|
|
89
|
+
<div style="display:flex;flex-wrap:wrap;gap:14px;justify-content:center;">
|
|
90
|
+
${nodes}
|
|
91
|
+
</div>
|
|
92
|
+
<div style="margin-top:18px;display:flex;gap:20px;justify-content:center;font-size:.9rem;color:var(--muted);">
|
|
93
|
+
<span><span style="display:inline-block;width:14px;height:14px;border-radius:3px;background:linear-gradient(135deg,#1B3B6F,#2F6DB3);vertical-align:middle;margin-right:5px;"></span> Principal</span>
|
|
94
|
+
<span><span style="display:inline-block;width:14px;height:14px;border-radius:3px;background:var(--line);border:1px solid var(--brand);vertical-align:middle;margin-right:5px;"></span> Suporte</span>
|
|
95
|
+
<span><span style="display:inline-block;width:14px;height:14px;border-radius:3px;background:var(--ok-bg);border:1px solid rgba(34,197,94,.3);vertical-align:middle;margin-right:5px;"></span> Generico</span>
|
|
96
|
+
</div>
|
|
97
|
+
</div>`;
|
|
98
|
+
}
|
|
99
|
+
const ROTULOS_PROB = ["baixa", "media", "alta"];
|
|
100
|
+
const ROTULOS_IMPACTO = ["baixo", "medio", "alto"];
|
|
101
|
+
function corRisco(prob, impact) {
|
|
102
|
+
const p = ROTULOS_PROB.indexOf(prob);
|
|
103
|
+
const i = ROTULOS_IMPACTO.indexOf(impact);
|
|
104
|
+
const score = p + i;
|
|
105
|
+
if (score >= 3)
|
|
106
|
+
return "background:rgba(239,68,68,.25);border:1px solid rgba(239,68,68,.5);color:#fca5a5;";
|
|
107
|
+
if (score === 2)
|
|
108
|
+
return "background:rgba(245,158,11,.2);border:1px solid rgba(245,158,11,.4);color:#fcd34d;";
|
|
109
|
+
return "background:rgba(34,197,94,.15);border:1px solid rgba(34,197,94,.3);color:#86efac;";
|
|
110
|
+
}
|
|
111
|
+
export function renderRiskMatrix(risks) {
|
|
112
|
+
const grid = Array.from({ length: 3 }, () => Array(3).fill(""));
|
|
113
|
+
for (const r of risks) {
|
|
114
|
+
const row = 2 - ROTULOS_PROB.indexOf(r.probability);
|
|
115
|
+
const col = ROTULOS_IMPACTO.indexOf(r.impact);
|
|
116
|
+
if (row >= 0 && row < 3 && col >= 0 && col < 3) {
|
|
117
|
+
grid[row][col] += (grid[row][col] ? "<br/>" : "") + r.name;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const cabecalhoImpacto = ["Baixo", "Medio", "Alto"];
|
|
121
|
+
const cabecalhoProb = ["Alta", "Media", "Baixa"];
|
|
122
|
+
let rows = "";
|
|
123
|
+
for (let r = 0; r < 3; r++) {
|
|
124
|
+
let cells = `<td style="font-weight:600;padding:10px 14px;color:var(--muted);text-align:right;white-space:nowrap;font-size:.95rem;">${cabecalhoProb[r]}</td>`;
|
|
125
|
+
for (let c = 0; c < 3; c++) {
|
|
126
|
+
const score = (2 - r) + c;
|
|
127
|
+
const bg = score >= 3
|
|
128
|
+
? "background:rgba(239,68,68,.18);"
|
|
129
|
+
: score === 2
|
|
130
|
+
? "background:rgba(245,158,11,.12);"
|
|
131
|
+
: "background:rgba(34,197,94,.1);";
|
|
132
|
+
const content = grid[r][c] || "<span style='color:var(--muted);font-size:.85rem;'>—</span>";
|
|
133
|
+
cells += `<td style="${bg}padding:12px;border-radius:8px;font-size:.95rem;min-width:140px;text-align:center;">${content}</td>`;
|
|
134
|
+
}
|
|
135
|
+
rows += `<tr>${cells}</tr>`;
|
|
136
|
+
}
|
|
137
|
+
const detalhes = risks.map((r) => {
|
|
138
|
+
const style = corRisco(r.probability, r.impact);
|
|
139
|
+
const icon = (ROTULOS_PROB.indexOf(r.probability) + ROTULOS_IMPACTO.indexOf(r.impact)) >= 3 ? "error" : "warning";
|
|
140
|
+
return `<div style="${style}border-radius:10px;padding:12px 16px;font-size:.95rem;display:flex;align-items:center;gap:10px;">
|
|
141
|
+
<span class="material-symbols-outlined" style="font-size:1.2rem;">${icon}</span>
|
|
142
|
+
<span><strong>${r.name}</strong>${r.category ? ` <span style="opacity:.7;">(${r.category})</span>` : ""} — Probabilidade: ${r.probability} | Impacto: ${r.impact}</span>
|
|
143
|
+
</div>`;
|
|
144
|
+
}).join("");
|
|
145
|
+
return `<div class="bd-wrap" style="padding:24px;">
|
|
146
|
+
<div style="text-align:center;margin-bottom:14px;font-size:.95rem;color:var(--muted);">
|
|
147
|
+
<span class="material-symbols-outlined" style="font-size:1.1rem;vertical-align:middle;">grid_on</span> Matriz de Riscos (Probabilidade x Impacto)
|
|
148
|
+
</div>
|
|
149
|
+
<table style="border-collapse:separate;border-spacing:8px;margin:0 auto;width:auto;">
|
|
150
|
+
<thead>
|
|
151
|
+
<tr>
|
|
152
|
+
<th style="padding:8px;"></th>
|
|
153
|
+
${cabecalhoImpacto.map((h) => `<th style="padding:8px 14px;font-size:.9rem;color:var(--muted);font-weight:500;">${h}</th>`).join("")}
|
|
154
|
+
</tr>
|
|
155
|
+
<tr><td></td><td colspan="3" style="text-align:center;font-size:.8rem;color:var(--muted);padding-bottom:6px;">IMPACTO <span class="material-symbols-outlined" style="font-size:.8rem;">arrow_forward</span></td></tr>
|
|
156
|
+
</thead>
|
|
157
|
+
<tbody>${rows}</tbody>
|
|
158
|
+
</table>
|
|
159
|
+
<div style="text-align:left;font-size:.8rem;color:var(--muted);margin-top:4px;margin-left:24px;">PROBABILIDADE <span class="material-symbols-outlined" style="font-size:.8rem;">arrow_upward</span></div>
|
|
160
|
+
${detalhes ? `<div style="display:flex;flex-direction:column;gap:8px;margin-top:18px;">${detalhes}</div>` : ""}
|
|
161
|
+
</div>`;
|
|
162
|
+
}
|
|
163
|
+
export function renderIntegrationFlow(nodes, links) {
|
|
164
|
+
const styleMap = {
|
|
165
|
+
interno: "background:linear-gradient(135deg,#1B3B6F,#2F6DB3);color:#fff;border:1px solid var(--accent);",
|
|
166
|
+
externo: "background:var(--line);color:var(--ink);border:1px solid var(--brand);",
|
|
167
|
+
gateway: "background:linear-gradient(135deg,#0D1321,#1B3B6F);color:#00E0FF;border:2px solid #00E0FF;",
|
|
168
|
+
};
|
|
169
|
+
const iconMap = { interno: "dns", externo: "cloud", gateway: "router" };
|
|
170
|
+
const labelMap = { interno: "Interno", externo: "Externo", gateway: "Gateway" };
|
|
171
|
+
const nodeHtml = nodes.map((n) => `
|
|
172
|
+
<div class="bd-node" style="${styleMap[n.type]}border-radius:14px;padding:18px 22px;text-align:center;min-width:145px;">
|
|
173
|
+
<span class="material-symbols-outlined" style="font-size:1.5rem;display:block;margin-bottom:6px;">${iconMap[n.type]}</span>
|
|
174
|
+
<strong style="font-size:1.05rem;">${n.name}</strong>
|
|
175
|
+
${n.protocol ? `<span class="bd-sub" style="display:block;font-size:.82rem;margin-top:4px;">${n.protocol}</span>` : ""}
|
|
176
|
+
</div>
|
|
177
|
+
`).join("");
|
|
178
|
+
const linkHtml = links.map((l) => `
|
|
179
|
+
<div style="display:flex;align-items:center;gap:10px;font-size:.95rem;padding:8px 0;">
|
|
180
|
+
<span style="color:var(--accent);font-weight:600;min-width:110px;text-align:right;">${l.from}</span>
|
|
181
|
+
${SETA_DIREITA}
|
|
182
|
+
<span style="color:var(--ink);font-weight:600;min-width:110px;">${l.to}</span>
|
|
183
|
+
${l.label ? `<span style="color:var(--muted);font-size:.85rem;margin-left:10px;">${l.label}</span>` : ""}
|
|
184
|
+
${l.protocol ? `<span class="pill" style="font-size:.75rem;padding:3px 10px;">${l.protocol}</span>` : ""}
|
|
185
|
+
</div>
|
|
186
|
+
`).join("");
|
|
187
|
+
return `<div class="bd-wrap" style="padding:24px;">
|
|
188
|
+
<div style="display:flex;flex-wrap:wrap;gap:18px;justify-content:center;align-items:center;margin-bottom:22px;">
|
|
189
|
+
${nodeHtml}
|
|
190
|
+
</div>
|
|
191
|
+
<div style="border-top:1px solid var(--line);padding-top:16px;">
|
|
192
|
+
<div style="font-size:.9rem;color:var(--muted);margin-bottom:10px;">
|
|
193
|
+
<span class="material-symbols-outlined" style="font-size:1rem;vertical-align:middle;">cable</span> Fluxo de Dados
|
|
194
|
+
</div>
|
|
195
|
+
${linkHtml}
|
|
196
|
+
</div>
|
|
197
|
+
<div style="margin-top:16px;display:flex;gap:20px;justify-content:center;font-size:.85rem;color:var(--muted);">
|
|
198
|
+
<span><span style="display:inline-block;width:14px;height:14px;border-radius:3px;background:linear-gradient(135deg,#1B3B6F,#2F6DB3);vertical-align:middle;margin-right:5px;"></span> ${labelMap.interno}</span>
|
|
199
|
+
<span><span style="display:inline-block;width:14px;height:14px;border-radius:3px;background:var(--line);border:1px solid var(--brand);vertical-align:middle;margin-right:5px;"></span> ${labelMap.externo}</span>
|
|
200
|
+
<span><span style="display:inline-block;width:14px;height:14px;border-radius:3px;background:linear-gradient(135deg,#0D1321,#1B3B6F);border:2px solid #00E0FF;vertical-align:middle;margin-right:5px;"></span> ${labelMap.gateway}</span>
|
|
201
|
+
</div>
|
|
202
|
+
</div>`;
|
|
203
|
+
}
|
|
204
|
+
export function renderBenchmarkMercado(concorrentes, dimensoes, titulo, fontes) {
|
|
205
|
+
const tipoStyle = {
|
|
206
|
+
nosso: { bg: "background:linear-gradient(135deg,#1B3B6F,#2F6DB3);color:#fff;border:2px solid #00E0FF;box-shadow:0 0 16px rgba(0,224,255,.15);", icon: "star", label: "Nosso Produto" },
|
|
207
|
+
banco: { bg: "background:var(--line);color:var(--ink);border:1px solid var(--brand);", icon: "account_balance", label: "Banco" },
|
|
208
|
+
fintech: { bg: "background:rgba(139,92,246,.15);color:#c4b5fd;border:1px solid rgba(139,92,246,.4);", icon: "smartphone", label: "Fintech" },
|
|
209
|
+
financeira: { bg: "background:rgba(245,158,11,.12);color:#fcd34d;border:1px solid rgba(245,158,11,.35);", icon: "payments", label: "Financeira" },
|
|
210
|
+
cooperativa: { bg: "background:rgba(34,197,94,.12);color:#86efac;border:1px solid rgba(34,197,94,.35);", icon: "groups", label: "Cooperativa" },
|
|
211
|
+
};
|
|
212
|
+
// Cards dos concorrentes
|
|
213
|
+
const cards = concorrentes.map((c) => {
|
|
214
|
+
const st = tipoStyle[c.tipo] || tipoStyle.banco;
|
|
215
|
+
const isNosso = c.tipo === "nosso";
|
|
216
|
+
return `<div style="${st.bg}border-radius:14px;padding:20px;min-width:170px;flex:1;text-align:center;${isNosso ? "transform:scale(1.04);" : ""}">
|
|
217
|
+
<span class="material-symbols-outlined" style="font-size:1.6rem;display:block;margin-bottom:6px;">${st.icon}</span>
|
|
218
|
+
<strong style="font-size:1.1rem;display:block;">${c.nome}</strong>
|
|
219
|
+
<span style="font-size:.8rem;opacity:.7;display:block;margin-top:2px;">${st.label}</span>
|
|
220
|
+
</div>`;
|
|
221
|
+
}).join("");
|
|
222
|
+
// Tabela comparativa
|
|
223
|
+
const headerCells = concorrentes.map((c) => {
|
|
224
|
+
const isNosso = c.tipo === "nosso";
|
|
225
|
+
return `<th style="padding:10px 14px;font-size:.92rem;font-weight:600;${isNosso ? "color:#00E0FF;background:rgba(0,224,255,.08);border-radius:8px 8px 0 0;" : "color:var(--muted);"}">${c.nome}</th>`;
|
|
226
|
+
}).join("");
|
|
227
|
+
const bodyRows = dimensoes.map((dim) => {
|
|
228
|
+
// Encontrar melhor valor para highlight
|
|
229
|
+
const numVals = concorrentes.map((c) => {
|
|
230
|
+
const v = c.indicadores[dim.chave];
|
|
231
|
+
return typeof v === "number" ? v : parseFloat(String(v)) || 0;
|
|
232
|
+
});
|
|
233
|
+
const bestVal = dim.maiorMelhor ? Math.max(...numVals) : Math.min(...numVals.filter((v) => v > 0));
|
|
234
|
+
const cells = concorrentes.map((c, ci) => {
|
|
235
|
+
const val = c.indicadores[dim.chave];
|
|
236
|
+
const numVal = typeof val === "number" ? val : parseFloat(String(val)) || 0;
|
|
237
|
+
const isBest = numVal === bestVal && numVal > 0;
|
|
238
|
+
const isNosso = c.tipo === "nosso";
|
|
239
|
+
const cellBg = isNosso ? "background:rgba(0,224,255,.05);" : "";
|
|
240
|
+
const bestStyle = isBest ? "color:#4ade80;font-weight:700;" : "color:var(--ink);";
|
|
241
|
+
const bestIcon = isBest ? `<span class="material-symbols-outlined" style="font-size:.85rem;color:#4ade80;vertical-align:middle;margin-left:3px;">emoji_events</span>` : "";
|
|
242
|
+
const display = typeof val === "string" ? val : `${val}${dim.unidade || ""}`;
|
|
243
|
+
return `<td style="${cellBg}${bestStyle}padding:10px 14px;font-size:.95rem;text-align:center;">${display}${bestIcon}</td>`;
|
|
244
|
+
}).join("");
|
|
245
|
+
return `<tr>
|
|
246
|
+
<td style="padding:10px 14px;font-size:.92rem;font-weight:500;color:var(--muted);text-align:left;white-space:nowrap;">
|
|
247
|
+
${dim.nome}
|
|
248
|
+
</td>
|
|
249
|
+
${cells}
|
|
250
|
+
</tr>`;
|
|
251
|
+
}).join("");
|
|
252
|
+
// Legenda de tipos
|
|
253
|
+
const legendItems = [...new Set(concorrentes.map((c) => c.tipo))].map((t) => {
|
|
254
|
+
const st = tipoStyle[t] || tipoStyle.banco;
|
|
255
|
+
return `<span style="display:flex;align-items:center;gap:4px;">
|
|
256
|
+
<span class="material-symbols-outlined" style="font-size:.9rem;">${st.icon}</span>
|
|
257
|
+
<span style="font-size:.82rem;">${st.label}</span>
|
|
258
|
+
</span>`;
|
|
259
|
+
}).join("");
|
|
260
|
+
return `<div class="bd-wrap" style="padding:24px;">
|
|
261
|
+
<div style="text-align:center;margin-bottom:20px;">
|
|
262
|
+
<div style="font-size:1.1rem;font-weight:600;color:var(--ink);margin-bottom:4px;">
|
|
263
|
+
<span class="material-symbols-outlined" style="font-size:1.3rem;vertical-align:middle;">leaderboard</span>
|
|
264
|
+
${titulo || "Benchmark de Mercado"}
|
|
265
|
+
</div>
|
|
266
|
+
<div style="font-size:.85rem;color:var(--muted);">Comparativo com principais concorrentes do segmento</div>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div style="display:flex;flex-wrap:wrap;gap:14px;justify-content:center;margin-bottom:24px;">
|
|
270
|
+
${cards}
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<div style="overflow-x:auto;">
|
|
274
|
+
<table style="width:100%;border-collapse:separate;border-spacing:0 4px;">
|
|
275
|
+
<thead>
|
|
276
|
+
<tr>
|
|
277
|
+
<th style="padding:10px 14px;font-size:.85rem;color:var(--muted);text-align:left;">Dimensao</th>
|
|
278
|
+
${headerCells}
|
|
279
|
+
</tr>
|
|
280
|
+
</thead>
|
|
281
|
+
<tbody>${bodyRows}</tbody>
|
|
282
|
+
</table>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<div style="margin-top:18px;display:flex;gap:18px;justify-content:center;color:var(--muted);flex-wrap:wrap;">
|
|
286
|
+
${legendItems}
|
|
287
|
+
<span style="display:flex;align-items:center;gap:4px;">
|
|
288
|
+
<span class="material-symbols-outlined" style="font-size:.9rem;color:#4ade80;">emoji_events</span>
|
|
289
|
+
<span style="font-size:.82rem;">Melhor do segmento</span>
|
|
290
|
+
</span>
|
|
291
|
+
</div>
|
|
292
|
+
${fontes && fontes.length > 0 ? `
|
|
293
|
+
<div style="margin-top:22px;padding-top:14px;border-top:1px solid var(--line);">
|
|
294
|
+
<div style="font-size:.82rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:5px;">
|
|
295
|
+
<span class="material-symbols-outlined" style="font-size:.9rem;">menu_book</span> Fontes da Pesquisa
|
|
296
|
+
</div>
|
|
297
|
+
<div style="display:flex;flex-direction:column;gap:4px;">
|
|
298
|
+
${fontes.map((f) => `<div style="font-size:.78rem;color:var(--muted);padding-left:12px;display:flex;align-items:baseline;gap:6px;">
|
|
299
|
+
<span style="color:var(--accent);font-size:.6rem;">●</span> ${f}
|
|
300
|
+
</div>`).join("")}
|
|
301
|
+
</div>
|
|
302
|
+
</div>` : ""}
|
|
303
|
+
</div>`;
|
|
304
|
+
}
|