feat(crm-ops): adiciona plugin crm-ops (agents, skills, knowledge e metadados do projeto)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
---
|
||||
name: proposta-visual
|
||||
description: 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.
|
||||
layer: 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):**
|
||||
```json
|
||||
{
|
||||
"monthly": 459,
|
||||
"hours": 8,
|
||||
"originalPrice": 540,
|
||||
"discount": 15
|
||||
}
|
||||
```
|
||||
Mostra: "459 €/mês" + "8h/mês incluídas · contrato anual"
|
||||
|
||||
**Preço único (investimento):**
|
||||
```json
|
||||
{
|
||||
"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
|
||||
Reference in New Issue
Block a user