@gyramais/gyra-design 0.2.2 → 0.2.21
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/dist/components/Filter/index.js +6 -6
- package/dist/components/Filter/index.js.map +1 -1
- package/dist/components/Input/index.js +1 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/MultiSelectPicker/MultiSelectPicker.d.ts +0 -3
- package/dist/components/MultiSelectPicker/index.js +1 -1
- package/dist/components/MultiSelectPicker/index.js.map +1 -1
- package/dist/components/RichInput/index.js +3 -3
- package/dist/components/RichInput/index.js.map +1 -1
- package/dist/components/SelectTree/index.js +1 -1
- package/dist/components/SelectTree/index.js.map +1 -1
- package/dist/components/TourGuide/TourGuide.d.ts +39 -0
- package/dist/components/TourGuide/index.d.ts +1 -0
- package/dist/components/TourGuide/index.js +7 -0
- package/dist/components/TourGuide/index.js.map +1 -0
- package/dist/index.css +164 -220
- package/package.json +1 -1
- package/dist/components/AiParecer/AiParecer.d.ts +0 -75
- package/dist/components/AiParecer/index.d.ts +0 -1
- package/dist/components/AiParecer/index.js +0 -7
- package/dist/components/AiParecer/index.js.map +0 -1
- package/dist/components/Email/Email.d.ts +0 -25
- package/dist/components/Email/index.d.ts +0 -2
- package/dist/components/Email/index.js +0 -2
- package/dist/components/Email/index.js.map +0 -1
- package/dist/components/EmailBalanceSheetFinalized/EmailBalanceSheetFinalized.d.ts +0 -18
- package/dist/components/EmailBalanceSheetFinalized/index.d.ts +0 -2
- package/dist/components/EmailBalanceSheetFinalized/index.js +0 -2
- package/dist/components/EmailBalanceSheetFinalized/index.js.map +0 -1
- package/dist/emails/balance-sheet-finalized/index.d.ts +0 -33
- package/dist/emails/balance-sheet-finalized/index.esm.js +0 -2
- package/dist/emails/balance-sheet-finalized/index.esm.js.map +0 -1
- package/dist/emails/balance-sheet-finalized/index.js +0 -2
- package/dist/emails/balance-sheet-finalized/index.js.map +0 -1
- package/dist/emails/email/index.d.ts +0 -94
- package/dist/emails/email/index.esm.js +0 -2
- package/dist/emails/email/index.esm.js.map +0 -1
- package/dist/emails/email/index.js +0 -2
- package/dist/emails/email/index.js.map +0 -1
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { EmailLayoutInput } from '../../emails/email';
|
|
3
|
-
/**
|
|
4
|
-
* Preview de email transacional Gyra+ — genérico, configurável.
|
|
5
|
-
*
|
|
6
|
-
* Renderiza o HTML produzido por `buildEmail(input)` dentro de um iframe
|
|
7
|
-
* com `sandbox=""` pra isolar do CSS do Storybook e simular Gmail/Outlook
|
|
8
|
-
* com fidelidade.
|
|
9
|
-
*
|
|
10
|
-
* **Uso**: passe qualquer combinação de campos opcionais (greeting, title
|
|
11
|
-
* obrigatório, paragraphs, highlights lado-a-lado, aiParecer, buttons,
|
|
12
|
-
* footerNote). É o mesmo `EmailLayoutInput` que o `buildEmail` aceita.
|
|
13
|
-
*
|
|
14
|
-
* Pra previsualizar um email transacional específico (ex: reset de senha,
|
|
15
|
-
* batch concluído, alerta), basta montar os props correspondentes — não
|
|
16
|
-
* precisa criar um componente novo por caso.
|
|
17
|
-
*/
|
|
18
|
-
export type EmailProps = EmailLayoutInput & {
|
|
19
|
-
/** Largura visual do iframe. Default 700px (cabe o card 600 + paddings). */
|
|
20
|
-
width?: number | string;
|
|
21
|
-
/** Altura visual do iframe. Default 1000px. Ajuste se o conteúdo crescer. */
|
|
22
|
-
height?: number | string;
|
|
23
|
-
};
|
|
24
|
-
export declare const Email: React.FC<EmailProps>;
|
|
25
|
-
export default Email;
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use client";import t from"react";const e="#1256FF",n="#7C4EFF",i="#6635E0",r="#F5F5F7",l="#FFFFFF",o="#1D1D1F",a="#636366",p="#8E8E93",d="#D2D2D7",s="#E8E8ED",g="#F6F4FF",c="rgba(124,78,255,0.22)",x={ok:{bg:"#E8F9ED",color:"#1A7D37"},warn:{bg:"#FFF4E0",color:"#946200"},danger:{bg:"#FFEDEC",color:"#C93430"},info:{bg:"#EBF4FF",color:"#0055D4"}},h="'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif",m=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");function f(t){const n="primary"===(t.variant??"primary"),i=n?"#FFFFFF":e;return`\n <table role="presentation" cellpadding="0" cellspacing="0" style="margin:8px 8px 0 0;display:inline-block;">\n <tr>\n <td style="border-radius:10px;background:${n?e:l};border:1px solid ${n?e:d};">\n <a href="${m(function(t){try{const e=new URL(String(t));return"http:"===e.protocol||"https:"===e.protocol?e.href:"#"}catch{return"#"}}(t.url))}" target="_blank"\n style="display:inline-block;padding:12px 22px;font-family:${h};font-size:14px;font-weight:600;color:${i};text-decoration:none;border-radius:10px;">\n ${m(t.label)}\n </a>\n </td>\n </tr>\n </table>`}function b(t){const{preheader:e,greeting:d,title:b,paragraphs:$=[],highlights:y=[],aiParecer:u,buttons:F=[],footerNote:w}=t,v=y.length?Math.floor(100/y.length):100,E=y.length?`<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 4px;"><tr>${y.map(t=>function(t,e){return`\n <td width="${e}%" style="padding:6px;" valign="top">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="background:#FAFAFA;border:1px solid ${s};border-radius:12px;">\n <tr><td style="padding:14px 16px;font-family:${h};">\n <div style="font-size:12px;color:${a};margin-bottom:4px;">${m(t.label)}</div>\n <div style="font-size:22px;font-weight:700;color:${t.color||o};font-family:'Lexend',${h};">${m(t.value)}</div>\n </td></tr>\n </table>\n </td>`}(t,v)).join("")}</tr></table>`:"",k=$.map(t=>`<p style="margin:0 0 14px;font-family:${h};font-size:15px;line-height:1.6;color:${a};">${m(t)}</p>`).join(""),z=F.length?`<div style="margin-top:18px;">${F.map(f).join("")}</div>`:"";return`<!DOCTYPE html>\n<html lang="pt-BR">\n<head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1.0" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n <title>${m(b)}</title>\n</head>\n<body style="margin:0;padding:0;background:${r};">\n ${e?`<div style="display:none;max-height:0;overflow:hidden;opacity:0;">${m(e)}</div>`:""}\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${r};padding:24px 0;">\n <tr>\n <td align="center">\n <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px;max-width:92%;">\n <tr>\n <td style="padding:8px 4px 20px;">\n <img src="https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png" alt="GYRA+" height="30" style="height:30px;display:block;" />\n </td>\n </tr>\n <tr>\n <td style="background:${l};border:1px solid ${s};border-radius:16px;padding:32px;">\n ${d?`<p style="margin:0 0 6px;font-family:${h};font-size:14px;color:${a};">${m(d)}</p>`:""}\n <h1 style="margin:0 0 18px;font-family:'Lexend',${h};font-size:23px;line-height:1.3;color:${o};">${m(b)}</h1>\n ${k}\n ${E}\n ${u?function(t){const e=t.verdict?(()=>{const e=x[t.verdictTone||"info"];return`<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;"><tr>\n <td style="background:${e.bg};border-radius:999px;padding:4px 10px;font-family:${h};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${e.color};white-space:nowrap;">${m(t.verdict)}</td></tr></table>`})():"";return`\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="margin:18px 0 4px;background:${g};border:1px solid ${c};border-radius:12px;">\n <tr><td style="padding:16px 18px;font-family:${h};">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>\n <td valign="middle" width="28">\n <div style="width:22px;height:22px;border-radius:50%;background:${n};color:#fff;text-align:center;line-height:22px;font-size:13px;">✦</div>\n </td>\n <td valign="middle" style="padding-left:8px;">\n <span style="font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${i};">Parecer GYRA+IA</span>\n </td>\n <td valign="middle" align="right">${e}</td>\n </tr></table>\n <p style="margin:10px 0 0;font-size:14px;line-height:1.55;color:${a};">${m(t.text)}</p>\n ${t.footer?`<div style="margin-top:10px;padding-top:10px;border-top:1px dashed ${c};font-size:11px;color:${p};">${m(t.footer)}</div>`:""}\n </td></tr>\n </table>`}(u):""}\n ${z}\n </td>\n </tr>\n <tr>\n <td style="padding:20px 8px;font-family:${h};font-size:12px;line-height:1.5;color:${p};">\n ${w?`<p style="margin:0 0 8px;">${m(w)}</p>`:""}\n <p style="margin:0;">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`}const $=({width:e=700,height:n=1e3,...i})=>{const r=b(i);return t.createElement("iframe",{title:`Email preview — ${i.title}`,srcDoc:r,sandbox:"",style:{width:"number"==typeof e?`${e}px`:e,height:"number"==typeof n?`${n}px`:n,border:"1px solid #E8E8ED",borderRadius:8,background:"#F5F5F7"}})};export{$ as Email,$ as default};
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/emails/email/index.ts","../../../src/components/Email/Email.tsx"],"sourcesContent":["/**\n * Email transacional genérico — Gyra+ design system.\n *\n * **FONTE DA VERDADE** dos layouts de email transacional. Usado por\n * qualquer fluxo que precise mandar email no padrão visual Gyra+:\n * - reset de senha, confirmação de cadastro, alertas internos\n * - finalização de processamento (Finsights, batch-report, etc)\n * - notificações de operação/crédito\n * - convites, mudança de plano, alerts de billing...\n *\n * **TS puro**: sem React, sem DOM, sem browser globals. Apenas funções que\n * retornam string HTML. Funciona igual em qualquer runtime Node (Nest) ou\n * bundle browser (Next).\n *\n * **Email-safe**: tabelas + inline-style (clients ignoram <style> externo,\n * flexbox e Material Symbols). Gradientes têm fallback sólido.\n *\n * Tokens visuais alinhados com gyra-design: azul #1256FF (acao/primary),\n * roxo #7C4EFF (IA/parecer), neutrals Apple-gray, fonte Inter.\n *\n * **Como usar:**\n * ```ts\n * import { buildEmail } from '@gyramais/gyra-design/dist/emails/email';\n *\n * const html = buildEmail({\n * preheader: 'Senha redefinida com sucesso',\n * greeting: 'Olá, Maria',\n * title: 'Sua senha foi redefinida',\n * paragraphs: ['Tudo certo. Você já pode entrar com a nova senha.'],\n * buttons: [{ label: 'Entrar', url: 'https://app.gyramais.com/login' }],\n * footerNote: 'Se não foi você, redefina novamente clicando em \"Esqueci minha senha\".',\n * });\n * ```\n *\n * Pra emails recorrentes (mesmo conteudo, payload variavel), criar um\n * **preset** em `src/emails/<preset-name>/index.ts` que monta o\n * `EmailLayoutInput` a partir de campos de dominio e delega aqui. Ver\n * `balance-sheet-finalized` como exemplo.\n */\n\n// ── Tipos publicos ─────────────────────────────────────────────────────────\n\nexport type EmailButton = {\n label: string;\n url: string;\n /** primary = botão azul preenchido, secondary = outline. Default primary. */\n variant?: 'primary' | 'secondary';\n};\n\nexport type EmailHighlight = {\n /** label pequeno em cima (ex: \"Score\", \"Nivel de risco\"). */\n label: string;\n /** valor grande em baixo (ex: \"820\", \"Baixo\"). */\n value: string;\n /** cor do valor (ex: cor do risco). Default: tinta padrao Apple-gray. */\n color?: string;\n};\n\n/** Tom semantico do chip de veredito do bloco Parecer GYRA+IA. */\nexport type AiParecerTone = 'ok' | 'warn' | 'danger' | 'info';\n\nexport type EmailAiParecer = {\n /** chip de veredito (ex: \"Médio\", \"Consistente\"). Opcional. */\n verdict?: string;\n verdictTone?: AiParecerTone;\n /** corpo do parecer. */\n text: string;\n /** rodape do bloco (ex: \"GYRA+IA · Confiança 96%\"). Opcional. */\n footer?: string;\n};\n\nexport type EmailLayoutInput = {\n /** Texto invisivel no preheader (preview no inbox). */\n preheader?: string;\n /** Linha curta acima do titulo (ex: \"Olá, Maria\"). */\n greeting?: string;\n /** Titulo principal (H1). */\n title: string;\n /** Paragrafos de corpo (sub-frases). */\n paragraphs?: string[];\n /** Cards lado-a-lado (1 a N). Largura dividida igualmente. */\n highlights?: EmailHighlight[];\n /** Bloco \"Parecer GYRA+IA\" (faixa roxa com chip + texto + rodape). */\n aiParecer?: EmailAiParecer;\n /** Botoes de acao. Multiplos viram lado-a-lado. */\n buttons?: EmailButton[];\n /** Texto pequeno no rodape do card (disclaimer, instrucoes extras). */\n footerNote?: string;\n};\n\n// ── Tokens (espelho de gyra-design CSS variables, hard-coded pra email) ────\n\nconst C = {\n blue: '#1256FF',\n teal: '#1FB58A',\n aiPurple: '#7C4EFF',\n aiPurpleText: '#6635E0',\n bg: '#F5F5F7',\n card: '#FFFFFF',\n fg1: '#1D1D1F',\n fg2: '#636366',\n fg3: '#8E8E93',\n border: '#D2D2D7',\n borderSubtle: '#E8E8ED',\n aiCardBg: '#F6F4FF',\n aiBorder: 'rgba(124,78,255,0.22)',\n};\n\nconst VERDICT: Record<AiParecerTone, { bg: string; color: string }> = {\n ok: { bg: '#E8F9ED', color: '#1A7D37' },\n warn: { bg: '#FFF4E0', color: '#946200' },\n danger: { bg: '#FFEDEC', color: '#C93430' },\n info: { bg: '#EBF4FF', color: '#0055D4' },\n};\n\nconst FONT =\n \"'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif\";\n\nconst LOGO_URL =\n 'https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png';\n\n// ── Helpers internos ───────────────────────────────────────────────────────\n\nconst esc = (s: string): string =>\n String(s ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\n/**\n * Só permite http(s). Bloqueia schemes perigosos (javascript:, data:)\n * que poderiam virar href executável. URL inválida vira '#'.\n */\nfunction safeUrl(url: string): string {\n try {\n const u = new URL(String(url));\n return u.protocol === 'http:' || u.protocol === 'https:' ? u.href : '#';\n } catch {\n return '#';\n }\n}\n\nfunction renderButton(btn: EmailButton): string {\n const isPrimary = (btn.variant ?? 'primary') === 'primary';\n const bg = isPrimary ? C.blue : C.card;\n const color = isPrimary ? '#FFFFFF' : C.blue;\n const border = isPrimary ? C.blue : C.border;\n return `\n <table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 8px 0 0;display:inline-block;\">\n <tr>\n <td style=\"border-radius:10px;background:${bg};border:1px solid ${border};\">\n <a href=\"${esc(safeUrl(btn.url))}\" target=\"_blank\"\n style=\"display:inline-block;padding:12px 22px;font-family:${FONT};font-size:14px;font-weight:600;color:${color};text-decoration:none;border-radius:10px;\">\n ${esc(btn.label)}\n </a>\n </td>\n </tr>\n </table>`;\n}\n\nfunction renderHighlight(h: EmailHighlight, widthPct: number): string {\n return `\n <td width=\"${widthPct}%\" style=\"padding:6px;\" valign=\"top\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"background:#FAFAFA;border:1px solid ${C.borderSubtle};border-radius:12px;\">\n <tr><td style=\"padding:14px 16px;font-family:${FONT};\">\n <div style=\"font-size:12px;color:${C.fg2};margin-bottom:4px;\">${esc(h.label)}</div>\n <div style=\"font-size:22px;font-weight:700;color:${h.color || C.fg1};font-family:'Lexend',${FONT};\">${esc(h.value)}</div>\n </td></tr>\n </table>\n </td>`;\n}\n\nfunction renderAiParecer(p: EmailAiParecer): string {\n const v = p.verdict\n ? (() => {\n const t = VERDICT[p.verdictTone || 'info'];\n // table/td (Outlook ignora padding/border-radius em <span>).\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"display:inline-block;\"><tr>\n <td style=\"background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${FONT};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;\">${esc(\n p.verdict,\n )}</td></tr></table>`;\n })()\n : '';\n return `\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"margin:18px 0 4px;background:${C.aiCardBg};border:1px solid ${C.aiBorder};border-radius:12px;\">\n <tr><td style=\"padding:16px 18px;font-family:${FONT};\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n <td valign=\"middle\" width=\"28\">\n <div style=\"width:22px;height:22px;border-radius:50%;background:${C.aiPurple};color:#fff;text-align:center;line-height:22px;font-size:13px;\">✦</div>\n </td>\n <td valign=\"middle\" style=\"padding-left:8px;\">\n <span style=\"font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${C.aiPurpleText};\">Parecer GYRA+IA</span>\n </td>\n <td valign=\"middle\" align=\"right\">${v}</td>\n </tr></table>\n <p style=\"margin:10px 0 0;font-size:14px;line-height:1.55;color:${C.fg2};\">${esc(p.text)}</p>\n ${\n p.footer\n ? `<div style=\"margin-top:10px;padding-top:10px;border-top:1px dashed ${C.aiBorder};font-size:11px;color:${C.fg3};\">${esc(\n p.footer,\n )}</div>`\n : ''\n }\n </td></tr>\n </table>`;\n}\n\n// ── API publica ────────────────────────────────────────────────────────────\n\n/**\n * Monta o HTML completo de um email no padrão Gyra+: logo no topo, card\n * principal com greeting + título + parágrafos + highlights (chips lado a\n * lado) + bloco \"Parecer GYRA+IA\" + botões, e rodapé padrão.\n *\n * Email-safe (tabelas + inline-style, sem flexbox, sem <style>). Funciona\n * em Gmail, Outlook (Windows/Web/Mac), Apple Mail, Thunderbird, mobile.\n *\n * Todas as seções (greeting, paragraphs, highlights, aiParecer, buttons,\n * footerNote) são **opcionais** — só `title` é obrigatório.\n */\nexport function buildEmail(input: EmailLayoutInput): string {\n const {\n preheader,\n greeting,\n title,\n paragraphs = [],\n highlights = [],\n aiParecer,\n buttons = [],\n footerNote,\n } = input;\n\n const highlightWidth = highlights.length\n ? Math.floor(100 / highlights.length)\n : 100;\n const highlightsRow = highlights.length\n ? `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 4px;\"><tr>${highlights\n .map((h) => renderHighlight(h, highlightWidth))\n .join('')}</tr></table>`\n : '';\n\n const paragraphsHtml = paragraphs\n .map(\n (p) =>\n `<p style=\"margin:0 0 14px;font-family:${FONT};font-size:15px;line-height:1.6;color:${C.fg2};\">${esc(\n p,\n )}</p>`,\n )\n .join('');\n\n const buttonsHtml = buttons.length\n ? `<div style=\"margin-top:18px;\">${buttons.map(renderButton).join('')}</div>`\n : '';\n\n return `<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>${esc(title)}</title>\n</head>\n<body style=\"margin:0;padding:0;background:${C.bg};\">\n ${\n preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${esc(preheader)}</div>`\n : ''\n }\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:${C.bg};padding:24px 0;\">\n <tr>\n <td align=\"center\">\n <table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;max-width:92%;\">\n <tr>\n <td style=\"padding:8px 4px 20px;\">\n <img src=\"${LOGO_URL}\" alt=\"GYRA+\" height=\"30\" style=\"height:30px;display:block;\" />\n </td>\n </tr>\n <tr>\n <td style=\"background:${C.card};border:1px solid ${C.borderSubtle};border-radius:16px;padding:32px;\">\n ${\n greeting\n ? `<p style=\"margin:0 0 6px;font-family:${FONT};font-size:14px;color:${C.fg2};\">${esc(\n greeting,\n )}</p>`\n : ''\n }\n <h1 style=\"margin:0 0 18px;font-family:'Lexend',${FONT};font-size:23px;line-height:1.3;color:${C.fg1};\">${esc(\n title,\n )}</h1>\n ${paragraphsHtml}\n ${highlightsRow}\n ${aiParecer ? renderAiParecer(aiParecer) : ''}\n ${buttonsHtml}\n </td>\n </tr>\n <tr>\n <td style=\"padding:20px 8px;font-family:${FONT};font-size:12px;line-height:1.5;color:${C.fg3};\">\n ${\n footerNote\n ? `<p style=\"margin:0 0 8px;\">${esc(footerNote)}</p>`\n : ''\n }\n <p style=\"margin:0;\">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`;\n}\n","import React from 'react';\nimport { buildEmail, EmailLayoutInput } from '../../emails/email';\n\n/**\n * Preview de email transacional Gyra+ — genérico, configurável.\n *\n * Renderiza o HTML produzido por `buildEmail(input)` dentro de um iframe\n * com `sandbox=\"\"` pra isolar do CSS do Storybook e simular Gmail/Outlook\n * com fidelidade.\n *\n * **Uso**: passe qualquer combinação de campos opcionais (greeting, title\n * obrigatório, paragraphs, highlights lado-a-lado, aiParecer, buttons,\n * footerNote). É o mesmo `EmailLayoutInput` que o `buildEmail` aceita.\n *\n * Pra previsualizar um email transacional específico (ex: reset de senha,\n * batch concluído, alerta), basta montar os props correspondentes — não\n * precisa criar um componente novo por caso.\n */\nexport type EmailProps = EmailLayoutInput & {\n /** Largura visual do iframe. Default 700px (cabe o card 600 + paddings). */\n width?: number | string;\n /** Altura visual do iframe. Default 1000px. Ajuste se o conteúdo crescer. */\n height?: number | string;\n};\n\nexport const Email: React.FC<EmailProps> = ({\n width = 700,\n height = 1000,\n ...input\n}) => {\n const html = buildEmail(input);\n return (\n <iframe\n title={`Email preview — ${input.title}`}\n srcDoc={html}\n sandbox=\"\"\n style={{\n width: typeof width === 'number' ? `${width}px` : width,\n height: typeof height === 'number' ? `${height}px` : height,\n border: '1px solid #E8E8ED',\n borderRadius: 8,\n background: '#F5F5F7',\n }}\n />\n );\n};\n\nexport default Email;\n"],"names":["C","VERDICT","ok","bg","color","warn","danger","info","FONT","esc","s","String","replace","renderButton","btn","isPrimary","variant","url","u","URL","protocol","href","safeUrl","label","buildEmail","input","preheader","greeting","title","paragraphs","highlights","aiParecer","buttons","footerNote","highlightWidth","length","Math","floor","highlightsRow","map","h","widthPct","value","renderHighlight","join","paragraphsHtml","p","buttonsHtml","v","verdict","t","verdictTone","text","footer","renderAiParecer","Email","width","height","html","React","createElement","srcDoc","sandbox","style","border","borderRadius","background"],"mappings":"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"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { BalanceSheetFinalizedPayload } from '../../emails/balance-sheet-finalized';
|
|
3
|
-
/**
|
|
4
|
-
* Preview do email "Análise financeira concluída" (Finsights / CU-86aherfzc).
|
|
5
|
-
*
|
|
6
|
-
* Renderiza o HTML do template REAL (importado de
|
|
7
|
-
* `gyra-design/src/emails/balance-sheet-finalized`) dentro de um iframe.
|
|
8
|
-
* O mesmo módulo é consumido pelo `gyra-communication` pra disparar via
|
|
9
|
-
* SendGrid — fonte da verdade unificada.
|
|
10
|
-
*/
|
|
11
|
-
export type EmailBalanceSheetFinalizedProps = BalanceSheetFinalizedPayload & {
|
|
12
|
-
/** Largura visual do iframe. Default 700px (cabe o card 600 + paddings). */
|
|
13
|
-
width?: number | string;
|
|
14
|
-
/** Altura visual do iframe. Default 1100px (cobre todos os blocos). */
|
|
15
|
-
height?: number | string;
|
|
16
|
-
};
|
|
17
|
-
export declare const EmailBalanceSheetFinalized: React.FC<EmailBalanceSheetFinalizedProps>;
|
|
18
|
-
export default EmailBalanceSheetFinalized;
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use client";import e from"react";const t="#1256FF",n="#7C4EFF",i="#6635E0",a="#F5F5F7",r="#FFFFFF",o="#1D1D1F",l="#636366",d="#8E8E93",p="#D2D2D7",s="#E8E8ED",c="#F6F4FF",g="rgba(124,78,255,0.22)",h={ok:{bg:"#E8F9ED",color:"#1A7D37"},warn:{bg:"#FFF4E0",color:"#946200"},danger:{bg:"#FFEDEC",color:"#C93430"},info:{bg:"#EBF4FF",color:"#0055D4"}},x="'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif",m=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");function u(e){const n="primary"===(e.variant??"primary"),i=n?"#FFFFFF":t;return`\n <table role="presentation" cellpadding="0" cellspacing="0" style="margin:8px 8px 0 0;display:inline-block;">\n <tr>\n <td style="border-radius:10px;background:${n?t:r};border:1px solid ${n?t:p};">\n <a href="${m(function(e){try{const t=new URL(String(e));return"http:"===t.protocol||"https:"===t.protocol?t.href:"#"}catch{return"#"}}(e.url))}" target="_blank"\n style="display:inline-block;padding:12px 22px;font-family:${x};font-size:14px;font-weight:600;color:${i};text-decoration:none;border-radius:10px;">\n ${m(e.label)}\n </a>\n </td>\n </tr>\n </table>`}function f(e){const{preheader:t,greeting:p,title:f,paragraphs:b=[],highlights:$=[],aiParecer:y,buttons:v=[],footerNote:F}=e,w=$.length?Math.floor(100/$.length):100,E=$.length?`<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 4px;"><tr>${$.map(e=>function(e,t){return`\n <td width="${t}%" style="padding:6px;" valign="top">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="background:#FAFAFA;border:1px solid ${s};border-radius:12px;">\n <tr><td style="padding:14px 16px;font-family:${x};">\n <div style="font-size:12px;color:${l};margin-bottom:4px;">${m(e.label)}</div>\n <div style="font-size:22px;font-weight:700;color:${e.color||o};font-family:'Lexend',${x};">${m(e.value)}</div>\n </td></tr>\n </table>\n </td>`}(e,w)).join("")}</tr></table>`:"",A=b.map(e=>`<p style="margin:0 0 14px;font-family:${x};font-size:15px;line-height:1.6;color:${l};">${m(e)}</p>`).join(""),k=v.length?`<div style="margin-top:18px;">${v.map(u).join("")}</div>`:"";return`<!DOCTYPE html>\n<html lang="pt-BR">\n<head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1.0" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n <title>${m(f)}</title>\n</head>\n<body style="margin:0;padding:0;background:${a};">\n ${t?`<div style="display:none;max-height:0;overflow:hidden;opacity:0;">${m(t)}</div>`:""}\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${a};padding:24px 0;">\n <tr>\n <td align="center">\n <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px;max-width:92%;">\n <tr>\n <td style="padding:8px 4px 20px;">\n <img src="https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png" alt="GYRA+" height="30" style="height:30px;display:block;" />\n </td>\n </tr>\n <tr>\n <td style="background:${r};border:1px solid ${s};border-radius:16px;padding:32px;">\n ${p?`<p style="margin:0 0 6px;font-family:${x};font-size:14px;color:${l};">${m(p)}</p>`:""}\n <h1 style="margin:0 0 18px;font-family:'Lexend',${x};font-size:23px;line-height:1.3;color:${o};">${m(f)}</h1>\n ${A}\n ${E}\n ${y?function(e){const t=e.verdict?(()=>{const t=h[e.verdictTone||"info"];return`<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;"><tr>\n <td style="background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${x};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;">${m(e.verdict)}</td></tr></table>`})():"";return`\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="margin:18px 0 4px;background:${c};border:1px solid ${g};border-radius:12px;">\n <tr><td style="padding:16px 18px;font-family:${x};">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>\n <td valign="middle" width="28">\n <div style="width:22px;height:22px;border-radius:50%;background:${n};color:#fff;text-align:center;line-height:22px;font-size:13px;">✦</div>\n </td>\n <td valign="middle" style="padding-left:8px;">\n <span style="font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${i};">Parecer GYRA+IA</span>\n </td>\n <td valign="middle" align="right">${t}</td>\n </tr></table>\n <p style="margin:10px 0 0;font-size:14px;line-height:1.55;color:${l};">${m(e.text)}</p>\n ${e.footer?`<div style="margin-top:10px;padding-top:10px;border-top:1px dashed ${g};font-size:11px;color:${d};">${m(e.footer)}</div>`:""}\n </td></tr>\n </table>`}(y):""}\n ${k}\n </td>\n </tr>\n <tr>\n <td style="padding:20px 8px;font-family:${x};font-size:12px;line-height:1.5;color:${d};">\n <p style="margin:0 0 8px;">${m(F)}</p>\n <p style="margin:0;">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`}function b(e){const t=(e||"").toLowerCase();return t.includes("muito")&&t.includes("alto")?"#C93430":t.includes("alto")||t.includes("medio")||t.includes("médio")?"#946200":t.includes("baixo")?"#1A7D37":"#1D1D1F"}function $(e){const t=(e||"").toLowerCase();return t.includes("alto")?"danger":t.includes("medio")||t.includes("médio")?"warn":t.includes("baixo")?"ok":"info"}const y=({width:t=700,height:n=1100,...i})=>{const{html:a}=function(e){const{firstName:t,companyName:n,score:i,risk:a,recommendation:r,xlsxUrl:o,reportUrl:l}=e,d=n||"a empresa analisada",p="Análise financeira concluída"+(n?` — ${n}`:""),s=[];null!=i&&""!=`${i}`&&s.push({label:"Score GYRA+IA",value:`${i}`}),a&&s.push({label:"Nível de risco",value:a,color:b(a)});const c=[];return o&&c.push({label:"Baixar análise (Excel)",url:o,variant:"primary"}),l&&c.push({label:"Ver relatório",url:l,variant:"secondary"}),{subject:p,html:f({preheader:p,greeting:t?`Olá, ${t}`:void 0,title:"Sua análise financeira está pronta",paragraphs:[`A análise financeira de ${d} foi processada com sucesso pela GYRA+IA.`],highlights:s,aiParecer:r?{verdict:a||void 0,verdictTone:$(a),text:r,footer:"GYRA+IA · gerado a partir do balanço enviado"}:void 0,buttons:c,footerNote:"Os valores e a recomendação são gerados por IA a partir do balanço enviado e devem ser revisados pelo analista."})}}(i);return e.createElement("iframe",{title:"Email preview — Análise financeira concluída",srcDoc:a,sandbox:"",style:{width:"number"==typeof t?`${t}px`:t,height:"number"==typeof n?`${n}px`:n,border:"1px solid #E8E8ED",borderRadius:8,background:"#F5F5F7"}})};export{y as EmailBalanceSheetFinalized,y as default};
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/emails/balance-sheet-finalized/index.ts","../../../src/components/EmailBalanceSheetFinalized/EmailBalanceSheetFinalized.tsx"],"sourcesContent":["/**\n * Template do email transacional \"Análise financeira concluída\" enviado\n * quando o gyra-ai finaliza a extração de um balanço (feature Finsights —\n * card CU-86aherfzc).\n *\n * **Esta é a FONTE DA VERDADE.** Foi extraído do `gyra-communication`\n * pra cá pra ser reutilizável: o gyra-communication importa a função\n * `balanceSheetFinalizedEmail` pra disparar via SendGrid, e o componente\n * Storybook do gyra-design (`EmailBalanceSheetFinalized.tsx`) chama a mesma\n * função pra renderizar preview num iframe.\n *\n * **TS puro**: sem React, sem DOM, sem browser globals. Apenas funções que\n * retornam string HTML. Funciona igual em qualquer runtime Node (Nest) ou\n * bundle browser (Next).\n *\n * **Email-safe**: tabelas + inline-style (clients ignoram <style> externo,\n * flexbox e Material Symbols). Gradientes têm fallback sólido.\n *\n * Tokens visuais alinhados com gyra-design: azul #1256FF (acao/primary),\n * roxo #7C4EFF (IA/parecer), neutrals Apple-gray, fonte Inter.\n */\n\n// ── Tipos públicos ─────────────────────────────────────────────────────────\n\nexport type EmailButton = {\n label: string;\n url: string;\n variant?: 'primary' | 'secondary';\n};\n\nexport type EmailHighlight = {\n label: string;\n value: string;\n /** cor do valor (ex: risco). Default: tinta padrao. */\n color?: string;\n};\n\nexport type AiParecerTone = 'ok' | 'warn' | 'danger' | 'info';\n\nexport type EmailAiParecer = {\n /** chip de veredito (ex: \"Médio\", \"Consistente\"). */\n verdict?: string;\n verdictTone?: AiParecerTone;\n /** corpo do parecer. */\n text: string;\n /** rodape (ex: \"GYRA+IA · Confiança 96%\"). */\n footer?: string;\n};\n\nexport type EmailLayoutInput = {\n preheader?: string;\n greeting?: string;\n title: string;\n paragraphs?: string[];\n highlights?: EmailHighlight[];\n /** Faixa \"Parecer de IA\" (gradiente roxo→azul). */\n aiParecer?: EmailAiParecer;\n buttons?: EmailButton[];\n footerNote?: string;\n};\n\nexport type BalanceSheetFinalizedPayload = {\n firstName?: string;\n companyName?: string;\n score?: number | string;\n risk?: string;\n recommendation?: string;\n xlsxUrl?: string;\n reportUrl?: string;\n};\n\n// ── Tokens (espelho de gyra-design CSS variables, hard-coded pra email) ────\n\nconst C = {\n blue: '#1256FF',\n teal: '#1FB58A',\n aiPurple: '#7C4EFF',\n aiPurpleText: '#6635E0',\n bg: '#F5F5F7',\n card: '#FFFFFF',\n fg1: '#1D1D1F',\n fg2: '#636366',\n fg3: '#8E8E93',\n border: '#D2D2D7',\n borderSubtle: '#E8E8ED',\n aiCardBg: '#F6F4FF', // flatten do gradiente roxo (fallback Outlook)\n aiBorder: 'rgba(124,78,255,0.22)',\n};\n\nconst VERDICT: Record<AiParecerTone, { bg: string; color: string }> = {\n ok: { bg: '#E8F9ED', color: '#1A7D37' },\n warn: { bg: '#FFF4E0', color: '#946200' },\n danger: { bg: '#FFEDEC', color: '#C93430' },\n info: { bg: '#EBF4FF', color: '#0055D4' },\n};\n\nconst FONT =\n \"'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif\";\n\nconst LOGO_URL =\n 'https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png';\n\n// ── Helpers ────────────────────────────────────────────────────────────────\n\nconst esc = (s: string): string =>\n String(s ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\n/**\n * Só permite http(s). Bloqueia schemes perigosos (javascript:, data:)\n * que poderiam virar href executável. URL inválida vira '#'.\n */\nfunction safeUrl(url: string): string {\n try {\n const u = new URL(String(url));\n return u.protocol === 'http:' || u.protocol === 'https:' ? u.href : '#';\n } catch {\n return '#';\n }\n}\n\nfunction renderButton(btn: EmailButton): string {\n const isPrimary = (btn.variant ?? 'primary') === 'primary';\n const bg = isPrimary ? C.blue : C.card;\n const color = isPrimary ? '#FFFFFF' : C.blue;\n const border = isPrimary ? C.blue : C.border;\n return `\n <table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 8px 0 0;display:inline-block;\">\n <tr>\n <td style=\"border-radius:10px;background:${bg};border:1px solid ${border};\">\n <a href=\"${esc(safeUrl(btn.url))}\" target=\"_blank\"\n style=\"display:inline-block;padding:12px 22px;font-family:${FONT};font-size:14px;font-weight:600;color:${color};text-decoration:none;border-radius:10px;\">\n ${esc(btn.label)}\n </a>\n </td>\n </tr>\n </table>`;\n}\n\nfunction renderHighlight(h: EmailHighlight, widthPct: number): string {\n return `\n <td width=\"${widthPct}%\" style=\"padding:6px;\" valign=\"top\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"background:#FAFAFA;border:1px solid ${C.borderSubtle};border-radius:12px;\">\n <tr><td style=\"padding:14px 16px;font-family:${FONT};\">\n <div style=\"font-size:12px;color:${C.fg2};margin-bottom:4px;\">${esc(h.label)}</div>\n <div style=\"font-size:22px;font-weight:700;color:${h.color || C.fg1};font-family:'Lexend',${FONT};\">${esc(h.value)}</div>\n </td></tr>\n </table>\n </td>`;\n}\n\nfunction renderAiParecer(p: EmailAiParecer): string {\n const v = p.verdict\n ? (() => {\n const t = VERDICT[p.verdictTone || 'info'];\n // table/td (Outlook ignora padding/border-radius em <span>).\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"display:inline-block;\"><tr>\n <td style=\"background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${FONT};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;\">${esc(\n p.verdict,\n )}</td></tr></table>`;\n })()\n : '';\n return `\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"margin:18px 0 4px;background:${C.aiCardBg};border:1px solid ${C.aiBorder};border-radius:12px;\">\n <tr><td style=\"padding:16px 18px;font-family:${FONT};\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n <td valign=\"middle\" width=\"28\">\n <div style=\"width:22px;height:22px;border-radius:50%;background:${C.aiPurple};color:#fff;text-align:center;line-height:22px;font-size:13px;\">✦</div>\n </td>\n <td valign=\"middle\" style=\"padding-left:8px;\">\n <span style=\"font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${C.aiPurpleText};\">Parecer GYRA+IA</span>\n </td>\n <td valign=\"middle\" align=\"right\">${v}</td>\n </tr></table>\n <p style=\"margin:10px 0 0;font-size:14px;line-height:1.55;color:${C.fg2};\">${esc(p.text)}</p>\n ${\n p.footer\n ? `<div style=\"margin-top:10px;padding-top:10px;border-top:1px dashed ${C.aiBorder};font-size:11px;color:${C.fg3};\">${esc(\n p.footer,\n )}</div>`\n : ''\n }\n </td></tr>\n </table>`;\n}\n\n// ── API pública ────────────────────────────────────────────────────────────\n\n/**\n * Monta o HTML completo de um email no padrão Gyra+: logo, card principal,\n * highlights opcionais (chips), bloco \"Parecer GYRA+IA\" opcional, botões\n * e rodapé padrão. Email-safe (tabelas + inline-style).\n */\nexport function buildEmail(input: EmailLayoutInput): string {\n const {\n preheader,\n greeting,\n title,\n paragraphs = [],\n highlights = [],\n aiParecer,\n buttons = [],\n footerNote,\n } = input;\n\n const highlightWidth = highlights.length\n ? Math.floor(100 / highlights.length)\n : 100;\n const highlightsRow = highlights.length\n ? `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 4px;\"><tr>${highlights\n .map((h) => renderHighlight(h, highlightWidth))\n .join('')}</tr></table>`\n : '';\n\n const paragraphsHtml = paragraphs\n .map(\n (p) =>\n `<p style=\"margin:0 0 14px;font-family:${FONT};font-size:15px;line-height:1.6;color:${C.fg2};\">${esc(\n p,\n )}</p>`,\n )\n .join('');\n\n const buttonsHtml = buttons.length\n ? `<div style=\"margin-top:18px;\">${buttons.map(renderButton).join('')}</div>`\n : '';\n\n return `<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>${esc(title)}</title>\n</head>\n<body style=\"margin:0;padding:0;background:${C.bg};\">\n ${\n preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${esc(preheader)}</div>`\n : ''\n }\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:${C.bg};padding:24px 0;\">\n <tr>\n <td align=\"center\">\n <table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;max-width:92%;\">\n <tr>\n <td style=\"padding:8px 4px 20px;\">\n <img src=\"${LOGO_URL}\" alt=\"GYRA+\" height=\"30\" style=\"height:30px;display:block;\" />\n </td>\n </tr>\n <tr>\n <td style=\"background:${C.card};border:1px solid ${C.borderSubtle};border-radius:16px;padding:32px;\">\n ${\n greeting\n ? `<p style=\"margin:0 0 6px;font-family:${FONT};font-size:14px;color:${C.fg2};\">${esc(\n greeting,\n )}</p>`\n : ''\n }\n <h1 style=\"margin:0 0 18px;font-family:'Lexend',${FONT};font-size:23px;line-height:1.3;color:${C.fg1};\">${esc(\n title,\n )}</h1>\n ${paragraphsHtml}\n ${highlightsRow}\n ${aiParecer ? renderAiParecer(aiParecer) : ''}\n ${buttonsHtml}\n </td>\n </tr>\n <tr>\n <td style=\"padding:20px 8px;font-family:${FONT};font-size:12px;line-height:1.5;color:${C.fg3};\">\n ${\n footerNote\n ? `<p style=\"margin:0 0 8px;\">${esc(footerNote)}</p>`\n : ''\n }\n <p style=\"margin:0;\">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`;\n}\n\n/** Mapeia nível de risco textual -> cor do chip de score. */\nfunction riskColor(risk?: string): string {\n const r = (risk || '').toLowerCase();\n if (r.includes('muito') && r.includes('alto')) return '#C93430';\n if (r.includes('alto')) return '#946200';\n if (r.includes('medio') || r.includes('médio')) return '#946200';\n if (r.includes('baixo')) return '#1A7D37';\n return '#1D1D1F';\n}\n\n/** Risco -> tom do veredito do Parecer de IA. */\nfunction riskTone(risk?: string): AiParecerTone {\n const r = (risk || '').toLowerCase();\n if (r.includes('alto')) return 'danger';\n if (r.includes('medio') || r.includes('médio')) return 'warn';\n if (r.includes('baixo')) return 'ok';\n return 'info';\n}\n\n/**\n * Monta `{ subject, html }` do email \"Análise financeira concluída\" a\n * partir do payload do gyra-ai (score, risco, recomendação, XLS, etc).\n * Usado tanto pelo gyra-communication (envio via SendGrid) quanto pelo\n * componente Storybook do gyra-design (preview).\n */\nexport function balanceSheetFinalizedEmail(\n payload: BalanceSheetFinalizedPayload,\n): { subject: string; html: string } {\n const {\n firstName,\n companyName,\n score,\n risk,\n recommendation,\n xlsxUrl,\n reportUrl,\n } = payload;\n\n const empresa = companyName || 'a empresa analisada';\n const subject = `Análise financeira concluída${companyName ? ` — ${companyName}` : ''}`;\n\n const highlights: EmailHighlight[] = [];\n if (score !== undefined && score !== null && `${score}` !== '') {\n highlights.push({ label: 'Score GYRA+IA', value: `${score}` });\n }\n if (risk) {\n highlights.push({\n label: 'Nível de risco',\n value: risk,\n color: riskColor(risk),\n });\n }\n\n const buttons: EmailButton[] = [];\n if (xlsxUrl) {\n buttons.push({\n label: 'Baixar análise (Excel)',\n url: xlsxUrl,\n variant: 'primary',\n });\n }\n if (reportUrl) {\n buttons.push({\n label: 'Ver relatório',\n url: reportUrl,\n variant: 'secondary',\n });\n }\n\n const aiParecer: EmailAiParecer | undefined = recommendation\n ? {\n verdict: risk || undefined,\n verdictTone: riskTone(risk),\n text: recommendation,\n footer: 'GYRA+IA · gerado a partir do balanço enviado',\n }\n : undefined;\n\n const html = buildEmail({\n preheader: subject,\n greeting: firstName ? `Olá, ${firstName}` : undefined,\n title: 'Sua análise financeira está pronta',\n paragraphs: [\n `A análise financeira de ${empresa} foi processada com sucesso pela GYRA+IA.`,\n ],\n highlights,\n aiParecer,\n buttons,\n footerNote:\n 'Os valores e a recomendação são gerados por IA a partir do balanço enviado e devem ser revisados pelo analista.',\n });\n\n return { subject, html };\n}\n","import React from 'react';\nimport {\n balanceSheetFinalizedEmail,\n BalanceSheetFinalizedPayload,\n} from '../../emails/balance-sheet-finalized';\n\n/**\n * Preview do email \"Análise financeira concluída\" (Finsights / CU-86aherfzc).\n *\n * Renderiza o HTML do template REAL (importado de\n * `gyra-design/src/emails/balance-sheet-finalized`) dentro de um iframe.\n * O mesmo módulo é consumido pelo `gyra-communication` pra disparar via\n * SendGrid — fonte da verdade unificada.\n */\n\nexport type EmailBalanceSheetFinalizedProps = BalanceSheetFinalizedPayload & {\n /** Largura visual do iframe. Default 700px (cabe o card 600 + paddings). */\n width?: number | string;\n /** Altura visual do iframe. Default 1100px (cobre todos os blocos). */\n height?: number | string;\n};\n\nexport const EmailBalanceSheetFinalized: React.FC<\n EmailBalanceSheetFinalizedProps\n> = ({ width = 700, height = 1100, ...payload }) => {\n const { html } = balanceSheetFinalizedEmail(payload);\n return (\n <iframe\n title=\"Email preview — Análise financeira concluída\"\n srcDoc={html}\n sandbox=\"\"\n style={{\n width: typeof width === 'number' ? `${width}px` : width,\n height: typeof height === 'number' ? `${height}px` : height,\n border: '1px solid #E8E8ED',\n borderRadius: 8,\n background: '#F5F5F7',\n }}\n />\n );\n};\n\nexport default EmailBalanceSheetFinalized;\n"],"names":["C","VERDICT","ok","bg","color","warn","danger","info","FONT","esc","s","String","replace","renderButton","btn","isPrimary","variant","url","u","URL","protocol","href","safeUrl","label","buildEmail","input","preheader","greeting","title","paragraphs","highlights","aiParecer","buttons","footerNote","highlightWidth","length","Math","floor","highlightsRow","map","h","widthPct","value","renderHighlight","join","paragraphsHtml","p","buttonsHtml","v","verdict","t","verdictTone","text","footer","renderAiParecer","riskColor","risk","r","toLowerCase","includes","riskTone","EmailBalanceSheetFinalized","width","height","payload","html","firstName","companyName","score","recommendation","xlsxUrl","reportUrl","empresa","subject","push","undefined","balanceSheetFinalizedEmail","React","createElement","srcDoc","sandbox","style","border","borderRadius","background"],"mappings":"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"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Preset do email "Análise financeira concluída" enviado quando o gyra-ai
|
|
3
|
-
* finaliza a extração de um balanço (feature Finsights — CU-86aherfzc).
|
|
4
|
-
*
|
|
5
|
-
* Esta é uma **camada fina de mapeamento de domínio** sobre o
|
|
6
|
-
* `buildEmail` genérico: recebe os campos crus que o gyra-ai produz
|
|
7
|
-
* (score, risk, recommendation, xlsxUrl, ...) e monta o `EmailLayoutInput`
|
|
8
|
-
* com as decisões visuais (cor do risco, tom do veredito, textos padrão).
|
|
9
|
-
*
|
|
10
|
-
* Pra criar outro preset (ex: `password-reset`, `report-finalized`,
|
|
11
|
-
* `batch-finalized`), criar uma pasta nova em `src/emails/<name>/` que
|
|
12
|
-
* importe `buildEmail` e exporte uma função `<name>Email(payload)` com a
|
|
13
|
-
* mesma assinatura: `{ subject, html }`.
|
|
14
|
-
*/
|
|
15
|
-
export type BalanceSheetFinalizedPayload = {
|
|
16
|
-
firstName?: string;
|
|
17
|
-
companyName?: string;
|
|
18
|
-
score?: number | string;
|
|
19
|
-
risk?: string;
|
|
20
|
-
recommendation?: string;
|
|
21
|
-
xlsxUrl?: string;
|
|
22
|
-
reportUrl?: string;
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Monta `{ subject, html }` do email "Análise financeira concluída" a
|
|
26
|
-
* partir do payload do gyra-ai (score, risco, recomendação, XLS, etc).
|
|
27
|
-
* Usado pelo gyra-communication (envio via SendGrid) e pelo Storybook
|
|
28
|
-
* (preview).
|
|
29
|
-
*/
|
|
30
|
-
export declare function balanceSheetFinalizedEmail(payload: BalanceSheetFinalizedPayload): {
|
|
31
|
-
subject: string;
|
|
32
|
-
html: string;
|
|
33
|
-
};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e="#1256FF",t="#7C4EFF",n="#6635E0",a="#F5F5F7",i="#FFFFFF",o="#1D1D1F",r="#636366",l="#8E8E93",d="#D2D2D7",p="#E8E8ED",s="#F6F4FF",c="rgba(124,78,255,0.22)",g={ok:{bg:"#E8F9ED",color:"#1A7D37"},warn:{bg:"#FFF4E0",color:"#946200"},danger:{bg:"#FFEDEC",color:"#C93430"},info:{bg:"#EBF4FF",color:"#0055D4"}},x="'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif",h=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");function m(t){const n="primary"===(t.variant??"primary"),a=n?"#FFFFFF":e;return`\n <table role="presentation" cellpadding="0" cellspacing="0" style="margin:8px 8px 0 0;display:inline-block;">\n <tr>\n <td style="border-radius:10px;background:${n?e:i};border:1px solid ${n?e:d};">\n <a href="${h(function(e){try{const t=new URL(String(e));return"http:"===t.protocol||"https:"===t.protocol?t.href:"#"}catch{return"#"}}(t.url))}" target="_blank"\n style="display:inline-block;padding:12px 22px;font-family:${x};font-size:14px;font-weight:600;color:${a};text-decoration:none;border-radius:10px;">\n ${h(t.label)}\n </a>\n </td>\n </tr>\n </table>`}function u(e){const{preheader:d,greeting:u,title:f,paragraphs:b=[],highlights:$=[],aiParecer:y,buttons:v=[],footerNote:F}=e,w=$.length?Math.floor(100/$.length):100,A=$.length?`<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 4px;"><tr>${$.map(e=>function(e,t){return`\n <td width="${t}%" style="padding:6px;" valign="top">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="background:#FAFAFA;border:1px solid ${p};border-radius:12px;">\n <tr><td style="padding:14px 16px;font-family:${x};">\n <div style="font-size:12px;color:${r};margin-bottom:4px;">${h(e.label)}</div>\n <div style="font-size:22px;font-weight:700;color:${e.color||o};font-family:'Lexend',${x};">${h(e.value)}</div>\n </td></tr>\n </table>\n </td>`}(e,w)).join("")}</tr></table>`:"",k=b.map(e=>`<p style="margin:0 0 14px;font-family:${x};font-size:15px;line-height:1.6;color:${r};">${h(e)}</p>`).join(""),E=v.length?`<div style="margin-top:18px;">${v.map(m).join("")}</div>`:"";return`<!DOCTYPE html>\n<html lang="pt-BR">\n<head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1.0" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n <title>${h(f)}</title>\n</head>\n<body style="margin:0;padding:0;background:${a};">\n ${d?`<div style="display:none;max-height:0;overflow:hidden;opacity:0;">${h(d)}</div>`:""}\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${a};padding:24px 0;">\n <tr>\n <td align="center">\n <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px;max-width:92%;">\n <tr>\n <td style="padding:8px 4px 20px;">\n <img src="https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png" alt="GYRA+" height="30" style="height:30px;display:block;" />\n </td>\n </tr>\n <tr>\n <td style="background:${i};border:1px solid ${p};border-radius:16px;padding:32px;">\n ${u?`<p style="margin:0 0 6px;font-family:${x};font-size:14px;color:${r};">${h(u)}</p>`:""}\n <h1 style="margin:0 0 18px;font-family:'Lexend',${x};font-size:23px;line-height:1.3;color:${o};">${h(f)}</h1>\n ${k}\n ${A}\n ${y?function(e){const a=e.verdict?(()=>{const t=g[e.verdictTone||"info"];return`<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;"><tr>\n <td style="background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${x};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;">${h(e.verdict)}</td></tr></table>`})():"";return`\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="margin:18px 0 4px;background:${s};border:1px solid ${c};border-radius:12px;">\n <tr><td style="padding:16px 18px;font-family:${x};">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>\n <td valign="middle" width="28">\n <div style="width:22px;height:22px;border-radius:50%;background:${t};color:#fff;text-align:center;line-height:22px;font-size:13px;">✦</div>\n </td>\n <td valign="middle" style="padding-left:8px;">\n <span style="font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${n};">Parecer GYRA+IA</span>\n </td>\n <td valign="middle" align="right">${a}</td>\n </tr></table>\n <p style="margin:10px 0 0;font-size:14px;line-height:1.55;color:${r};">${h(e.text)}</p>\n ${e.footer?`<div style="margin-top:10px;padding-top:10px;border-top:1px dashed ${c};font-size:11px;color:${l};">${h(e.footer)}</div>`:""}\n </td></tr>\n </table>`}(y):""}\n ${E}\n </td>\n </tr>\n <tr>\n <td style="padding:20px 8px;font-family:${x};font-size:12px;line-height:1.5;color:${l};">\n <p style="margin:0 0 8px;">${h(F)}</p>\n <p style="margin:0;">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`}function f(e){const t=(e||"").toLowerCase();return t.includes("muito")&&t.includes("alto")?"#C93430":t.includes("alto")||t.includes("medio")||t.includes("médio")?"#946200":t.includes("baixo")?"#1A7D37":"#1D1D1F"}function b(e){const t=(e||"").toLowerCase();return t.includes("alto")?"danger":t.includes("medio")||t.includes("médio")?"warn":t.includes("baixo")?"ok":"info"}function $(e){const{firstName:t,companyName:n,score:a,risk:i,recommendation:o,xlsxUrl:r,reportUrl:l}=e,d=n||"a empresa analisada",p="Análise financeira concluída"+(n?` — ${n}`:""),s=[];null!=a&&""!=`${a}`&&s.push({label:"Score GYRA+IA",value:`${a}`}),i&&s.push({label:"Nível de risco",value:i,color:f(i)});const c=[];r&&c.push({label:"Baixar análise (Excel)",url:r,variant:"primary"}),l&&c.push({label:"Ver relatório",url:l,variant:"secondary"});return{subject:p,html:u({preheader:p,greeting:t?`Olá, ${t}`:void 0,title:"Sua análise financeira está pronta",paragraphs:[`A análise financeira de ${d} foi processada com sucesso pela GYRA+IA.`],highlights:s,aiParecer:o?{verdict:i||void 0,verdictTone:b(i),text:o,footer:"GYRA+IA · gerado a partir do balanço enviado"}:void 0,buttons:c,footerNote:"Os valores e a recomendação são gerados por IA a partir do balanço enviado e devem ser revisados pelo analista."})}}export{$ as balanceSheetFinalizedEmail};
|
|
2
|
-
//# sourceMappingURL=index.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../../../src/emails/email/index.ts","../../../src/emails/balance-sheet-finalized/index.ts"],"sourcesContent":["/**\n * Email transacional genérico — Gyra+ design system.\n *\n * **FONTE DA VERDADE** dos layouts de email transacional. Usado por\n * qualquer fluxo que precise mandar email no padrão visual Gyra+:\n * - reset de senha, confirmação de cadastro, alertas internos\n * - finalização de processamento (Finsights, batch-report, etc)\n * - notificações de operação/crédito\n * - convites, mudança de plano, alerts de billing...\n *\n * **TS puro**: sem React, sem DOM, sem browser globals. Apenas funções que\n * retornam string HTML. Funciona igual em qualquer runtime Node (Nest) ou\n * bundle browser (Next).\n *\n * **Email-safe**: tabelas + inline-style (clients ignoram <style> externo,\n * flexbox e Material Symbols). Gradientes têm fallback sólido.\n *\n * Tokens visuais alinhados com gyra-design: azul #1256FF (acao/primary),\n * roxo #7C4EFF (IA/parecer), neutrals Apple-gray, fonte Inter.\n *\n * **Como usar:**\n * ```ts\n * import { buildEmail } from '@gyramais/gyra-design/dist/emails/email';\n *\n * const html = buildEmail({\n * preheader: 'Senha redefinida com sucesso',\n * greeting: 'Olá, Maria',\n * title: 'Sua senha foi redefinida',\n * paragraphs: ['Tudo certo. Você já pode entrar com a nova senha.'],\n * buttons: [{ label: 'Entrar', url: 'https://app.gyramais.com/login' }],\n * footerNote: 'Se não foi você, redefina novamente clicando em \"Esqueci minha senha\".',\n * });\n * ```\n *\n * Pra emails recorrentes (mesmo conteudo, payload variavel), criar um\n * **preset** em `src/emails/<preset-name>/index.ts` que monta o\n * `EmailLayoutInput` a partir de campos de dominio e delega aqui. Ver\n * `balance-sheet-finalized` como exemplo.\n */\n\n// ── Tipos publicos ─────────────────────────────────────────────────────────\n\nexport type EmailButton = {\n label: string;\n url: string;\n /** primary = botão azul preenchido, secondary = outline. Default primary. */\n variant?: 'primary' | 'secondary';\n};\n\nexport type EmailHighlight = {\n /** label pequeno em cima (ex: \"Score\", \"Nivel de risco\"). */\n label: string;\n /** valor grande em baixo (ex: \"820\", \"Baixo\"). */\n value: string;\n /** cor do valor (ex: cor do risco). Default: tinta padrao Apple-gray. */\n color?: string;\n};\n\n/** Tom semantico do chip de veredito do bloco Parecer GYRA+IA. */\nexport type AiParecerTone = 'ok' | 'warn' | 'danger' | 'info';\n\nexport type EmailAiParecer = {\n /** chip de veredito (ex: \"Médio\", \"Consistente\"). Opcional. */\n verdict?: string;\n verdictTone?: AiParecerTone;\n /** corpo do parecer. */\n text: string;\n /** rodape do bloco (ex: \"GYRA+IA · Confiança 96%\"). Opcional. */\n footer?: string;\n};\n\nexport type EmailLayoutInput = {\n /** Texto invisivel no preheader (preview no inbox). */\n preheader?: string;\n /** Linha curta acima do titulo (ex: \"Olá, Maria\"). */\n greeting?: string;\n /** Titulo principal (H1). */\n title: string;\n /** Paragrafos de corpo (sub-frases). */\n paragraphs?: string[];\n /** Cards lado-a-lado (1 a N). Largura dividida igualmente. */\n highlights?: EmailHighlight[];\n /** Bloco \"Parecer GYRA+IA\" (faixa roxa com chip + texto + rodape). */\n aiParecer?: EmailAiParecer;\n /** Botoes de acao. Multiplos viram lado-a-lado. */\n buttons?: EmailButton[];\n /** Texto pequeno no rodape do card (disclaimer, instrucoes extras). */\n footerNote?: string;\n};\n\n// ── Tokens (espelho de gyra-design CSS variables, hard-coded pra email) ────\n\nconst C = {\n blue: '#1256FF',\n teal: '#1FB58A',\n aiPurple: '#7C4EFF',\n aiPurpleText: '#6635E0',\n bg: '#F5F5F7',\n card: '#FFFFFF',\n fg1: '#1D1D1F',\n fg2: '#636366',\n fg3: '#8E8E93',\n border: '#D2D2D7',\n borderSubtle: '#E8E8ED',\n aiCardBg: '#F6F4FF',\n aiBorder: 'rgba(124,78,255,0.22)',\n};\n\nconst VERDICT: Record<AiParecerTone, { bg: string; color: string }> = {\n ok: { bg: '#E8F9ED', color: '#1A7D37' },\n warn: { bg: '#FFF4E0', color: '#946200' },\n danger: { bg: '#FFEDEC', color: '#C93430' },\n info: { bg: '#EBF4FF', color: '#0055D4' },\n};\n\nconst FONT =\n \"'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif\";\n\nconst LOGO_URL =\n 'https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png';\n\n// ── Helpers internos ───────────────────────────────────────────────────────\n\nconst esc = (s: string): string =>\n String(s ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\n/**\n * Só permite http(s). Bloqueia schemes perigosos (javascript:, data:)\n * que poderiam virar href executável. URL inválida vira '#'.\n */\nfunction safeUrl(url: string): string {\n try {\n const u = new URL(String(url));\n return u.protocol === 'http:' || u.protocol === 'https:' ? u.href : '#';\n } catch {\n return '#';\n }\n}\n\nfunction renderButton(btn: EmailButton): string {\n const isPrimary = (btn.variant ?? 'primary') === 'primary';\n const bg = isPrimary ? C.blue : C.card;\n const color = isPrimary ? '#FFFFFF' : C.blue;\n const border = isPrimary ? C.blue : C.border;\n return `\n <table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 8px 0 0;display:inline-block;\">\n <tr>\n <td style=\"border-radius:10px;background:${bg};border:1px solid ${border};\">\n <a href=\"${esc(safeUrl(btn.url))}\" target=\"_blank\"\n style=\"display:inline-block;padding:12px 22px;font-family:${FONT};font-size:14px;font-weight:600;color:${color};text-decoration:none;border-radius:10px;\">\n ${esc(btn.label)}\n </a>\n </td>\n </tr>\n </table>`;\n}\n\nfunction renderHighlight(h: EmailHighlight, widthPct: number): string {\n return `\n <td width=\"${widthPct}%\" style=\"padding:6px;\" valign=\"top\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"background:#FAFAFA;border:1px solid ${C.borderSubtle};border-radius:12px;\">\n <tr><td style=\"padding:14px 16px;font-family:${FONT};\">\n <div style=\"font-size:12px;color:${C.fg2};margin-bottom:4px;\">${esc(h.label)}</div>\n <div style=\"font-size:22px;font-weight:700;color:${h.color || C.fg1};font-family:'Lexend',${FONT};\">${esc(h.value)}</div>\n </td></tr>\n </table>\n </td>`;\n}\n\nfunction renderAiParecer(p: EmailAiParecer): string {\n const v = p.verdict\n ? (() => {\n const t = VERDICT[p.verdictTone || 'info'];\n // table/td (Outlook ignora padding/border-radius em <span>).\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"display:inline-block;\"><tr>\n <td style=\"background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${FONT};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;\">${esc(\n p.verdict,\n )}</td></tr></table>`;\n })()\n : '';\n return `\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"margin:18px 0 4px;background:${C.aiCardBg};border:1px solid ${C.aiBorder};border-radius:12px;\">\n <tr><td style=\"padding:16px 18px;font-family:${FONT};\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n <td valign=\"middle\" width=\"28\">\n <div style=\"width:22px;height:22px;border-radius:50%;background:${C.aiPurple};color:#fff;text-align:center;line-height:22px;font-size:13px;\">✦</div>\n </td>\n <td valign=\"middle\" style=\"padding-left:8px;\">\n <span style=\"font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${C.aiPurpleText};\">Parecer GYRA+IA</span>\n </td>\n <td valign=\"middle\" align=\"right\">${v}</td>\n </tr></table>\n <p style=\"margin:10px 0 0;font-size:14px;line-height:1.55;color:${C.fg2};\">${esc(p.text)}</p>\n ${\n p.footer\n ? `<div style=\"margin-top:10px;padding-top:10px;border-top:1px dashed ${C.aiBorder};font-size:11px;color:${C.fg3};\">${esc(\n p.footer,\n )}</div>`\n : ''\n }\n </td></tr>\n </table>`;\n}\n\n// ── API publica ────────────────────────────────────────────────────────────\n\n/**\n * Monta o HTML completo de um email no padrão Gyra+: logo no topo, card\n * principal com greeting + título + parágrafos + highlights (chips lado a\n * lado) + bloco \"Parecer GYRA+IA\" + botões, e rodapé padrão.\n *\n * Email-safe (tabelas + inline-style, sem flexbox, sem <style>). Funciona\n * em Gmail, Outlook (Windows/Web/Mac), Apple Mail, Thunderbird, mobile.\n *\n * Todas as seções (greeting, paragraphs, highlights, aiParecer, buttons,\n * footerNote) são **opcionais** — só `title` é obrigatório.\n */\nexport function buildEmail(input: EmailLayoutInput): string {\n const {\n preheader,\n greeting,\n title,\n paragraphs = [],\n highlights = [],\n aiParecer,\n buttons = [],\n footerNote,\n } = input;\n\n const highlightWidth = highlights.length\n ? Math.floor(100 / highlights.length)\n : 100;\n const highlightsRow = highlights.length\n ? `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 4px;\"><tr>${highlights\n .map((h) => renderHighlight(h, highlightWidth))\n .join('')}</tr></table>`\n : '';\n\n const paragraphsHtml = paragraphs\n .map(\n (p) =>\n `<p style=\"margin:0 0 14px;font-family:${FONT};font-size:15px;line-height:1.6;color:${C.fg2};\">${esc(\n p,\n )}</p>`,\n )\n .join('');\n\n const buttonsHtml = buttons.length\n ? `<div style=\"margin-top:18px;\">${buttons.map(renderButton).join('')}</div>`\n : '';\n\n return `<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>${esc(title)}</title>\n</head>\n<body style=\"margin:0;padding:0;background:${C.bg};\">\n ${\n preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${esc(preheader)}</div>`\n : ''\n }\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:${C.bg};padding:24px 0;\">\n <tr>\n <td align=\"center\">\n <table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;max-width:92%;\">\n <tr>\n <td style=\"padding:8px 4px 20px;\">\n <img src=\"${LOGO_URL}\" alt=\"GYRA+\" height=\"30\" style=\"height:30px;display:block;\" />\n </td>\n </tr>\n <tr>\n <td style=\"background:${C.card};border:1px solid ${C.borderSubtle};border-radius:16px;padding:32px;\">\n ${\n greeting\n ? `<p style=\"margin:0 0 6px;font-family:${FONT};font-size:14px;color:${C.fg2};\">${esc(\n greeting,\n )}</p>`\n : ''\n }\n <h1 style=\"margin:0 0 18px;font-family:'Lexend',${FONT};font-size:23px;line-height:1.3;color:${C.fg1};\">${esc(\n title,\n )}</h1>\n ${paragraphsHtml}\n ${highlightsRow}\n ${aiParecer ? renderAiParecer(aiParecer) : ''}\n ${buttonsHtml}\n </td>\n </tr>\n <tr>\n <td style=\"padding:20px 8px;font-family:${FONT};font-size:12px;line-height:1.5;color:${C.fg3};\">\n ${\n footerNote\n ? `<p style=\"margin:0 0 8px;\">${esc(footerNote)}</p>`\n : ''\n }\n <p style=\"margin:0;\">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`;\n}\n","/**\n * Preset do email \"Análise financeira concluída\" enviado quando o gyra-ai\n * finaliza a extração de um balanço (feature Finsights — CU-86aherfzc).\n *\n * Esta é uma **camada fina de mapeamento de domínio** sobre o\n * `buildEmail` genérico: recebe os campos crus que o gyra-ai produz\n * (score, risk, recommendation, xlsxUrl, ...) e monta o `EmailLayoutInput`\n * com as decisões visuais (cor do risco, tom do veredito, textos padrão).\n *\n * Pra criar outro preset (ex: `password-reset`, `report-finalized`,\n * `batch-finalized`), criar uma pasta nova em `src/emails/<name>/` que\n * importe `buildEmail` e exporte uma função `<name>Email(payload)` com a\n * mesma assinatura: `{ subject, html }`.\n */\n\nimport { buildEmail, AiParecerTone, EmailButton, EmailHighlight } from '../email';\n\nexport type BalanceSheetFinalizedPayload = {\n firstName?: string;\n companyName?: string;\n score?: number | string;\n risk?: string;\n recommendation?: string;\n xlsxUrl?: string;\n reportUrl?: string;\n};\n\n/** Mapeia nível de risco textual -> cor do chip de score. */\nfunction riskColor(risk?: string): string {\n const r = (risk || '').toLowerCase();\n if (r.includes('muito') && r.includes('alto')) return '#C93430';\n if (r.includes('alto')) return '#946200';\n if (r.includes('medio') || r.includes('médio')) return '#946200';\n if (r.includes('baixo')) return '#1A7D37';\n return '#1D1D1F';\n}\n\n/** Risco -> tom do veredito do Parecer de IA. */\nfunction riskTone(risk?: string): AiParecerTone {\n const r = (risk || '').toLowerCase();\n if (r.includes('alto')) return 'danger';\n if (r.includes('medio') || r.includes('médio')) return 'warn';\n if (r.includes('baixo')) return 'ok';\n return 'info';\n}\n\n/**\n * Monta `{ subject, html }` do email \"Análise financeira concluída\" a\n * partir do payload do gyra-ai (score, risco, recomendação, XLS, etc).\n * Usado pelo gyra-communication (envio via SendGrid) e pelo Storybook\n * (preview).\n */\nexport function balanceSheetFinalizedEmail(\n payload: BalanceSheetFinalizedPayload,\n): { subject: string; html: string } {\n const {\n firstName,\n companyName,\n score,\n risk,\n recommendation,\n xlsxUrl,\n reportUrl,\n } = payload;\n\n const empresa = companyName || 'a empresa analisada';\n const subject = `Análise financeira concluída${companyName ? ` — ${companyName}` : ''}`;\n\n const highlights: EmailHighlight[] = [];\n if (score !== undefined && score !== null && `${score}` !== '') {\n highlights.push({ label: 'Score GYRA+IA', value: `${score}` });\n }\n if (risk) {\n highlights.push({\n label: 'Nível de risco',\n value: risk,\n color: riskColor(risk),\n });\n }\n\n const buttons: EmailButton[] = [];\n if (xlsxUrl) {\n buttons.push({\n label: 'Baixar análise (Excel)',\n url: xlsxUrl,\n variant: 'primary',\n });\n }\n if (reportUrl) {\n buttons.push({\n label: 'Ver relatório',\n url: reportUrl,\n variant: 'secondary',\n });\n }\n\n const html = buildEmail({\n preheader: subject,\n greeting: firstName ? `Olá, ${firstName}` : undefined,\n title: 'Sua análise financeira está pronta',\n paragraphs: [\n `A análise financeira de ${empresa} foi processada com sucesso pela GYRA+IA.`,\n ],\n highlights,\n aiParecer: recommendation\n ? {\n verdict: risk || undefined,\n verdictTone: riskTone(risk),\n text: recommendation,\n footer: 'GYRA+IA · gerado a partir do balanço enviado',\n }\n : undefined,\n buttons,\n footerNote:\n 'Os valores e a recomendação são gerados por IA a partir do balanço enviado e devem ser revisados pelo analista.',\n });\n\n return { subject, html };\n}\n"],"names":["C","VERDICT","ok","bg","color","warn","danger","info","FONT","esc","s","String","replace","renderButton","btn","isPrimary","variant","url","u","URL","protocol","href","safeUrl","label","buildEmail","input","preheader","greeting","title","paragraphs","highlights","aiParecer","buttons","footerNote","highlightWidth","length","Math","floor","highlightsRow","map","h","widthPct","value","renderHighlight","join","paragraphsHtml","p","buttonsHtml","v","verdict","t","verdictTone","text","footer","renderAiParecer","riskColor","risk","r","toLowerCase","includes","riskTone","balanceSheetFinalizedEmail","payload","firstName","companyName","score","recommendation","xlsxUrl","reportUrl","empresa","subject","push","html","undefined"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const e="#1256FF",t="#7C4EFF",n="#6635E0",a="#F5F5F7",i="#FFFFFF",r="#1D1D1F",o="#636366",l="#8E8E93",d="#D2D2D7",p="#E8E8ED",s="#F6F4FF",c="rgba(124,78,255,0.22)",g={ok:{bg:"#E8F9ED",color:"#1A7D37"},warn:{bg:"#FFF4E0",color:"#946200"},danger:{bg:"#FFEDEC",color:"#C93430"},info:{bg:"#EBF4FF",color:"#0055D4"}},x="'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif",h=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");function m(t){const n="primary"===(t.variant??"primary"),a=n?"#FFFFFF":e;return`\n <table role="presentation" cellpadding="0" cellspacing="0" style="margin:8px 8px 0 0;display:inline-block;">\n <tr>\n <td style="border-radius:10px;background:${n?e:i};border:1px solid ${n?e:d};">\n <a href="${h(function(e){try{const t=new URL(String(e));return"http:"===t.protocol||"https:"===t.protocol?t.href:"#"}catch{return"#"}}(t.url))}" target="_blank"\n style="display:inline-block;padding:12px 22px;font-family:${x};font-size:14px;font-weight:600;color:${a};text-decoration:none;border-radius:10px;">\n ${h(t.label)}\n </a>\n </td>\n </tr>\n </table>`}function u(e){const{preheader:d,greeting:u,title:f,paragraphs:b=[],highlights:$=[],aiParecer:y,buttons:v=[],footerNote:F}=e,w=$.length?Math.floor(100/$.length):100,A=$.length?`<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 4px;"><tr>${$.map(e=>function(e,t){return`\n <td width="${t}%" style="padding:6px;" valign="top">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="background:#FAFAFA;border:1px solid ${p};border-radius:12px;">\n <tr><td style="padding:14px 16px;font-family:${x};">\n <div style="font-size:12px;color:${o};margin-bottom:4px;">${h(e.label)}</div>\n <div style="font-size:22px;font-weight:700;color:${e.color||r};font-family:'Lexend',${x};">${h(e.value)}</div>\n </td></tr>\n </table>\n </td>`}(e,w)).join("")}</tr></table>`:"",E=b.map(e=>`<p style="margin:0 0 14px;font-family:${x};font-size:15px;line-height:1.6;color:${o};">${h(e)}</p>`).join(""),k=v.length?`<div style="margin-top:18px;">${v.map(m).join("")}</div>`:"";return`<!DOCTYPE html>\n<html lang="pt-BR">\n<head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1.0" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n <title>${h(f)}</title>\n</head>\n<body style="margin:0;padding:0;background:${a};">\n ${d?`<div style="display:none;max-height:0;overflow:hidden;opacity:0;">${h(d)}</div>`:""}\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${a};padding:24px 0;">\n <tr>\n <td align="center">\n <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px;max-width:92%;">\n <tr>\n <td style="padding:8px 4px 20px;">\n <img src="https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png" alt="GYRA+" height="30" style="height:30px;display:block;" />\n </td>\n </tr>\n <tr>\n <td style="background:${i};border:1px solid ${p};border-radius:16px;padding:32px;">\n ${u?`<p style="margin:0 0 6px;font-family:${x};font-size:14px;color:${o};">${h(u)}</p>`:""}\n <h1 style="margin:0 0 18px;font-family:'Lexend',${x};font-size:23px;line-height:1.3;color:${r};">${h(f)}</h1>\n ${E}\n ${A}\n ${y?function(e){const a=e.verdict?(()=>{const t=g[e.verdictTone||"info"];return`<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;"><tr>\n <td style="background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${x};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;">${h(e.verdict)}</td></tr></table>`})():"";return`\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="margin:18px 0 4px;background:${s};border:1px solid ${c};border-radius:12px;">\n <tr><td style="padding:16px 18px;font-family:${x};">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>\n <td valign="middle" width="28">\n <div style="width:22px;height:22px;border-radius:50%;background:${t};color:#fff;text-align:center;line-height:22px;font-size:13px;">✦</div>\n </td>\n <td valign="middle" style="padding-left:8px;">\n <span style="font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${n};">Parecer GYRA+IA</span>\n </td>\n <td valign="middle" align="right">${a}</td>\n </tr></table>\n <p style="margin:10px 0 0;font-size:14px;line-height:1.55;color:${o};">${h(e.text)}</p>\n ${e.footer?`<div style="margin-top:10px;padding-top:10px;border-top:1px dashed ${c};font-size:11px;color:${l};">${h(e.footer)}</div>`:""}\n </td></tr>\n </table>`}(y):""}\n ${k}\n </td>\n </tr>\n <tr>\n <td style="padding:20px 8px;font-family:${x};font-size:12px;line-height:1.5;color:${l};">\n <p style="margin:0 0 8px;">${h(F)}</p>\n <p style="margin:0;">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`}function f(e){const t=(e||"").toLowerCase();return t.includes("muito")&&t.includes("alto")?"#C93430":t.includes("alto")||t.includes("medio")||t.includes("médio")?"#946200":t.includes("baixo")?"#1A7D37":"#1D1D1F"}function b(e){const t=(e||"").toLowerCase();return t.includes("alto")?"danger":t.includes("medio")||t.includes("médio")?"warn":t.includes("baixo")?"ok":"info"}exports.balanceSheetFinalizedEmail=function(e){const{firstName:t,companyName:n,score:a,risk:i,recommendation:r,xlsxUrl:o,reportUrl:l}=e,d=n||"a empresa analisada",p="Análise financeira concluída"+(n?` — ${n}`:""),s=[];null!=a&&""!=`${a}`&&s.push({label:"Score GYRA+IA",value:`${a}`}),i&&s.push({label:"Nível de risco",value:i,color:f(i)});const c=[];return o&&c.push({label:"Baixar análise (Excel)",url:o,variant:"primary"}),l&&c.push({label:"Ver relatório",url:l,variant:"secondary"}),{subject:p,html:u({preheader:p,greeting:t?`Olá, ${t}`:void 0,title:"Sua análise financeira está pronta",paragraphs:[`A análise financeira de ${d} foi processada com sucesso pela GYRA+IA.`],highlights:s,aiParecer:r?{verdict:i||void 0,verdictTone:b(i),text:r,footer:"GYRA+IA · gerado a partir do balanço enviado"}:void 0,buttons:c,footerNote:"Os valores e a recomendação são gerados por IA a partir do balanço enviado e devem ser revisados pelo analista."})}};
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/emails/email/index.ts","../../../src/emails/balance-sheet-finalized/index.ts"],"sourcesContent":["/**\n * Email transacional genérico — Gyra+ design system.\n *\n * **FONTE DA VERDADE** dos layouts de email transacional. Usado por\n * qualquer fluxo que precise mandar email no padrão visual Gyra+:\n * - reset de senha, confirmação de cadastro, alertas internos\n * - finalização de processamento (Finsights, batch-report, etc)\n * - notificações de operação/crédito\n * - convites, mudança de plano, alerts de billing...\n *\n * **TS puro**: sem React, sem DOM, sem browser globals. Apenas funções que\n * retornam string HTML. Funciona igual em qualquer runtime Node (Nest) ou\n * bundle browser (Next).\n *\n * **Email-safe**: tabelas + inline-style (clients ignoram <style> externo,\n * flexbox e Material Symbols). Gradientes têm fallback sólido.\n *\n * Tokens visuais alinhados com gyra-design: azul #1256FF (acao/primary),\n * roxo #7C4EFF (IA/parecer), neutrals Apple-gray, fonte Inter.\n *\n * **Como usar:**\n * ```ts\n * import { buildEmail } from '@gyramais/gyra-design/dist/emails/email';\n *\n * const html = buildEmail({\n * preheader: 'Senha redefinida com sucesso',\n * greeting: 'Olá, Maria',\n * title: 'Sua senha foi redefinida',\n * paragraphs: ['Tudo certo. Você já pode entrar com a nova senha.'],\n * buttons: [{ label: 'Entrar', url: 'https://app.gyramais.com/login' }],\n * footerNote: 'Se não foi você, redefina novamente clicando em \"Esqueci minha senha\".',\n * });\n * ```\n *\n * Pra emails recorrentes (mesmo conteudo, payload variavel), criar um\n * **preset** em `src/emails/<preset-name>/index.ts` que monta o\n * `EmailLayoutInput` a partir de campos de dominio e delega aqui. Ver\n * `balance-sheet-finalized` como exemplo.\n */\n\n// ── Tipos publicos ─────────────────────────────────────────────────────────\n\nexport type EmailButton = {\n label: string;\n url: string;\n /** primary = botão azul preenchido, secondary = outline. Default primary. */\n variant?: 'primary' | 'secondary';\n};\n\nexport type EmailHighlight = {\n /** label pequeno em cima (ex: \"Score\", \"Nivel de risco\"). */\n label: string;\n /** valor grande em baixo (ex: \"820\", \"Baixo\"). */\n value: string;\n /** cor do valor (ex: cor do risco). Default: tinta padrao Apple-gray. */\n color?: string;\n};\n\n/** Tom semantico do chip de veredito do bloco Parecer GYRA+IA. */\nexport type AiParecerTone = 'ok' | 'warn' | 'danger' | 'info';\n\nexport type EmailAiParecer = {\n /** chip de veredito (ex: \"Médio\", \"Consistente\"). Opcional. */\n verdict?: string;\n verdictTone?: AiParecerTone;\n /** corpo do parecer. */\n text: string;\n /** rodape do bloco (ex: \"GYRA+IA · Confiança 96%\"). Opcional. */\n footer?: string;\n};\n\nexport type EmailLayoutInput = {\n /** Texto invisivel no preheader (preview no inbox). */\n preheader?: string;\n /** Linha curta acima do titulo (ex: \"Olá, Maria\"). */\n greeting?: string;\n /** Titulo principal (H1). */\n title: string;\n /** Paragrafos de corpo (sub-frases). */\n paragraphs?: string[];\n /** Cards lado-a-lado (1 a N). Largura dividida igualmente. */\n highlights?: EmailHighlight[];\n /** Bloco \"Parecer GYRA+IA\" (faixa roxa com chip + texto + rodape). */\n aiParecer?: EmailAiParecer;\n /** Botoes de acao. Multiplos viram lado-a-lado. */\n buttons?: EmailButton[];\n /** Texto pequeno no rodape do card (disclaimer, instrucoes extras). */\n footerNote?: string;\n};\n\n// ── Tokens (espelho de gyra-design CSS variables, hard-coded pra email) ────\n\nconst C = {\n blue: '#1256FF',\n teal: '#1FB58A',\n aiPurple: '#7C4EFF',\n aiPurpleText: '#6635E0',\n bg: '#F5F5F7',\n card: '#FFFFFF',\n fg1: '#1D1D1F',\n fg2: '#636366',\n fg3: '#8E8E93',\n border: '#D2D2D7',\n borderSubtle: '#E8E8ED',\n aiCardBg: '#F6F4FF',\n aiBorder: 'rgba(124,78,255,0.22)',\n};\n\nconst VERDICT: Record<AiParecerTone, { bg: string; color: string }> = {\n ok: { bg: '#E8F9ED', color: '#1A7D37' },\n warn: { bg: '#FFF4E0', color: '#946200' },\n danger: { bg: '#FFEDEC', color: '#C93430' },\n info: { bg: '#EBF4FF', color: '#0055D4' },\n};\n\nconst FONT =\n \"'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif\";\n\nconst LOGO_URL =\n 'https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png';\n\n// ── Helpers internos ───────────────────────────────────────────────────────\n\nconst esc = (s: string): string =>\n String(s ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\n/**\n * Só permite http(s). Bloqueia schemes perigosos (javascript:, data:)\n * que poderiam virar href executável. URL inválida vira '#'.\n */\nfunction safeUrl(url: string): string {\n try {\n const u = new URL(String(url));\n return u.protocol === 'http:' || u.protocol === 'https:' ? u.href : '#';\n } catch {\n return '#';\n }\n}\n\nfunction renderButton(btn: EmailButton): string {\n const isPrimary = (btn.variant ?? 'primary') === 'primary';\n const bg = isPrimary ? C.blue : C.card;\n const color = isPrimary ? '#FFFFFF' : C.blue;\n const border = isPrimary ? C.blue : C.border;\n return `\n <table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 8px 0 0;display:inline-block;\">\n <tr>\n <td style=\"border-radius:10px;background:${bg};border:1px solid ${border};\">\n <a href=\"${esc(safeUrl(btn.url))}\" target=\"_blank\"\n style=\"display:inline-block;padding:12px 22px;font-family:${FONT};font-size:14px;font-weight:600;color:${color};text-decoration:none;border-radius:10px;\">\n ${esc(btn.label)}\n </a>\n </td>\n </tr>\n </table>`;\n}\n\nfunction renderHighlight(h: EmailHighlight, widthPct: number): string {\n return `\n <td width=\"${widthPct}%\" style=\"padding:6px;\" valign=\"top\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"background:#FAFAFA;border:1px solid ${C.borderSubtle};border-radius:12px;\">\n <tr><td style=\"padding:14px 16px;font-family:${FONT};\">\n <div style=\"font-size:12px;color:${C.fg2};margin-bottom:4px;\">${esc(h.label)}</div>\n <div style=\"font-size:22px;font-weight:700;color:${h.color || C.fg1};font-family:'Lexend',${FONT};\">${esc(h.value)}</div>\n </td></tr>\n </table>\n </td>`;\n}\n\nfunction renderAiParecer(p: EmailAiParecer): string {\n const v = p.verdict\n ? (() => {\n const t = VERDICT[p.verdictTone || 'info'];\n // table/td (Outlook ignora padding/border-radius em <span>).\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"display:inline-block;\"><tr>\n <td style=\"background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${FONT};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;\">${esc(\n p.verdict,\n )}</td></tr></table>`;\n })()\n : '';\n return `\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"margin:18px 0 4px;background:${C.aiCardBg};border:1px solid ${C.aiBorder};border-radius:12px;\">\n <tr><td style=\"padding:16px 18px;font-family:${FONT};\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n <td valign=\"middle\" width=\"28\">\n <div style=\"width:22px;height:22px;border-radius:50%;background:${C.aiPurple};color:#fff;text-align:center;line-height:22px;font-size:13px;\">✦</div>\n </td>\n <td valign=\"middle\" style=\"padding-left:8px;\">\n <span style=\"font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${C.aiPurpleText};\">Parecer GYRA+IA</span>\n </td>\n <td valign=\"middle\" align=\"right\">${v}</td>\n </tr></table>\n <p style=\"margin:10px 0 0;font-size:14px;line-height:1.55;color:${C.fg2};\">${esc(p.text)}</p>\n ${\n p.footer\n ? `<div style=\"margin-top:10px;padding-top:10px;border-top:1px dashed ${C.aiBorder};font-size:11px;color:${C.fg3};\">${esc(\n p.footer,\n )}</div>`\n : ''\n }\n </td></tr>\n </table>`;\n}\n\n// ── API publica ────────────────────────────────────────────────────────────\n\n/**\n * Monta o HTML completo de um email no padrão Gyra+: logo no topo, card\n * principal com greeting + título + parágrafos + highlights (chips lado a\n * lado) + bloco \"Parecer GYRA+IA\" + botões, e rodapé padrão.\n *\n * Email-safe (tabelas + inline-style, sem flexbox, sem <style>). Funciona\n * em Gmail, Outlook (Windows/Web/Mac), Apple Mail, Thunderbird, mobile.\n *\n * Todas as seções (greeting, paragraphs, highlights, aiParecer, buttons,\n * footerNote) são **opcionais** — só `title` é obrigatório.\n */\nexport function buildEmail(input: EmailLayoutInput): string {\n const {\n preheader,\n greeting,\n title,\n paragraphs = [],\n highlights = [],\n aiParecer,\n buttons = [],\n footerNote,\n } = input;\n\n const highlightWidth = highlights.length\n ? Math.floor(100 / highlights.length)\n : 100;\n const highlightsRow = highlights.length\n ? `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 4px;\"><tr>${highlights\n .map((h) => renderHighlight(h, highlightWidth))\n .join('')}</tr></table>`\n : '';\n\n const paragraphsHtml = paragraphs\n .map(\n (p) =>\n `<p style=\"margin:0 0 14px;font-family:${FONT};font-size:15px;line-height:1.6;color:${C.fg2};\">${esc(\n p,\n )}</p>`,\n )\n .join('');\n\n const buttonsHtml = buttons.length\n ? `<div style=\"margin-top:18px;\">${buttons.map(renderButton).join('')}</div>`\n : '';\n\n return `<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>${esc(title)}</title>\n</head>\n<body style=\"margin:0;padding:0;background:${C.bg};\">\n ${\n preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${esc(preheader)}</div>`\n : ''\n }\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:${C.bg};padding:24px 0;\">\n <tr>\n <td align=\"center\">\n <table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;max-width:92%;\">\n <tr>\n <td style=\"padding:8px 4px 20px;\">\n <img src=\"${LOGO_URL}\" alt=\"GYRA+\" height=\"30\" style=\"height:30px;display:block;\" />\n </td>\n </tr>\n <tr>\n <td style=\"background:${C.card};border:1px solid ${C.borderSubtle};border-radius:16px;padding:32px;\">\n ${\n greeting\n ? `<p style=\"margin:0 0 6px;font-family:${FONT};font-size:14px;color:${C.fg2};\">${esc(\n greeting,\n )}</p>`\n : ''\n }\n <h1 style=\"margin:0 0 18px;font-family:'Lexend',${FONT};font-size:23px;line-height:1.3;color:${C.fg1};\">${esc(\n title,\n )}</h1>\n ${paragraphsHtml}\n ${highlightsRow}\n ${aiParecer ? renderAiParecer(aiParecer) : ''}\n ${buttonsHtml}\n </td>\n </tr>\n <tr>\n <td style=\"padding:20px 8px;font-family:${FONT};font-size:12px;line-height:1.5;color:${C.fg3};\">\n ${\n footerNote\n ? `<p style=\"margin:0 0 8px;\">${esc(footerNote)}</p>`\n : ''\n }\n <p style=\"margin:0;\">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`;\n}\n","/**\n * Preset do email \"Análise financeira concluída\" enviado quando o gyra-ai\n * finaliza a extração de um balanço (feature Finsights — CU-86aherfzc).\n *\n * Esta é uma **camada fina de mapeamento de domínio** sobre o\n * `buildEmail` genérico: recebe os campos crus que o gyra-ai produz\n * (score, risk, recommendation, xlsxUrl, ...) e monta o `EmailLayoutInput`\n * com as decisões visuais (cor do risco, tom do veredito, textos padrão).\n *\n * Pra criar outro preset (ex: `password-reset`, `report-finalized`,\n * `batch-finalized`), criar uma pasta nova em `src/emails/<name>/` que\n * importe `buildEmail` e exporte uma função `<name>Email(payload)` com a\n * mesma assinatura: `{ subject, html }`.\n */\n\nimport { buildEmail, AiParecerTone, EmailButton, EmailHighlight } from '../email';\n\nexport type BalanceSheetFinalizedPayload = {\n firstName?: string;\n companyName?: string;\n score?: number | string;\n risk?: string;\n recommendation?: string;\n xlsxUrl?: string;\n reportUrl?: string;\n};\n\n/** Mapeia nível de risco textual -> cor do chip de score. */\nfunction riskColor(risk?: string): string {\n const r = (risk || '').toLowerCase();\n if (r.includes('muito') && r.includes('alto')) return '#C93430';\n if (r.includes('alto')) return '#946200';\n if (r.includes('medio') || r.includes('médio')) return '#946200';\n if (r.includes('baixo')) return '#1A7D37';\n return '#1D1D1F';\n}\n\n/** Risco -> tom do veredito do Parecer de IA. */\nfunction riskTone(risk?: string): AiParecerTone {\n const r = (risk || '').toLowerCase();\n if (r.includes('alto')) return 'danger';\n if (r.includes('medio') || r.includes('médio')) return 'warn';\n if (r.includes('baixo')) return 'ok';\n return 'info';\n}\n\n/**\n * Monta `{ subject, html }` do email \"Análise financeira concluída\" a\n * partir do payload do gyra-ai (score, risco, recomendação, XLS, etc).\n * Usado pelo gyra-communication (envio via SendGrid) e pelo Storybook\n * (preview).\n */\nexport function balanceSheetFinalizedEmail(\n payload: BalanceSheetFinalizedPayload,\n): { subject: string; html: string } {\n const {\n firstName,\n companyName,\n score,\n risk,\n recommendation,\n xlsxUrl,\n reportUrl,\n } = payload;\n\n const empresa = companyName || 'a empresa analisada';\n const subject = `Análise financeira concluída${companyName ? ` — ${companyName}` : ''}`;\n\n const highlights: EmailHighlight[] = [];\n if (score !== undefined && score !== null && `${score}` !== '') {\n highlights.push({ label: 'Score GYRA+IA', value: `${score}` });\n }\n if (risk) {\n highlights.push({\n label: 'Nível de risco',\n value: risk,\n color: riskColor(risk),\n });\n }\n\n const buttons: EmailButton[] = [];\n if (xlsxUrl) {\n buttons.push({\n label: 'Baixar análise (Excel)',\n url: xlsxUrl,\n variant: 'primary',\n });\n }\n if (reportUrl) {\n buttons.push({\n label: 'Ver relatório',\n url: reportUrl,\n variant: 'secondary',\n });\n }\n\n const html = buildEmail({\n preheader: subject,\n greeting: firstName ? `Olá, ${firstName}` : undefined,\n title: 'Sua análise financeira está pronta',\n paragraphs: [\n `A análise financeira de ${empresa} foi processada com sucesso pela GYRA+IA.`,\n ],\n highlights,\n aiParecer: recommendation\n ? {\n verdict: risk || undefined,\n verdictTone: riskTone(risk),\n text: recommendation,\n footer: 'GYRA+IA · gerado a partir do balanço enviado',\n }\n : undefined,\n buttons,\n footerNote:\n 'Os valores e a recomendação são gerados por IA a partir do balanço enviado e devem ser revisados pelo analista.',\n });\n\n return { subject, html };\n}\n"],"names":["C","VERDICT","ok","bg","color","warn","danger","info","FONT","esc","s","String","replace","renderButton","btn","isPrimary","variant","url","u","URL","protocol","href","safeUrl","label","buildEmail","input","preheader","greeting","title","paragraphs","highlights","aiParecer","buttons","footerNote","highlightWidth","length","Math","floor","highlightsRow","map","h","widthPct","value","renderHighlight","join","paragraphsHtml","p","buttonsHtml","v","verdict","t","verdictTone","text","footer","renderAiParecer","riskColor","risk","r","toLowerCase","includes","riskTone","payload","firstName","companyName","score","recommendation","xlsxUrl","reportUrl","empresa","subject","push","html","undefined"],"mappings":"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"}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Email transacional genérico — Gyra+ design system.
|
|
3
|
-
*
|
|
4
|
-
* **FONTE DA VERDADE** dos layouts de email transacional. Usado por
|
|
5
|
-
* qualquer fluxo que precise mandar email no padrão visual Gyra+:
|
|
6
|
-
* - reset de senha, confirmação de cadastro, alertas internos
|
|
7
|
-
* - finalização de processamento (Finsights, batch-report, etc)
|
|
8
|
-
* - notificações de operação/crédito
|
|
9
|
-
* - convites, mudança de plano, alerts de billing...
|
|
10
|
-
*
|
|
11
|
-
* **TS puro**: sem React, sem DOM, sem browser globals. Apenas funções que
|
|
12
|
-
* retornam string HTML. Funciona igual em qualquer runtime Node (Nest) ou
|
|
13
|
-
* bundle browser (Next).
|
|
14
|
-
*
|
|
15
|
-
* **Email-safe**: tabelas + inline-style (clients ignoram <style> externo,
|
|
16
|
-
* flexbox e Material Symbols). Gradientes têm fallback sólido.
|
|
17
|
-
*
|
|
18
|
-
* Tokens visuais alinhados com gyra-design: azul #1256FF (acao/primary),
|
|
19
|
-
* roxo #7C4EFF (IA/parecer), neutrals Apple-gray, fonte Inter.
|
|
20
|
-
*
|
|
21
|
-
* **Como usar:**
|
|
22
|
-
* ```ts
|
|
23
|
-
* import { buildEmail } from '@gyramais/gyra-design/dist/emails/email';
|
|
24
|
-
*
|
|
25
|
-
* const html = buildEmail({
|
|
26
|
-
* preheader: 'Senha redefinida com sucesso',
|
|
27
|
-
* greeting: 'Olá, Maria',
|
|
28
|
-
* title: 'Sua senha foi redefinida',
|
|
29
|
-
* paragraphs: ['Tudo certo. Você já pode entrar com a nova senha.'],
|
|
30
|
-
* buttons: [{ label: 'Entrar', url: 'https://app.gyramais.com/login' }],
|
|
31
|
-
* footerNote: 'Se não foi você, redefina novamente clicando em "Esqueci minha senha".',
|
|
32
|
-
* });
|
|
33
|
-
* ```
|
|
34
|
-
*
|
|
35
|
-
* Pra emails recorrentes (mesmo conteudo, payload variavel), criar um
|
|
36
|
-
* **preset** em `src/emails/<preset-name>/index.ts` que monta o
|
|
37
|
-
* `EmailLayoutInput` a partir de campos de dominio e delega aqui. Ver
|
|
38
|
-
* `balance-sheet-finalized` como exemplo.
|
|
39
|
-
*/
|
|
40
|
-
export type EmailButton = {
|
|
41
|
-
label: string;
|
|
42
|
-
url: string;
|
|
43
|
-
/** primary = botão azul preenchido, secondary = outline. Default primary. */
|
|
44
|
-
variant?: 'primary' | 'secondary';
|
|
45
|
-
};
|
|
46
|
-
export type EmailHighlight = {
|
|
47
|
-
/** label pequeno em cima (ex: "Score", "Nivel de risco"). */
|
|
48
|
-
label: string;
|
|
49
|
-
/** valor grande em baixo (ex: "820", "Baixo"). */
|
|
50
|
-
value: string;
|
|
51
|
-
/** cor do valor (ex: cor do risco). Default: tinta padrao Apple-gray. */
|
|
52
|
-
color?: string;
|
|
53
|
-
};
|
|
54
|
-
/** Tom semantico do chip de veredito do bloco Parecer GYRA+IA. */
|
|
55
|
-
export type AiParecerTone = 'ok' | 'warn' | 'danger' | 'info';
|
|
56
|
-
export type EmailAiParecer = {
|
|
57
|
-
/** chip de veredito (ex: "Médio", "Consistente"). Opcional. */
|
|
58
|
-
verdict?: string;
|
|
59
|
-
verdictTone?: AiParecerTone;
|
|
60
|
-
/** corpo do parecer. */
|
|
61
|
-
text: string;
|
|
62
|
-
/** rodape do bloco (ex: "GYRA+IA · Confiança 96%"). Opcional. */
|
|
63
|
-
footer?: string;
|
|
64
|
-
};
|
|
65
|
-
export type EmailLayoutInput = {
|
|
66
|
-
/** Texto invisivel no preheader (preview no inbox). */
|
|
67
|
-
preheader?: string;
|
|
68
|
-
/** Linha curta acima do titulo (ex: "Olá, Maria"). */
|
|
69
|
-
greeting?: string;
|
|
70
|
-
/** Titulo principal (H1). */
|
|
71
|
-
title: string;
|
|
72
|
-
/** Paragrafos de corpo (sub-frases). */
|
|
73
|
-
paragraphs?: string[];
|
|
74
|
-
/** Cards lado-a-lado (1 a N). Largura dividida igualmente. */
|
|
75
|
-
highlights?: EmailHighlight[];
|
|
76
|
-
/** Bloco "Parecer GYRA+IA" (faixa roxa com chip + texto + rodape). */
|
|
77
|
-
aiParecer?: EmailAiParecer;
|
|
78
|
-
/** Botoes de acao. Multiplos viram lado-a-lado. */
|
|
79
|
-
buttons?: EmailButton[];
|
|
80
|
-
/** Texto pequeno no rodape do card (disclaimer, instrucoes extras). */
|
|
81
|
-
footerNote?: string;
|
|
82
|
-
};
|
|
83
|
-
/**
|
|
84
|
-
* Monta o HTML completo de um email no padrão Gyra+: logo no topo, card
|
|
85
|
-
* principal com greeting + título + parágrafos + highlights (chips lado a
|
|
86
|
-
* lado) + bloco "Parecer GYRA+IA" + botões, e rodapé padrão.
|
|
87
|
-
*
|
|
88
|
-
* Email-safe (tabelas + inline-style, sem flexbox, sem <style>). Funciona
|
|
89
|
-
* em Gmail, Outlook (Windows/Web/Mac), Apple Mail, Thunderbird, mobile.
|
|
90
|
-
*
|
|
91
|
-
* Todas as seções (greeting, paragraphs, highlights, aiParecer, buttons,
|
|
92
|
-
* footerNote) são **opcionais** — só `title` é obrigatório.
|
|
93
|
-
*/
|
|
94
|
-
export declare function buildEmail(input: EmailLayoutInput): string;
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const t="#1256FF",n="#7C4EFF",e="#6635E0",i="#F5F5F7",l="#FFFFFF",r="#1D1D1F",o="#636366",a="#8E8E93",p="#D2D2D7",d="#E8E8ED",s="#F6F4FF",g="rgba(124,78,255,0.22)",c={ok:{bg:"#E8F9ED",color:"#1A7D37"},warn:{bg:"#FFF4E0",color:"#946200"},danger:{bg:"#FFEDEC",color:"#C93430"},info:{bg:"#EBF4FF",color:"#0055D4"}},x="'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif",h=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");function f(n){const e="primary"===(n.variant??"primary"),i=e?"#FFFFFF":t;return`\n <table role="presentation" cellpadding="0" cellspacing="0" style="margin:8px 8px 0 0;display:inline-block;">\n <tr>\n <td style="border-radius:10px;background:${e?t:l};border:1px solid ${e?t:p};">\n <a href="${h(function(t){try{const n=new URL(String(t));return"http:"===n.protocol||"https:"===n.protocol?n.href:"#"}catch{return"#"}}(n.url))}" target="_blank"\n style="display:inline-block;padding:12px 22px;font-family:${x};font-size:14px;font-weight:600;color:${i};text-decoration:none;border-radius:10px;">\n ${h(n.label)}\n </a>\n </td>\n </tr>\n </table>`}function $(t){const{preheader:p,greeting:$,title:m,paragraphs:y=[],highlights:b=[],aiParecer:u,buttons:F=[],footerNote:v}=t,w=b.length?Math.floor(100/b.length):100,E=b.length?`<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 4px;"><tr>${b.map(t=>function(t,n){return`\n <td width="${n}%" style="padding:6px;" valign="top">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="background:#FAFAFA;border:1px solid ${d};border-radius:12px;">\n <tr><td style="padding:14px 16px;font-family:${x};">\n <div style="font-size:12px;color:${o};margin-bottom:4px;">${h(t.label)}</div>\n <div style="font-size:22px;font-weight:700;color:${t.color||r};font-family:'Lexend',${x};">${h(t.value)}</div>\n </td></tr>\n </table>\n </td>`}(t,w)).join("")}</tr></table>`:"",k=y.map(t=>`<p style="margin:0 0 14px;font-family:${x};font-size:15px;line-height:1.6;color:${o};">${h(t)}</p>`).join(""),z=F.length?`<div style="margin-top:18px;">${F.map(f).join("")}</div>`:"";return`<!DOCTYPE html>\n<html lang="pt-BR">\n<head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1.0" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n <title>${h(m)}</title>\n</head>\n<body style="margin:0;padding:0;background:${i};">\n ${p?`<div style="display:none;max-height:0;overflow:hidden;opacity:0;">${h(p)}</div>`:""}\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${i};padding:24px 0;">\n <tr>\n <td align="center">\n <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px;max-width:92%;">\n <tr>\n <td style="padding:8px 4px 20px;">\n <img src="https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png" alt="GYRA+" height="30" style="height:30px;display:block;" />\n </td>\n </tr>\n <tr>\n <td style="background:${l};border:1px solid ${d};border-radius:16px;padding:32px;">\n ${$?`<p style="margin:0 0 6px;font-family:${x};font-size:14px;color:${o};">${h($)}</p>`:""}\n <h1 style="margin:0 0 18px;font-family:'Lexend',${x};font-size:23px;line-height:1.3;color:${r};">${h(m)}</h1>\n ${k}\n ${E}\n ${u?function(t){const i=t.verdict?(()=>{const n=c[t.verdictTone||"info"];return`<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;"><tr>\n <td style="background:${n.bg};border-radius:999px;padding:4px 10px;font-family:${x};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${n.color};white-space:nowrap;">${h(t.verdict)}</td></tr></table>`})():"";return`\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="margin:18px 0 4px;background:${s};border:1px solid ${g};border-radius:12px;">\n <tr><td style="padding:16px 18px;font-family:${x};">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>\n <td valign="middle" width="28">\n <div style="width:22px;height:22px;border-radius:50%;background:${n};color:#fff;text-align:center;line-height:22px;font-size:13px;">✦</div>\n </td>\n <td valign="middle" style="padding-left:8px;">\n <span style="font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${e};">Parecer GYRA+IA</span>\n </td>\n <td valign="middle" align="right">${i}</td>\n </tr></table>\n <p style="margin:10px 0 0;font-size:14px;line-height:1.55;color:${o};">${h(t.text)}</p>\n ${t.footer?`<div style="margin-top:10px;padding-top:10px;border-top:1px dashed ${g};font-size:11px;color:${a};">${h(t.footer)}</div>`:""}\n </td></tr>\n </table>`}(u):""}\n ${z}\n </td>\n </tr>\n <tr>\n <td style="padding:20px 8px;font-family:${x};font-size:12px;line-height:1.5;color:${a};">\n ${v?`<p style="margin:0 0 8px;">${h(v)}</p>`:""}\n <p style="margin:0;">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`}export{$ as buildEmail};
|
|
2
|
-
//# sourceMappingURL=index.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../../../src/emails/email/index.ts"],"sourcesContent":["/**\n * Email transacional genérico — Gyra+ design system.\n *\n * **FONTE DA VERDADE** dos layouts de email transacional. Usado por\n * qualquer fluxo que precise mandar email no padrão visual Gyra+:\n * - reset de senha, confirmação de cadastro, alertas internos\n * - finalização de processamento (Finsights, batch-report, etc)\n * - notificações de operação/crédito\n * - convites, mudança de plano, alerts de billing...\n *\n * **TS puro**: sem React, sem DOM, sem browser globals. Apenas funções que\n * retornam string HTML. Funciona igual em qualquer runtime Node (Nest) ou\n * bundle browser (Next).\n *\n * **Email-safe**: tabelas + inline-style (clients ignoram <style> externo,\n * flexbox e Material Symbols). Gradientes têm fallback sólido.\n *\n * Tokens visuais alinhados com gyra-design: azul #1256FF (acao/primary),\n * roxo #7C4EFF (IA/parecer), neutrals Apple-gray, fonte Inter.\n *\n * **Como usar:**\n * ```ts\n * import { buildEmail } from '@gyramais/gyra-design/dist/emails/email';\n *\n * const html = buildEmail({\n * preheader: 'Senha redefinida com sucesso',\n * greeting: 'Olá, Maria',\n * title: 'Sua senha foi redefinida',\n * paragraphs: ['Tudo certo. Você já pode entrar com a nova senha.'],\n * buttons: [{ label: 'Entrar', url: 'https://app.gyramais.com/login' }],\n * footerNote: 'Se não foi você, redefina novamente clicando em \"Esqueci minha senha\".',\n * });\n * ```\n *\n * Pra emails recorrentes (mesmo conteudo, payload variavel), criar um\n * **preset** em `src/emails/<preset-name>/index.ts` que monta o\n * `EmailLayoutInput` a partir de campos de dominio e delega aqui. Ver\n * `balance-sheet-finalized` como exemplo.\n */\n\n// ── Tipos publicos ─────────────────────────────────────────────────────────\n\nexport type EmailButton = {\n label: string;\n url: string;\n /** primary = botão azul preenchido, secondary = outline. Default primary. */\n variant?: 'primary' | 'secondary';\n};\n\nexport type EmailHighlight = {\n /** label pequeno em cima (ex: \"Score\", \"Nivel de risco\"). */\n label: string;\n /** valor grande em baixo (ex: \"820\", \"Baixo\"). */\n value: string;\n /** cor do valor (ex: cor do risco). Default: tinta padrao Apple-gray. */\n color?: string;\n};\n\n/** Tom semantico do chip de veredito do bloco Parecer GYRA+IA. */\nexport type AiParecerTone = 'ok' | 'warn' | 'danger' | 'info';\n\nexport type EmailAiParecer = {\n /** chip de veredito (ex: \"Médio\", \"Consistente\"). Opcional. */\n verdict?: string;\n verdictTone?: AiParecerTone;\n /** corpo do parecer. */\n text: string;\n /** rodape do bloco (ex: \"GYRA+IA · Confiança 96%\"). Opcional. */\n footer?: string;\n};\n\nexport type EmailLayoutInput = {\n /** Texto invisivel no preheader (preview no inbox). */\n preheader?: string;\n /** Linha curta acima do titulo (ex: \"Olá, Maria\"). */\n greeting?: string;\n /** Titulo principal (H1). */\n title: string;\n /** Paragrafos de corpo (sub-frases). */\n paragraphs?: string[];\n /** Cards lado-a-lado (1 a N). Largura dividida igualmente. */\n highlights?: EmailHighlight[];\n /** Bloco \"Parecer GYRA+IA\" (faixa roxa com chip + texto + rodape). */\n aiParecer?: EmailAiParecer;\n /** Botoes de acao. Multiplos viram lado-a-lado. */\n buttons?: EmailButton[];\n /** Texto pequeno no rodape do card (disclaimer, instrucoes extras). */\n footerNote?: string;\n};\n\n// ── Tokens (espelho de gyra-design CSS variables, hard-coded pra email) ────\n\nconst C = {\n blue: '#1256FF',\n teal: '#1FB58A',\n aiPurple: '#7C4EFF',\n aiPurpleText: '#6635E0',\n bg: '#F5F5F7',\n card: '#FFFFFF',\n fg1: '#1D1D1F',\n fg2: '#636366',\n fg3: '#8E8E93',\n border: '#D2D2D7',\n borderSubtle: '#E8E8ED',\n aiCardBg: '#F6F4FF',\n aiBorder: 'rgba(124,78,255,0.22)',\n};\n\nconst VERDICT: Record<AiParecerTone, { bg: string; color: string }> = {\n ok: { bg: '#E8F9ED', color: '#1A7D37' },\n warn: { bg: '#FFF4E0', color: '#946200' },\n danger: { bg: '#FFEDEC', color: '#C93430' },\n info: { bg: '#EBF4FF', color: '#0055D4' },\n};\n\nconst FONT =\n \"'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif\";\n\nconst LOGO_URL =\n 'https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png';\n\n// ── Helpers internos ───────────────────────────────────────────────────────\n\nconst esc = (s: string): string =>\n String(s ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\n/**\n * Só permite http(s). Bloqueia schemes perigosos (javascript:, data:)\n * que poderiam virar href executável. URL inválida vira '#'.\n */\nfunction safeUrl(url: string): string {\n try {\n const u = new URL(String(url));\n return u.protocol === 'http:' || u.protocol === 'https:' ? u.href : '#';\n } catch {\n return '#';\n }\n}\n\nfunction renderButton(btn: EmailButton): string {\n const isPrimary = (btn.variant ?? 'primary') === 'primary';\n const bg = isPrimary ? C.blue : C.card;\n const color = isPrimary ? '#FFFFFF' : C.blue;\n const border = isPrimary ? C.blue : C.border;\n return `\n <table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 8px 0 0;display:inline-block;\">\n <tr>\n <td style=\"border-radius:10px;background:${bg};border:1px solid ${border};\">\n <a href=\"${esc(safeUrl(btn.url))}\" target=\"_blank\"\n style=\"display:inline-block;padding:12px 22px;font-family:${FONT};font-size:14px;font-weight:600;color:${color};text-decoration:none;border-radius:10px;\">\n ${esc(btn.label)}\n </a>\n </td>\n </tr>\n </table>`;\n}\n\nfunction renderHighlight(h: EmailHighlight, widthPct: number): string {\n return `\n <td width=\"${widthPct}%\" style=\"padding:6px;\" valign=\"top\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"background:#FAFAFA;border:1px solid ${C.borderSubtle};border-radius:12px;\">\n <tr><td style=\"padding:14px 16px;font-family:${FONT};\">\n <div style=\"font-size:12px;color:${C.fg2};margin-bottom:4px;\">${esc(h.label)}</div>\n <div style=\"font-size:22px;font-weight:700;color:${h.color || C.fg1};font-family:'Lexend',${FONT};\">${esc(h.value)}</div>\n </td></tr>\n </table>\n </td>`;\n}\n\nfunction renderAiParecer(p: EmailAiParecer): string {\n const v = p.verdict\n ? (() => {\n const t = VERDICT[p.verdictTone || 'info'];\n // table/td (Outlook ignora padding/border-radius em <span>).\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"display:inline-block;\"><tr>\n <td style=\"background:${t.bg};border-radius:999px;padding:4px 10px;font-family:${FONT};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${t.color};white-space:nowrap;\">${esc(\n p.verdict,\n )}</td></tr></table>`;\n })()\n : '';\n return `\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n style=\"margin:18px 0 4px;background:${C.aiCardBg};border:1px solid ${C.aiBorder};border-radius:12px;\">\n <tr><td style=\"padding:16px 18px;font-family:${FONT};\">\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n <td valign=\"middle\" width=\"28\">\n <div style=\"width:22px;height:22px;border-radius:50%;background:${C.aiPurple};color:#fff;text-align:center;line-height:22px;font-size:13px;\">✦</div>\n </td>\n <td valign=\"middle\" style=\"padding-left:8px;\">\n <span style=\"font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${C.aiPurpleText};\">Parecer GYRA+IA</span>\n </td>\n <td valign=\"middle\" align=\"right\">${v}</td>\n </tr></table>\n <p style=\"margin:10px 0 0;font-size:14px;line-height:1.55;color:${C.fg2};\">${esc(p.text)}</p>\n ${\n p.footer\n ? `<div style=\"margin-top:10px;padding-top:10px;border-top:1px dashed ${C.aiBorder};font-size:11px;color:${C.fg3};\">${esc(\n p.footer,\n )}</div>`\n : ''\n }\n </td></tr>\n </table>`;\n}\n\n// ── API publica ────────────────────────────────────────────────────────────\n\n/**\n * Monta o HTML completo de um email no padrão Gyra+: logo no topo, card\n * principal com greeting + título + parágrafos + highlights (chips lado a\n * lado) + bloco \"Parecer GYRA+IA\" + botões, e rodapé padrão.\n *\n * Email-safe (tabelas + inline-style, sem flexbox, sem <style>). Funciona\n * em Gmail, Outlook (Windows/Web/Mac), Apple Mail, Thunderbird, mobile.\n *\n * Todas as seções (greeting, paragraphs, highlights, aiParecer, buttons,\n * footerNote) são **opcionais** — só `title` é obrigatório.\n */\nexport function buildEmail(input: EmailLayoutInput): string {\n const {\n preheader,\n greeting,\n title,\n paragraphs = [],\n highlights = [],\n aiParecer,\n buttons = [],\n footerNote,\n } = input;\n\n const highlightWidth = highlights.length\n ? Math.floor(100 / highlights.length)\n : 100;\n const highlightsRow = highlights.length\n ? `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 4px;\"><tr>${highlights\n .map((h) => renderHighlight(h, highlightWidth))\n .join('')}</tr></table>`\n : '';\n\n const paragraphsHtml = paragraphs\n .map(\n (p) =>\n `<p style=\"margin:0 0 14px;font-family:${FONT};font-size:15px;line-height:1.6;color:${C.fg2};\">${esc(\n p,\n )}</p>`,\n )\n .join('');\n\n const buttonsHtml = buttons.length\n ? `<div style=\"margin-top:18px;\">${buttons.map(renderButton).join('')}</div>`\n : '';\n\n return `<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <title>${esc(title)}</title>\n</head>\n<body style=\"margin:0;padding:0;background:${C.bg};\">\n ${\n preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${esc(preheader)}</div>`\n : ''\n }\n <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:${C.bg};padding:24px 0;\">\n <tr>\n <td align=\"center\">\n <table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;max-width:92%;\">\n <tr>\n <td style=\"padding:8px 4px 20px;\">\n <img src=\"${LOGO_URL}\" alt=\"GYRA+\" height=\"30\" style=\"height:30px;display:block;\" />\n </td>\n </tr>\n <tr>\n <td style=\"background:${C.card};border:1px solid ${C.borderSubtle};border-radius:16px;padding:32px;\">\n ${\n greeting\n ? `<p style=\"margin:0 0 6px;font-family:${FONT};font-size:14px;color:${C.fg2};\">${esc(\n greeting,\n )}</p>`\n : ''\n }\n <h1 style=\"margin:0 0 18px;font-family:'Lexend',${FONT};font-size:23px;line-height:1.3;color:${C.fg1};\">${esc(\n title,\n )}</h1>\n ${paragraphsHtml}\n ${highlightsRow}\n ${aiParecer ? renderAiParecer(aiParecer) : ''}\n ${buttonsHtml}\n </td>\n </tr>\n <tr>\n <td style=\"padding:20px 8px;font-family:${FONT};font-size:12px;line-height:1.5;color:${C.fg3};\">\n ${\n footerNote\n ? `<p style=\"margin:0 0 8px;\">${esc(footerNote)}</p>`\n : ''\n }\n <p style=\"margin:0;\">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`;\n}\n"],"names":["C","VERDICT","ok","bg","color","warn","danger","info","FONT","esc","s","String","replace","renderButton","btn","isPrimary","variant","url","u","URL","protocol","href","safeUrl","label","buildEmail","input","preheader","greeting","title","paragraphs","highlights","aiParecer","buttons","footerNote","highlightWidth","length","Math","floor","highlightsRow","map","h","widthPct","value","renderHighlight","join","paragraphsHtml","p","buttonsHtml","v","verdict","t","verdictTone","text","footer","renderAiParecer"],"mappings":"AA4FA,MAAMA,EACE,UADFA,EAGM,UAHNA,EAIU,UAJVA,EAKA,UALAA,EAME,UANFA,EAOC,UAPDA,EAQC,UARDA,EASC,UATDA,EAUI,UAVJA,EAWU,UAXVA,EAYM,UAZNA,EAaM,wBAGNC,EAAgE,CACpEC,GAAI,CAAEC,GAAI,UAAWC,MAAO,WAC5BC,KAAM,CAAEF,GAAI,UAAWC,MAAO,WAC9BE,OAAQ,CAAEH,GAAI,UAAWC,MAAO,WAChCG,KAAM,CAAEJ,GAAI,UAAWC,MAAO,YAG1BI,EACJ,8EAOIC,EAAOC,GACXC,OAAOD,GAAK,IACTE,QAAQ,KAAM,SACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,UACdA,QAAQ,KAAM,SAenB,SAASC,EAAaC,GACpB,MAAMC,EAA2C,aAA9BD,EAAIE,SAAW,WAE5BZ,EAAQW,EAAY,UAAYf,EAEtC,MAAO,oLAHIe,EAAYf,EAASA,sBAEjBe,EAAYf,EAASA,4BAKjBS,EAlBrB,SAAiBQ,GACf,IACE,MAAMC,EAAI,IAAIC,IAAIR,OAAOM,IACzB,MAAsB,UAAfC,EAAEE,UAAuC,WAAfF,EAAEE,SAAwBF,EAAEG,KAAO,GACtE,CAAE,MACA,MAAO,GACT,CACF,CAWyBC,CAAQR,EAAIG,kGACoCT,0CAA6CJ,6DACxGK,EAAIK,EAAIS,kEAKtB,CAgEM,SAAUC,EAAWC,GACzB,MAAMC,UACJA,EAASC,SACTA,EAAQC,MACRA,EAAKC,WACLA,EAAa,GAAEC,WACfA,EAAa,GAAEC,UACfA,EAASC,QACTA,EAAU,GAAEC,WACZA,GACER,EAEES,EAAiBJ,EAAWK,OAC9BC,KAAKC,MAAM,IAAMP,EAAWK,QAC5B,IACEG,EAAgBR,EAAWK,OAC7B,yGAAyGL,EACtGS,IAAKC,GA/Ed,SAAyBA,EAAmBC,GAC1C,MAAO,oBACQA,kLAEyCzC,iFACHQ,oDACVR,yBAA6BS,EAAI+B,EAAEjB,4EACnBiB,EAAEpC,OAASJ,0BAA8BQ,OAAUC,EAAI+B,EAAEE,6DAItH,CAoEoBC,CAAgBH,EAAGN,IAC9BU,KAAK,mBACR,GAEEC,EAAiBhB,EACpBU,IACEO,GACC,yCAAyCtC,0CAA6CR,OAAWS,EAC/FqC,UAGLF,KAAK,IAEFG,EAAcf,EAAQG,OACxB,iCAAiCH,EAAQO,IAAI1B,GAAc+B,KAAK,YAChE,GAEJ,MAAO,4NAMEnC,EAAImB,mEAE8B5B,WAEzC0B,EACI,qEAAqEjB,EAAIiB,WACzE,mGAEsF1B,4eAU1DA,sBAA2BA,uDAE/C2B,EACI,wCAAwCnB,0BAA6BR,OAAWS,EAC9EkB,SAEF,qEAE4CnB,0CAA6CR,OAAWS,EACxGmB,0BAEAiB,oBACAP,oBACAP,EAxHhB,SAAyBe,GACvB,MAAME,EAAIF,EAAEG,QACR,MACE,MAAMC,EAAIjD,EAAQ6C,EAAEK,aAAe,QAEnC,MAAO,kIACmBD,EAAE/C,uDAAuDK,yFAA4F0C,EAAE9C,8BAA8BK,EAC3MqC,EAAEG,4BAEP,EAPD,GAQA,GACJ,MAAO,iIAEwCjD,sBAA+BA,+EAC3BQ,sNAGyBR,4QAGiCA,4FAEjEgD,0GAE4BhD,OAAWS,EAAIqC,EAAEM,sBAEjFN,EAAEO,OACE,sEAAsErD,0BAAmCA,OAAWS,EAClHqC,EAAEO,gBAEJ,oCAId,CAsF4BC,CAAgBvB,GAAa,qBACzCgB,8GAIsCvC,0CAA6CR,uBAEnFiC,EACI,8BAA8BxB,EAAIwB,SAClC,yNAWpB"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const t="#1256FF",n="#7C4EFF",e="#6635E0",i="#F5F5F7",l="#FFFFFF",r="#1D1D1F",o="#636366",a="#8E8E93",p="#D2D2D7",d="#E8E8ED",s="#F6F4FF",g="rgba(124,78,255,0.22)",c={ok:{bg:"#E8F9ED",color:"#1A7D37"},warn:{bg:"#FFF4E0",color:"#946200"},danger:{bg:"#FFEDEC",color:"#C93430"},info:{bg:"#EBF4FF",color:"#0055D4"}},x="'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif",h=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");function f(n){const e="primary"===(n.variant??"primary"),i=e?"#FFFFFF":t;return`\n <table role="presentation" cellpadding="0" cellspacing="0" style="margin:8px 8px 0 0;display:inline-block;">\n <tr>\n <td style="border-radius:10px;background:${e?t:l};border:1px solid ${e?t:p};">\n <a href="${h(function(t){try{const n=new URL(String(t));return"http:"===n.protocol||"https:"===n.protocol?n.href:"#"}catch{return"#"}}(n.url))}" target="_blank"\n style="display:inline-block;padding:12px 22px;font-family:${x};font-size:14px;font-weight:600;color:${i};text-decoration:none;border-radius:10px;">\n ${h(n.label)}\n </a>\n </td>\n </tr>\n </table>`}exports.buildEmail=function(t){const{preheader:p,greeting:m,title:$,paragraphs:b=[],highlights:y=[],aiParecer:u,buttons:F=[],footerNote:v}=t,w=y.length?Math.floor(100/y.length):100,E=y.length?`<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:8px 0 4px;"><tr>${y.map(t=>function(t,n){return`\n <td width="${n}%" style="padding:6px;" valign="top">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="background:#FAFAFA;border:1px solid ${d};border-radius:12px;">\n <tr><td style="padding:14px 16px;font-family:${x};">\n <div style="font-size:12px;color:${o};margin-bottom:4px;">${h(t.label)}</div>\n <div style="font-size:22px;font-weight:700;color:${t.color||r};font-family:'Lexend',${x};">${h(t.value)}</div>\n </td></tr>\n </table>\n </td>`}(t,w)).join("")}</tr></table>`:"",k=b.map(t=>`<p style="margin:0 0 14px;font-family:${x};font-size:15px;line-height:1.6;color:${o};">${h(t)}</p>`).join(""),z=F.length?`<div style="margin-top:18px;">${F.map(f).join("")}</div>`:"";return`<!DOCTYPE html>\n<html lang="pt-BR">\n<head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1.0" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge" />\n <title>${h($)}</title>\n</head>\n<body style="margin:0;padding:0;background:${i};">\n ${p?`<div style="display:none;max-height:0;overflow:hidden;opacity:0;">${h(p)}</div>`:""}\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${i};padding:24px 0;">\n <tr>\n <td align="center">\n <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px;max-width:92%;">\n <tr>\n <td style="padding:8px 4px 20px;">\n <img src="https://gyra-file-prod.s3.us-west-2.amazonaws.com/assets/gyra-logo-color.png" alt="GYRA+" height="30" style="height:30px;display:block;" />\n </td>\n </tr>\n <tr>\n <td style="background:${l};border:1px solid ${d};border-radius:16px;padding:32px;">\n ${m?`<p style="margin:0 0 6px;font-family:${x};font-size:14px;color:${o};">${h(m)}</p>`:""}\n <h1 style="margin:0 0 18px;font-family:'Lexend',${x};font-size:23px;line-height:1.3;color:${r};">${h($)}</h1>\n ${k}\n ${E}\n ${u?function(t){const i=t.verdict?(()=>{const n=c[t.verdictTone||"info"];return`<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;"><tr>\n <td style="background:${n.bg};border-radius:999px;padding:4px 10px;font-family:${x};font-size:11px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;color:${n.color};white-space:nowrap;">${h(t.verdict)}</td></tr></table>`})():"";return`\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"\n style="margin:18px 0 4px;background:${s};border:1px solid ${g};border-radius:12px;">\n <tr><td style="padding:16px 18px;font-family:${x};">\n <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>\n <td valign="middle" width="28">\n <div style="width:22px;height:22px;border-radius:50%;background:${n};color:#fff;text-align:center;line-height:22px;font-size:13px;">✦</div>\n </td>\n <td valign="middle" style="padding-left:8px;">\n <span style="font-size:11px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;color:${e};">Parecer GYRA+IA</span>\n </td>\n <td valign="middle" align="right">${i}</td>\n </tr></table>\n <p style="margin:10px 0 0;font-size:14px;line-height:1.55;color:${o};">${h(t.text)}</p>\n ${t.footer?`<div style="margin-top:10px;padding-top:10px;border-top:1px dashed ${g};font-size:11px;color:${a};">${h(t.footer)}</div>`:""}\n </td></tr>\n </table>`}(u):""}\n ${z}\n </td>\n </tr>\n <tr>\n <td style="padding:20px 8px;font-family:${x};font-size:12px;line-height:1.5;color:${a};">\n ${v?`<p style="margin:0 0 8px;">${h(v)}</p>`:""}\n <p style="margin:0;">GYRA+ • Esta é uma mensagem automática, não responda este email.</p>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`};
|
|
2
|
-
//# sourceMappingURL=index.js.map
|