Files
claude-plugins/crm-ops/skills/proposta-visual/SKILL.md
T

5.0 KiB

name, description, layer
name description layer
proposta-visual Criação completa de propostas comerciais visuais — página web (propostas.descomplicar.pt) + PDF exportável + PPTX editável. Recolhe dados CRM, gera JSON, renderiza condicionalmente e faz deploy. wiki

Skill /proposta-visual — Propostas Comerciais Visuais

Cria propostas comerciais profissionais com 3 outputs: página web interactiva, PDF exportável e PPTX editável.

Triggers

  • /proposta-visual
  • "proposta visual", "proposta web", "criar proposta", "nova proposta", "proposta para [cliente]"

Quando usar

  • Criar proposta comercial completa para um cliente
  • Gerar página web + PDF + PPTX de uma proposta
  • Actualizar proposta existente com novos dados

Quando NÃO usar

  • Apenas orçamento sem visual (usar /orcamento)
  • Apenas PPTX sem página web (usar /proposal-deck)
  • Análise de lead sem proposta (usar /lead-approach)

Projecto base

Campo Valor
Código fonte /media/ealmeida/Dados/Dev/Propostas/
Stack React 18 + Vite + Tailwind + @react-pdf/renderer + Recharts + Framer Motion + Lucide React
Live propostas.descomplicar.pt/{slug}
Deploy Docker Swarm EasyPanel, serviço descomplicar_propostas, ficheiros /opt/propostas/
DNS propostas.descomplicar.pt → 5.9.90.70 (Cloudflare proxied)
Template PPTX /media/ealmeida/Dados/Hub/90-Templates/Comercial/descomplicar-proposal-template.pptx

REGRAS CRÍTICAS (ler antes de qualquer implementação)

1. PT-PT obrigatório — acentos em TODO o texto

Todos os textos no JSON, componentes web e componentes PDF DEVEM ter acentos PT-PT correctos:

  • ✓ migração, tradução, integração, opções, condições, execução, acção
  • ✗ migracao, traducao, integracao, opcoes, condicoes, execucao, accao

Verificar no PDF: labels hardcoded em PDFWorkDone.jsx e PDFDocument.jsx — já corrigidos mas confirmar sempre.

2. JSON data-driven — renderização condicional obrigatória

O ProposalView.jsx e o PDFDocument.jsx renderizam secções condicionalmente com base nos dados. Se um campo é null, [] ou não existe, a secção NÃO aparece.

Regra: NUNCA assumir que todos os campos existem. Cada proposta pode ter um subconjunto diferente de secções.

Campo JSON Secção web Secção PDF Condição
context.brandStrategy BrandStrategy PageBrandStrategy != null
salesData SalesChart PageSales .length > 0
context.diagnosis Context — .length > 0
context.scores ScoreGrid PDFScores .length > 0
service360 Service360 PageService360 != null
plans PricingCards PDFPricing .length > 0
licenses LicensePack PageLicenses != null
workDone WorkDone PDFWorkDone .length > 0
roadmap Roadmap PDFWorkDone .length > 0
conditions Conditions PDFWorkDone (pág. separada) .length > 0

3. Título dinâmico — campo title no JSON

O título da proposta (Hero web + capa PDF) vem do campo title no JSON:

  • Proposta 360°: "title": "Descomplicar 360°"
  • Migração técnica: "title": "Migração Bookeo"
  • Website: "title": "Website Profissional"
  • Fallback: "Proposta Comercial"

NUNCA hardcodar "Descomplicar 360°" — usar data.title.

4. Preços flexíveis — não assumir mensalidades

Os planos suportam dois formatos de preço:

Mensalidade (avença):

{
  "monthly": 459,
  "hours": 8,
  "originalPrice": 540,
  "discount": 15
}

Mostra: "459 €/mês" + "8h/mês incluídas · contrato anual"

Preço único (investimento):

{
  "price": 750,
  "priceLabel": "EUR + IVA",
  "priceSubtitle": "Investimento único · prazo 1-2 semanas",
  "monthly": null,
  "hours": null
}

Mostra: "750 EUR + IVA" + "Investimento único · prazo 1-2 semanas"

Campos usados: plan.price ?? plan.monthly para o valor, plan.priceLabel ?? "€/mês" para a unidade, plan.priceSubtitle para a descrição abaixo.

5. react-pdf — bugs conhecidos e PROIBIÇÕES

Proibição Porquê Alternativa
gap + flexWrap juntos Crasha silenciosamente marginRight + marginBottom nos items
borderColor com rgba() Renderiza verde Cor sólida (ex: #D4A020, #E8E8E8)
Import de componentes SVG (<Svg>, <Circle>) Crasha web worker do PDFDownloadLink Implementar gráficos inline com <View>
Font.register com fontes locais/CDN em multi-página Falha silenciosa Usar Helvetica/Helvetica-Bold (built-in)
href vazio no PDFDownloadLink PDF não gerou — verificar consola Cache do browser — Ctrl+Shift+R
width: "100%" em bars/elementos dentro de flex container Não resolve — fica invisível Usar alignSelf: "stretch" no elemento e no container pai
Imagens com path absoluto (/home/...) react-pdf não acede ao filesystem local Colocar em public/assets/ e referenciar como "assets/foto.png" (s