versao com plano de fundo nao testado
This commit is contained in:
583
instrucoes.md
583
instrucoes.md
@@ -1,9 +1,13 @@
|
||||
# Convite Safari 🦁 — Manual de Instruções
|
||||
# Convites Digitais — Manual de Instruções
|
||||
|
||||
Este documento reúne **tudo o que é preciso para instalar, publicar e usar** o Convite Safari.
|
||||
Este documento reúne **tudo o que é preciso para instalar, publicar e manter** os Convites Digitais no servidor. É para o **administrador do sistema**.
|
||||
|
||||
- **Parte 1 — Deploy:** para quem vai instalar e manter a aplicação no servidor.
|
||||
- **Parte 2 — Uso:** para o organizador da festa e para os convidados.
|
||||
- **Parte 1 — Deploy:** instalação, configuração, operação, backup e solução de problemas.
|
||||
- **Parte 2 — Histórico de alterações:** o que mudou em cada versão e como atualizar o servidor.
|
||||
|
||||
> Para **usar o sistema** (criar eventos e convites, enviar, acompanhar respostas, PDFs), veja o [MANUAL-USUARIO.md](MANUAL-USUARIO.md). Ele é escrito para o organizador da festa, sem termos técnicos, e pode ser entregue a quem vai gerenciar os eventos.
|
||||
>
|
||||
> Para **desenvolver ou alterar o código** (arquitetura, banco de dados, desenvolvimento local, serviço Python), veja o [README.md](README.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -23,12 +27,11 @@ Este documento reúne **tudo o que é preciso para instalar, publicar e usar** o
|
||||
10. [Operação do dia a dia](#10-operação-do-dia-a-dia)
|
||||
11. [Solução de problemas](#11-solução-de-problemas)
|
||||
|
||||
**Parte 2 — Uso**
|
||||
**Parte 2 — Histórico de alterações**
|
||||
|
||||
12. [Conceitos](#12-conceitos)
|
||||
13. [Área do organizador](#13-área-do-organizador)
|
||||
14. [Experiência do convidado](#14-experiência-do-convidado)
|
||||
15. [Perguntas frequentes](#15-perguntas-frequentes)
|
||||
12. [Versão de 05/10/2026](#12-versão-de-05102026)
|
||||
13. [Versão de 06/10/2026 — PDFs dos convites](#13-versão-de-06102026--pdfs-dos-convites)
|
||||
14. [Versão de 07/10/2026 — Mensagem do WhatsApp personalizada](#14-versão-de-07102026--mensagem-do-whatsapp-personalizada)
|
||||
|
||||
---
|
||||
|
||||
@@ -36,24 +39,31 @@ Este documento reúne **tudo o que é preciso para instalar, publicar e usar** o
|
||||
|
||||
## 1. Visão geral da arquitetura
|
||||
|
||||
A aplicação roda em **dois containers** gerenciados pelo Docker Compose:
|
||||
A aplicação roda em **três containers** gerenciados pelo Docker Compose:
|
||||
|
||||
| Serviço | Imagem | Função |
|
||||
| --- | --- | --- |
|
||||
| `app` | construída pelo `Dockerfile` do projeto | Servidor Next.js (páginas, área do organizador, RSVP) na porta `3000` |
|
||||
| `db` | `postgres:17-alpine` | Banco de dados. Os dados ficam no volume `pgdata` |
|
||||
| `pdf-service` | construída por `pdf-service/Dockerfile` (Python) | Gera os PDFs personalizados dos convites. Só o `app` acessa |
|
||||
|
||||
```
|
||||
Convidado / Organizador
|
||||
│
|
||||
▼
|
||||
Cloudflare (HTTPS) ──► Servidor :3000 ──► container app ──► container db (Postgres)
|
||||
└─ volume pgdata
|
||||
│ └─ volume pgdata
|
||||
│ HTTP interno (http://pdf-service:8000)
|
||||
▼
|
||||
container pdf-service
|
||||
│
|
||||
volume app-storage ◄──────┴── (app e pdf-service: PDFs modelo e gerados)
|
||||
```
|
||||
|
||||
Pontos importantes:
|
||||
|
||||
- O banco **não fica exposto** para fora do servidor; só o container `app` o acessa.
|
||||
- O banco e o `pdf-service` **não ficam expostos** para fora do servidor; só o container `app` os acessa.
|
||||
- Os PDFs (modelos e gerados) ficam no volume Docker `app-storage`, **fora do banco**. O banco guarda só as informações sobre eles.
|
||||
- As **migrations** (criação/atualização das tabelas) são aplicadas **automaticamente** toda vez que o container `app` sobe.
|
||||
- Os dados ficam no volume Docker `pgdata` e **sobrevivem** a reinícios e rebuilds dos containers.
|
||||
|
||||
@@ -102,6 +112,7 @@ postcss.config.mjs
|
||||
drizzle.config.ts
|
||||
drizzle/ (migrations SQL)
|
||||
scripts/ (script de migration)
|
||||
pdf-service/ (serviço Python de PDFs)
|
||||
src/
|
||||
public/
|
||||
```
|
||||
@@ -127,12 +138,17 @@ nano .env
|
||||
| `POSTGRES_PASSWORD` | **sim** | `Xk29fLq81` | Senha do banco. **Só letras e números** |
|
||||
| `DATABASE_URL` | não* | — | Usada apenas fora do Docker. No compose é montada automaticamente |
|
||||
| `APP_PORT` | não | `3000` | Porta publicada no servidor |
|
||||
| `APP_URL` | recomendada | `https://convite.seudominio.com` | Endereço público do app, sem barra no final. Usado no card de pré-visualização do WhatsApp |
|
||||
| `ORGANIZER_PASSWORD` | **sim** | — | Senha de acesso à área do organizador |
|
||||
| `SESSION_SECRET` | **sim** | — | Chave de 32+ caracteres para assinar a sessão |
|
||||
| `SESSION_SECRET` | **sim** | — | Chave secreta de 32+ caracteres usada na verificação da senha |
|
||||
| `COOKIE_SECURE` | **sim** | `false` | `false` para acesso por `http://IP`; `true` atrás do Cloudflare (HTTPS) |
|
||||
| `RUN_MIGRATIONS` | não | `true` | `false` desliga a migration automática na subida |
|
||||
| `INVITE_LINK_BASE_URL` | não | `https://convite.n1b3.com.br` | Endereço usado no link gravado em cada PDF: `<valor>/r/<token>`, sem barra no final. **Em branco, usa o `APP_URL`.** Só defina se o link do PDF tiver de ser diferente do endereço do app |
|
||||
| `INVITE_LINK_PATTERN` | não | `com.br/r/` | Trecho que identifica, no PDF modelo, o link a ser trocado pelo link de cada convite |
|
||||
| `PDF_SERVICE_URL` | não* | `http://pdf-service:8000` | Endereço do serviço de PDFs. No compose é definido automaticamente |
|
||||
| `STORAGE_DIR` | não* | `/storage` | Pasta dos PDFs. No compose é definida automaticamente (volume `app-storage`) |
|
||||
|
||||
\* Não precisa alterar para o deploy com Docker.
|
||||
\* Não precisa alterar para o deploy com Docker. Essas variáveis só importam ao rodar fora do Docker (ver [README.md](README.md#variáveis-de-ambiente)).
|
||||
|
||||
### Gerar o `SESSION_SECRET`
|
||||
|
||||
@@ -158,12 +174,17 @@ POSTGRES_USER=convite
|
||||
POSTGRES_PASSWORD=Xk29fLq81mPz
|
||||
|
||||
APP_PORT=3000
|
||||
APP_URL=http://192.168.18.97:3000
|
||||
|
||||
ORGANIZER_PASSWORD=uma-senha-forte-para-o-admin
|
||||
SESSION_SECRET=3qD0x9bN1pY7sKcVb2T8wM4rH6jLz5aEoUfGiQ
|
||||
COOKIE_SECURE=false
|
||||
|
||||
INVITE_LINK_PATTERN=com.br/r/
|
||||
```
|
||||
|
||||
> Os PDFs usam o `APP_URL` como endereço do link. Para um endereço diferente, acrescente `INVITE_LINK_BASE_URL=https://...`.
|
||||
|
||||
> 🔒 O `.env` contém senhas. Não envie esse arquivo por e-mail/chat e restrinja o acesso: `chmod 600 .env`.
|
||||
|
||||
---
|
||||
@@ -176,8 +197,8 @@ docker compose up -d --build
|
||||
|
||||
O que acontece, em ordem:
|
||||
|
||||
1. A imagem do `app` é construída (pode levar alguns minutos na primeira vez).
|
||||
2. O Postgres sobe; o `app` aguarda o banco ficar saudável.
|
||||
1. As imagens do `app` e do `pdf-service` são construídas (pode levar alguns minutos na primeira vez).
|
||||
2. O Postgres e o `pdf-service` sobem; o `app` aguarda o banco ficar saudável.
|
||||
3. O `app` aplica as migrations pendentes.
|
||||
4. O servidor Next.js inicia na porta `APP_PORT`.
|
||||
|
||||
@@ -185,7 +206,7 @@ O que acontece, em ordem:
|
||||
|
||||
## 6. Verificar se está tudo funcionando
|
||||
|
||||
**Status dos containers** — ambos devem estar `running` (e o `db` como `healthy`):
|
||||
**Status dos containers** — os três devem estar `running` (e `db` e `pdf-service` como `healthy`):
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
@@ -226,6 +247,7 @@ Quando o Cloudflare for apontar para a aplicação (mesmo padrão das outras apl
|
||||
|
||||
```dotenv
|
||||
COOKIE_SECURE=true
|
||||
APP_URL=https://<seu-domínio>
|
||||
```
|
||||
|
||||
3. Recrie o container do app:
|
||||
@@ -253,6 +275,7 @@ Os links de convite são gerados a partir do endereço em que o organizador est
|
||||
```
|
||||
|
||||
4. Verifique os logs (`docker compose logs -f app`). Novas migrations são aplicadas automaticamente.
|
||||
5. Consulte o [Histórico de alterações](#parte-2--histórico-de-alterações) para saber se a versão pede algum passo extra (ex.: variável nova no `.env`).
|
||||
|
||||
Os dados **não são perdidos** em atualizações.
|
||||
|
||||
@@ -276,6 +299,22 @@ Gera um arquivo como `backup-2026-11-10.sql` na pasta atual. Guarde uma cópia *
|
||||
|
||||
(crie a pasta antes: `mkdir -p /opt/convite/backups`)
|
||||
|
||||
### Backup dos PDFs
|
||||
|
||||
Os PDFs modelo e gerados ficam no volume `app-storage`, **não** entram no `pg_dump`. Para copiá-los:
|
||||
|
||||
```bash
|
||||
docker run --rm -v convite_app-storage:/storage -v "$PWD":/backup alpine tar czf /backup/pdfs-$(date +%F).tar.gz -C /storage .
|
||||
```
|
||||
|
||||
Para restaurar (com os containers parados):
|
||||
|
||||
```bash
|
||||
docker run --rm -v convite_app-storage:/storage -v "$PWD":/backup alpine sh -c 'tar xzf /backup/pdfs-AAAA-MM-DD.tar.gz -C /storage && chown -R 1001:1001 /storage'
|
||||
```
|
||||
|
||||
> Os PDFs gerados podem ser refeitos a qualquer momento (botão **Gerar PDFs de todos os convites**). O que vale guardar é o **PDF modelo** de cada evento.
|
||||
|
||||
### Restauração
|
||||
|
||||
A restauração deve ser feita em um **banco vazio**. Para recriar o banco do zero:
|
||||
@@ -288,7 +327,7 @@ docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB"' < back
|
||||
docker compose up -d app
|
||||
```
|
||||
|
||||
> ⚠️ **Nunca** use `docker compose down -v` sem querer apagar o banco — o `-v` remove o volume `pgdata`.
|
||||
> ⚠️ **Nunca** use `docker compose down -v` sem querer apagar o banco — o `-v` remove os volumes `pgdata` e `app-storage` (PDFs).
|
||||
|
||||
---
|
||||
|
||||
@@ -299,12 +338,14 @@ docker compose up -d app
|
||||
| Ver status | `docker compose ps` |
|
||||
| Ver logs do app | `docker compose logs -f app` |
|
||||
| Ver logs do banco | `docker compose logs -f db` |
|
||||
| Ver logs do serviço de PDFs | `docker compose logs -f pdf-service` |
|
||||
| Reiniciar o serviço de PDFs | `docker compose restart pdf-service` |
|
||||
| Reiniciar o app | `docker compose restart app` |
|
||||
| Parar tudo (mantém dados) | `docker compose down` |
|
||||
| Subir novamente | `docker compose up -d` |
|
||||
| Console SQL | `docker compose exec db sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB"'` |
|
||||
| Trocar a senha do organizador | Edite `ORGANIZER_PASSWORD` no `.env` e rode `docker compose up -d app` |
|
||||
| Derrubar todas as sessões abertas | Troque o `SESSION_SECRET` no `.env` e rode `docker compose up -d app` |
|
||||
| Derrubar todas as sessões abertas | No `/admin`, clique em **Sair de todos** (no topo da página) |
|
||||
|
||||
Os containers estão com `restart: unless-stopped`: voltam sozinhos se o servidor reiniciar.
|
||||
|
||||
@@ -320,186 +361,402 @@ Os containers estão com `restart: unless-stopped`: voltam sozinhos se o servido
|
||||
| Login do `/admin` sempre volta para a tela de login | `COOKIE_SECURE=true` com acesso via HTTP | Use `false` no acesso por IP, ou acesse pelo domínio HTTPS |
|
||||
| Erro `SESSION_SECRET não definida` no log | Variável vazia ou curta | Gere um valor de 32+ caracteres (seção 4) |
|
||||
| "Senha incorreta." no login | `ORGANIZER_PASSWORD` diferente da digitada | Confira o `.env` e recrie o app após alterar |
|
||||
| "Muitas tentativas incorretas" no login | 5 senhas erradas em 15 minutos no mesmo IP | Aguarde o tempo indicado, ou reinicie o app (`docker compose restart app`) para zerar o bloqueio |
|
||||
| Link colado no WhatsApp aparece sem imagem | `APP_URL` ausente ou errada, ou o WhatsApp guardou uma versão antiga | Ajuste `APP_URL` no `.env`, rode `docker compose up -d app` e teste com um convite que ainda não foi compartilhado |
|
||||
| Build falha baixando fontes/pacotes | Servidor sem internet no build | Libere o acesso à internet durante o `docker compose build` |
|
||||
| Porta 3000 em uso | Outra aplicação na mesma porta | Altere `APP_PORT` no `.env` |
|
||||
| Link do convite abre com o IP em vez do domínio | Link copiado acessando o admin pelo IP | Acesse o `/admin` pelo domínio e copie de novo |
|
||||
| "Endereço do link não configurado" ao gerar PDF | Nem `APP_URL` nem `INVITE_LINK_BASE_URL` no `.env` | Defina `APP_URL` (seção 4) e rode `docker compose up -d pdf-service` |
|
||||
| "Serviço de PDF indisponível" ao enviar modelo ou gerar PDF | Container `pdf-service` parado ou com erro | `docker compose ps` e `docker compose logs pdf-service`; suba com `docker compose up -d pdf-service` |
|
||||
| "Nenhum link de convite (…/r/…) foi encontrado neste PDF" | O modelo não tem um link clicável com `INVITE_LINK_PATTERN` | Confira o link no PDF (precisa ser um link clicável, não só texto) ou ajuste `INVITE_LINK_PATTERN` |
|
||||
| Convite com "Erro ao gerar" | Falha ao gerar aquele PDF | A mensagem aparece no card. Veja `docker compose logs app pdf-service` e clique em **Gerar novamente** |
|
||||
| "Geração interrompida pela reinicialização do servidor" | O app reiniciou durante a geração | Clique em **Gerar só pendentes e com erro** |
|
||||
| Erro de permissão (`Permission denied`) em `/storage` nos logs | Arquivos do volume com outro dono | `docker compose run --rm --user root --entrypoint chown pdf-service -R 1001:1001 /storage` |
|
||||
| PDF baixado com link errado | `APP_URL` (ou `INVITE_LINK_BASE_URL`, se definida) errada | Corrija no `.env`, rode `docker compose up -d pdf-service` e gere os PDFs de novo |
|
||||
|
||||
---
|
||||
### 11.1 Diagnóstico dos PDFs
|
||||
|
||||
# Parte 2 — Uso
|
||||
|
||||
## 12. Conceitos
|
||||
|
||||
| Termo | Significado |
|
||||
| Onde olhar | Comando |
|
||||
| --- | --- |
|
||||
| **Evento** | A festa: nome, data, horário, local e endereço. Ex.: "1º Aniversário do Arthur" |
|
||||
| **Convite** | Um link enviado para uma família/grupo. Tem um **nome exibido** (ex.: "Família Silva") e uma lista de convidados |
|
||||
| **Convidado** | Cada pessoa do convite, marcada como **Adulto** ou **Criança** |
|
||||
| **Token** | Código único no final do link (`/r/Ab3xY9kL`). Funciona como a "chave" do convite |
|
||||
| **RSVP / Status** | Resposta de cada convidado: ✓ **Presente**, ✗ **Ausente** ou ⏳ **Pendente** |
|
||||
| Saúde do serviço de PDFs | `docker compose ps pdf-service` (deve estar `healthy`) |
|
||||
| Logs da geração (lado Python) | `docker compose logs -f pdf-service` — linhas `PDF gerado convite=...` ou o erro completo |
|
||||
| Logs da geração (lado app) | `docker compose logs -f app` — linhas `Falha ao gerar o PDF do convite ...` |
|
||||
| Arquivos no volume | `docker compose exec app ls -R /storage/events` |
|
||||
| Lotes recentes | `docker compose exec db sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB" -c "select status, processed, failed, total, created_at from pdf_jobs order by created_at desc limit 5"'` |
|
||||
| Testar o serviço por dentro | `docker compose exec app wget -qO- http://pdf-service:8000/health` |
|
||||
|
||||
Um evento pode ter vários convites; cada convite pode ter vários convidados.
|
||||
Para erros no código do serviço Python (traceback, links de um PDF, nível de log), veja "Manutenção do `pdf-service`" no [README.md](README.md#manutenção-do-pdf-service-código-python).
|
||||
|
||||
---
|
||||
|
||||
## 13. Área do organizador
|
||||
# Parte 2 — Histórico de alterações
|
||||
|
||||
### 13.1 Entrar
|
||||
## 12. Versão de 05/10/2026
|
||||
|
||||
1. Acesse `https://<seu-domínio>/admin` (ou `http://192.168.18.97:3000/admin` antes da publicação). Também há o link discreto **"Área do organizador"** no rodapé da página inicial.
|
||||
2. Digite a senha do organizador e clique em **Entrar**.
|
||||
### 12.1 Resumo
|
||||
|
||||
A sessão dura **7 dias** no mesmo navegador. Para sair, clique em **Sair** no topo da página.
|
||||
| # | Alteração | Para quem muda algo | Exige ação no deploy? |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Visual neutro (Slate) na área do organizador e na página inicial | Organizador | Não |
|
||||
| 2 | Limite de tentativas de login | Organizador | Não |
|
||||
| 3 | Sessões guardadas no banco + botão **Sair de todos** | Organizador | Sim: migration automática; todos precisam entrar de novo |
|
||||
| 4 | Nova variável `APP_URL` | Quem faz o deploy | Sim: adicionar ao `.env` |
|
||||
| 5 | Card de pré-visualização do convite no WhatsApp | Convidado | Depende do item 4 |
|
||||
| 6 | Exportação da lista de convidados em CSV | Organizador | Não |
|
||||
| 7 | Projeto renomeado para **Convites Digitais** e documentação atualizada | Todos | Não |
|
||||
|
||||
### 13.2 Painel de eventos
|
||||
|
||||
A tela inicial do organizador lista todos os eventos com:
|
||||
|
||||
- data, horário e local;
|
||||
- quantidade de convites;
|
||||
- resumo de presença: **Presentes** (com divisão adultos/crianças), **Ausentes** e **Pendentes**.
|
||||
|
||||
Clique em um evento para abrir os detalhes.
|
||||
|
||||
### 13.3 Criar um evento
|
||||
|
||||
1. No painel, clique em **+ Novo evento**.
|
||||
2. Preencha:
|
||||
- **Nome da festa** — ex.: "1º Aniversário do Arthur"
|
||||
- **Data** e **Horário**
|
||||
- **Local** — ex.: "Espaço Festa"
|
||||
- **Endereço** — ex.: "Rua das Flores, 100 - Belo Horizonte/MG"
|
||||
3. Clique em **Criar evento**. Você será levado à página do evento.
|
||||
|
||||
### 13.4 Criar um convite
|
||||
|
||||
1. Na página do evento, clique em **+ Novo convite**.
|
||||
2. Preencha o **Nome exibido no convite** — é o título grande que o convidado vê. Ex.: "Família Silva", "Tio João e Tia Ana".
|
||||
3. Adicione os convidados:
|
||||
- digite o nome na primeira linha e escolha **Adulto** ou **Criança**;
|
||||
- use **+ Adulto** / **+ Criança** para adicionar mais linhas;
|
||||
- use **✕** para remover uma linha.
|
||||
Linhas com o nome em branco são ignoradas.
|
||||
4. Clique em **Criar convite**.
|
||||
|
||||
O link do convite é gerado automaticamente.
|
||||
|
||||
> 💡 Dica: crie **um convite por família/núcleo**. Assim cada família responde por todos os seus membros de uma vez.
|
||||
|
||||
### 13.5 Enviar o convite
|
||||
|
||||
Em cada convite, na página do evento:
|
||||
|
||||
- **Copiar link** — copia o endereço completo (ex.: `https://seu-dominio/r/Ab3xY9kL`) para colar onde quiser.
|
||||
- **WhatsApp** — abre o WhatsApp com uma mensagem pronta contendo o nome da família, o nome da festa e o link. Basta escolher o contato e enviar.
|
||||
- **Abrir convite** — abre o convite em outra aba, exatamente como o convidado verá.
|
||||
|
||||
> 🔒 Cada link dá acesso àquele convite e permite responder por ele. **Envie apenas para a família correspondente** e não publique em grupos abertos ou redes sociais.
|
||||
|
||||
### 13.6 Acompanhar as respostas
|
||||
|
||||
Na página do evento:
|
||||
|
||||
- O **Resumo de presença** mostra o total de presentes (adultos e crianças), ausentes e pendentes de todo o evento — útil para fechar números com buffet e decoração.
|
||||
- Cada convite mostra seus convidados com o status colorido:
|
||||
- 🟢 **✓ Presente**
|
||||
- 🔴 **✗ Ausente**
|
||||
- 🟡 **⏳ Pendente**
|
||||
- Quando a família responde, aparece **"respondeu em dd/mm/aaaa hh:mm"** abaixo do nome.
|
||||
|
||||
Recarregue a página para ver respostas novas.
|
||||
|
||||
### 13.7 Editar um convite
|
||||
|
||||
1. No convite, clique em **Editar**.
|
||||
2. Altere o nome exibido, renomeie convidados, troque o tipo, adicione ou remova pessoas.
|
||||
3. Clique em **Salvar alterações**.
|
||||
|
||||
O link **não muda** ao editar. O status de cada convidado aparece ao lado do nome durante a edição.
|
||||
|
||||
> ⚠️ Remover um convidado apaga também a resposta dele. Convidados mantidos ou apenas renomeados **preservam** a resposta.
|
||||
|
||||
### 13.8 Zerar as respostas de um convite
|
||||
|
||||
Se uma família respondeu por engano e quer começar de novo:
|
||||
|
||||
1. No convite, clique em **Zerar respostas** (só aparece se já houve resposta).
|
||||
2. Confirme.
|
||||
|
||||
Todos os convidados daquele convite voltam para **Pendente**. O link continua o mesmo.
|
||||
|
||||
### 13.9 Excluir um convite
|
||||
|
||||
1. No convite, clique em **Excluir** e confirme.
|
||||
|
||||
O link deixa de funcionar imediatamente (o convidado verá "Convite não encontrado"). **Não é possível desfazer.**
|
||||
|
||||
### 13.10 Editar ou excluir um evento
|
||||
|
||||
1. Na página do evento, clique em **Editar evento**.
|
||||
2. Altere os dados e clique em **Salvar alterações** — as mudanças aparecem na hora em todos os convites do evento.
|
||||
3. Para excluir, use **Excluir evento** no final da página e confirme.
|
||||
|
||||
> ⚠️ Excluir um evento apaga **todos os convites e respostas** dele. Não é possível desfazer.
|
||||
Os passos para atualizar o servidor estão na [seção 12.9](#129-roteiro-de-atualização-desta-versão).
|
||||
|
||||
---
|
||||
|
||||
## 14. Experiência do convidado
|
||||
### 12.2 Visual neutro na área do organizador
|
||||
|
||||
O convidado **não precisa de cadastro nem senha** — só do link recebido.
|
||||
**Antes:** login, painel e página inicial usavam as cores e o 🦁 do tema Safári, mesmo em eventos com outro tema.
|
||||
|
||||
### 14.1 Abrir o convite
|
||||
**Agora:**
|
||||
|
||||
Ao abrir o link, o convidado vê:
|
||||
- Paleta **Slate / Grafite**: fundo cinza-claro liso, cards brancos, textos em cinza-azulado e botões principais em índigo.
|
||||
- O 🦁 foi trocado por um ícone de calendário no login, no cabeçalho do painel e na página inicial.
|
||||
- A página inicial agora diz "Convites Digitais" em vez de "Convites Safari".
|
||||
- Contorno de foco, seleção de texto e checkboxes da área do organizador também usam índigo.
|
||||
- A página **"Convite não encontrado"** ficou neutra (ícone de lupa, fundo cinza-claro).
|
||||
- A cor da barra do navegador no celular agora é cinza-claro no app e **a cor do tema** na página de cada convite.
|
||||
|
||||
1. O **nome do convite** em destaque (ex.: "Família Silva — esperamos vocês para celebrar!").
|
||||
2. Os **detalhes da festa**: data, horário, local e endereço.
|
||||
3. A **lista de convidados** daquele convite, cada um com seu status atual.
|
||||
4. O quadro de **Confirmação de Presença**, com a contagem de presentes, ausentes e pendentes.
|
||||
**O que continua igual, de propósito:**
|
||||
|
||||
### 14.2 Responder
|
||||
- As cores de status: verde = presente, vermelho = ausente, âmbar = pendente.
|
||||
- A prévia do tema no formulário de evento (mostra as cores do tema escolhido).
|
||||
- O botão **WhatsApp** (verde).
|
||||
- As páginas dos convites, que continuam com o tema escolhido para cada evento.
|
||||
|
||||
1. Para cada pessoa da lista, toque em:
|
||||
- **✓** para confirmar presença, ou
|
||||
- **✗** para informar ausência.
|
||||
Para mudar de ideia antes de enviar, basta tocar no outro botão.
|
||||
2. Toque em **Confirmar Presença**.
|
||||
3. Aparece a tela **"Presença Confirmada!"** com o resumo de quem vai e quem não vai. Toque em **Fechar**.
|
||||
**Arquivos:** `src/app/admin/**`, `src/components/admin/**`, `src/app/page.tsx`, `src/app/r/[token]/not-found.tsx`, `src/app/globals.css` (classe `.admin-ui`) e `src/components/ui/index.ts`. `Button` e `Input` ganharam a opção `tone="neutral"`; o padrão continua sendo as cores do tema, usadas no convite.
|
||||
|
||||
Não é obrigatório responder por todos de uma vez: quem ficar como pendente pode ser respondido depois.
|
||||
---
|
||||
|
||||
### 14.3 Mudar a resposta
|
||||
### 12.3 Limite de tentativas de login
|
||||
|
||||
O convidado pode abrir o **mesmo link** a qualquer momento. As respostas já enviadas aparecem marcadas; basta trocar (ex.: de ✓ para ✗) e tocar em **Confirmar Presença** novamente.
|
||||
**Antes:** a senha do `/admin` podia ser testada sem limite.
|
||||
|
||||
### 14.4 Mensagens possíveis
|
||||
**Agora:**
|
||||
|
||||
| Mensagem | Significado |
|
||||
- Depois de **5 senhas erradas em 15 minutos**, o login fica **bloqueado por 15 minutos** para aquele IP.
|
||||
- Durante o bloqueio a senha nem é conferida, e a tela mostra quantos minutos faltam.
|
||||
- Um login certo zera o contador.
|
||||
|
||||
**Detalhes técnicos:**
|
||||
|
||||
- Os contadores ficam **na memória do app**. Reiniciar o container (`docker compose restart app`) zera todos os bloqueios.
|
||||
- O IP vem do cabeçalho `cf-connecting-ip`, enviado pelo Cloudflare. Sem ele, o app usa `x-forwarded-for` ou `x-real-ip`.
|
||||
- ⚠️ Quem acessa o servidor **direto pelo IP**, sem passar pelo Cloudflare, consegue forjar esses cabeçalhos e escapar do limite. A proteção completa é o servidor aceitar só tráfego vindo do Cloudflare (Cloudflare Tunnel ou regra de firewall).
|
||||
|
||||
**Arquivos:** `src/lib/login-rate-limit.ts` (novo) e `src/app/admin/auth-actions.ts`.
|
||||
|
||||
---
|
||||
|
||||
### 12.4 Sessões no banco e "Sair de todos"
|
||||
|
||||
**Antes:** o cookie de sessão se validava sozinho, por uma assinatura. **Sair** só apagava o cookie do navegador: uma cópia dele continuava valendo por até 7 dias. Para derrubar todas as sessões era preciso trocar o `SESSION_SECRET` e recriar o app.
|
||||
|
||||
**Agora:**
|
||||
|
||||
- Cada login cria uma linha na tabela nova **`organizer_sessions`**. O cookie guarda um token aleatório, e o banco guarda só o **hash** dele.
|
||||
- Toda página e ação do organizador confere se a sessão ainda existe e não venceu (continua durando 7 dias).
|
||||
- **Sair** apaga a sessão no banco, então uma cópia do cookie para de funcionar.
|
||||
- **Sair de todos**, um botão novo no topo do painel com confirmação, apaga as sessões de todos os dispositivos.
|
||||
- Sessões vencidas são apagadas a cada novo login.
|
||||
- O `SESSION_SECRET` continua **obrigatório**: agora ele é usado na verificação da senha.
|
||||
|
||||
**Impacto no deploy:**
|
||||
|
||||
- Migration nova `drizzle/0004_organizer_sessions.sql`, aplicada automaticamente quando o container sobe.
|
||||
- Os cookies antigos deixam de valer: **depois da atualização, todos precisam entrar de novo**.
|
||||
|
||||
**Arquivos:** `src/db/schema.ts`, `src/lib/auth.ts`, `src/app/admin/auth-actions.ts`, `src/app/admin/(painel)/layout.tsx` e `drizzle/0004_organizer_sessions.sql`.
|
||||
|
||||
---
|
||||
|
||||
### 12.5 Variável `APP_URL`
|
||||
|
||||
**Antes:** o endereço base usado nos links das pré-visualizações estava fixo no código como `http://localhost:3000`.
|
||||
|
||||
**Agora:** esse endereço vem da variável **`APP_URL`** do `.env`.
|
||||
|
||||
| Situação | Valor de `APP_URL` |
|
||||
| --- | --- |
|
||||
| **Convite não encontrado** | Link digitado errado, incompleto ou convite excluído pelo organizador |
|
||||
| **Não foi possível enviar sua resposta. Tente novamente.** | Falha de conexão ou o convite foi alterado no meio do caminho. Recarregue a página e responda de novo |
|
||||
| Acesso pelo IP (antes do Cloudflare) | `http://192.168.18.97:3000` |
|
||||
| Depois da publicação no Cloudflare | `https://<seu-domínio>` |
|
||||
|
||||
- Escreva **sem barra no final**.
|
||||
- O valor é lido com o app rodando: depois de alterar, basta `docker compose up -d app` (sem rebuild).
|
||||
- Se a variável faltar ou for inválida, o app usa `http://localhost:3000` e registra um aviso no log. Nesse caso o card do WhatsApp (12.6) fica sem imagem.
|
||||
|
||||
**Arquivos:** `src/lib/app-url.ts` (novo), `src/app/layout.tsx` e `.env.example`.
|
||||
|
||||
---
|
||||
|
||||
## 15. Perguntas frequentes
|
||||
### 12.6 Card de pré-visualização no WhatsApp
|
||||
|
||||
**O convidado precisa instalar algum aplicativo?**
|
||||
Não. Funciona no navegador do celular ou do computador.
|
||||
**Antes:** o link do convite colado no WhatsApp aparecia sem imagem e com um título genérico.
|
||||
|
||||
**Posso usar o mesmo sistema para várias festas?**
|
||||
Sim. Crie um evento para cada festa; cada um tem seus próprios convites e totais.
|
||||
**Agora:** aparece um card com:
|
||||
|
||||
**Duas pessoas da mesma família podem responder pelo mesmo link?**
|
||||
Sim. Vale a última resposta enviada para cada convidado.
|
||||
- **imagem** (1200×630) nas cores do tema do evento, com o selo e o ícone do tema, "*Família*, você está convidado para", o nome da festa, data · horário · local e "Toque para confirmar sua presença";
|
||||
- **título** "*Família*, você está convidado!";
|
||||
- **descrição** com o nome da festa, a data, o horário e o local.
|
||||
|
||||
**Um convidado novo apareceu depois que a família já respondeu. O que faço?**
|
||||
Edite o convite e adicione a pessoa. Ela aparece como **Pendente** e as respostas anteriores dos outros são mantidas. Peça para a família abrir o link de novo e responder por ela.
|
||||
**Detalhes:**
|
||||
|
||||
**Enviei o link errado para uma família. Como invalido?**
|
||||
Exclua o convite e crie outro — o novo convite terá um link diferente.
|
||||
- A imagem mostra só o que a página do convite já mostra para quem tem o link. **A lista de convidados não aparece.**
|
||||
- A imagem é gerada na hora, então edições no evento aparecem nos próximos compartilhamentos.
|
||||
- O WhatsApp **guarda o card de cada link por um tempo**: links compartilhados antes da atualização podem continuar sem imagem.
|
||||
- Os emojis da imagem são baixados da internet ao gerar o card: o servidor precisa de acesso à internet.
|
||||
- A mensagem pronta do botão **WhatsApp** agora usa o **ícone do tema do evento** (antes era sempre 🦁).
|
||||
|
||||
**Esqueci a senha do organizador.**
|
||||
Quem administra o servidor consulta/altera `ORGANIZER_PASSWORD` no `.env` e recria o app (`docker compose up -d app`).
|
||||
**Arquivos:** `src/app/r/[token]/opengraph-image.tsx` (novo), `src/app/r/[token]/page.tsx` e `src/components/admin/ShareInvitation.tsx`.
|
||||
|
||||
**Os dados ficam salvos se o servidor reiniciar?**
|
||||
Sim. Os dados ficam no banco Postgres, em um volume persistente. Mesmo assim, mantenha backups (seção 9).
|
||||
---
|
||||
|
||||
### 12.7 Exportar a lista de convidados (CSV)
|
||||
|
||||
**Agora:** na página do evento, abaixo do **Resumo de presença**, em **Exportar lista (CSV)**, há os botões:
|
||||
|
||||
| Botão | Conteúdo do arquivo |
|
||||
| --- | --- |
|
||||
| **Todos** | Todos os convidados do evento |
|
||||
| **Presentes** | Só quem confirmou |
|
||||
| **Ausentes** | Só quem recusou (e, depois do prazo, quem não respondeu) |
|
||||
| **Pendentes** | Só quem ainda não respondeu |
|
||||
|
||||
- **Colunas:** Convite, Convidado, Tipo (Adulto/Adolescente/Criança), Status e Respondido em (horário de Brasília).
|
||||
- **Abre direto no Excel em português** e no Google Planilhas, com colunas e acentos corretos (separador `;`, UTF-8).
|
||||
- **Nome do arquivo:** parte do nome da festa e do filtro, ex.: `1-aniversario-do-arthur-presentes.csv`.
|
||||
- Só funciona com o organizador logado. Sem login, o endereço redireciona para a tela de login.
|
||||
- Nomes que começam com `=`, `+`, `-` ou `@` recebem um apóstrofo na frente, para o Excel não tratá-los como fórmula.
|
||||
|
||||
**Arquivos:** `src/app/admin/(painel)/eventos/[id]/exportar/route.ts` (novo) e `src/app/admin/(painel)/eventos/[id]/page.tsx`.
|
||||
|
||||
---
|
||||
|
||||
### 12.8 Projeto renomeado e documentação
|
||||
|
||||
- **Novo nome:** "Convite Safari" passou a se chamar **Convites Digitais** no `README.md`, no `instrucoes.md` e na página inicial.
|
||||
- **Documentação atualizada:** o README e o `instrucoes.md`, que na época também trazia o manual de uso (hoje no `MANUAL-USUARIO.md`, ver 14.2), passaram a descrever o que já existia e não estava documentado (temas, prazo de confirmação, convidados adolescentes, textos de selo e rodapé) e as novidades desta versão.
|
||||
- **Mantido:** o tema padrão no banco continua `safari`, só para eventos antigos criados antes da escolha de tema. O tema **Safári** continua disponível normalmente.
|
||||
|
||||
---
|
||||
|
||||
### 12.9 Roteiro de atualização desta versão
|
||||
|
||||
1. **Backup do banco** (esta versão cria uma tabela nova):
|
||||
|
||||
```bash
|
||||
docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > backup-$(date +%F).sql
|
||||
```
|
||||
|
||||
2. **Copie o código novo** para o servidor (mesmo `rsync` da seção 3).
|
||||
3. **Adicione `APP_URL` ao `.env`** do servidor (ver 12.5):
|
||||
|
||||
```dotenv
|
||||
APP_URL=http://192.168.18.97:3000
|
||||
```
|
||||
|
||||
4. **Reconstrua e suba o app:**
|
||||
|
||||
```bash
|
||||
docker compose up -d --build app
|
||||
```
|
||||
|
||||
5. **Confira os logs** (`docker compose logs -f app`): deve aparecer `Migrations aplicadas.` e nenhum aviso de `APP_URL inválida`.
|
||||
6. **Valide:**
|
||||
- abra `/admin`: a tela de login deve estar no visual neutro; entre com a senha (as sessões antigas foram encerradas);
|
||||
- abra um evento e baixe um CSV em **Exportar lista (CSV)**;
|
||||
- cole o link de um convite **ainda não compartilhado** no WhatsApp e confira o card com imagem.
|
||||
7. **Quando publicar no Cloudflare**, além de `COOKIE_SECURE=true`, troque `APP_URL` para `https://<seu-domínio>` e rode `docker compose up -d app`.
|
||||
|
||||
---
|
||||
|
||||
## 13. Versão de 06/10/2026 — PDFs dos convites
|
||||
|
||||
### 13.1 Resumo
|
||||
|
||||
| # | Alteração | Para quem muda algo | Exige ação no deploy? |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | PDF modelo por evento (upload, substituição, download) | Organizador | Não |
|
||||
| 2 | Geração do PDF personalizado por convite e em lote, com progresso | Organizador | Não |
|
||||
| 3 | Novo container `pdf-service` (Python) e volume `app-storage` | Quem faz o deploy | Sim: build do novo serviço |
|
||||
| 4 | Novas variáveis opcionais `INVITE_LINK_BASE_URL` (padrão: `APP_URL`) e `INVITE_LINK_PATTERN` | Quem faz o deploy | Só conferir se `APP_URL` está correto |
|
||||
| 5 | Migration `0005_pdf_generation` (colunas de PDF e tabela `pdf_jobs`) | — | Automática na subida |
|
||||
|
||||
Os passos para atualizar o servidor estão na [seção 13.3](#133-roteiro-de-atualização-desta-versão).
|
||||
|
||||
---
|
||||
|
||||
### 13.2 O que mudou
|
||||
|
||||
- **Para o organizador:**
|
||||
- seção **PDF do convite** na página do evento;
|
||||
- status e botões de PDF em cada convite;
|
||||
- geração em lote com progresso.
|
||||
|
||||
Uso detalhado na [seção 2.11 do MANUAL-USUARIO.md](MANUAL-USUARIO.md#211-pdfs-dos-convites).
|
||||
- **No servidor:**
|
||||
- container novo `pdf-service` (Python), sem porta publicada;
|
||||
- volume novo `app-storage` com os PDFs, montado em `/storage` no `app` e no `pdf-service`;
|
||||
- migration `0005_pdf_generation`, aplicada na subida.
|
||||
- **Variáveis:** `INVITE_LINK_BASE_URL` (opcional; em branco usa o `APP_URL`) e `INVITE_LINK_PATTERN` (padrão `com.br/r/`). Ver [seção 4](#4-configurar-as-variáveis-de-ambiente).
|
||||
- **Segurança:**
|
||||
- downloads e ações exigem o organizador logado;
|
||||
- o convite precisa pertencer ao evento da URL;
|
||||
- o navegador nunca envia caminhos de arquivo;
|
||||
- o upload aceita só PDF de até 10 MB.
|
||||
|
||||
Arquitetura, banco, endpoints e manutenção do código estão no [README.md](README.md#pdfs-dos-convites).
|
||||
|
||||
**Arquivos:**
|
||||
|
||||
- **Novos:**
|
||||
- `pdf-service/` (Dockerfile, `requirements.txt`, `app/main.py`);
|
||||
- `src/lib/pdf/` (`storage.ts`, `service.ts`, `jobs.ts`, `state.ts`, `types.ts`);
|
||||
- `src/app/admin/pdf-actions.ts`;
|
||||
- `src/components/admin/EventPdf.tsx`;
|
||||
- `src/instrumentation.ts`;
|
||||
- `src/lib/slugify.ts`;
|
||||
- rotas em `src/app/admin/(painel)/eventos/[id]/pdf/` e `.../convites/[conviteId]/pdf/`;
|
||||
- `drizzle/0005_pdf_generation.sql`.
|
||||
- **Alterados:**
|
||||
- `src/db/schema.ts`;
|
||||
- `src/app/admin/actions.ts` (apaga os PDFs ao excluir evento ou convite);
|
||||
- página do evento;
|
||||
- `next.config.ts` (limite de upload de 11 MB nas Server Actions);
|
||||
- `Dockerfile`, `compose.yml`, `.env.example`, `.dockerignore` e `.gitignore`.
|
||||
|
||||
---
|
||||
|
||||
### 13.3 Roteiro de atualização desta versão
|
||||
|
||||
1. **Backup do banco** (esta versão altera tabelas):
|
||||
|
||||
```bash
|
||||
docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > backup-$(date +%F).sql
|
||||
```
|
||||
|
||||
2. **Copie o código novo** para o servidor (mesmo `rsync` da seção 3). Confira que a pasta `pdf-service/` foi copiada.
|
||||
3. **Confira o `.env`** do servidor: o `APP_URL` deve ser o endereço público (`https://convite.n1b3.com.br`), porque os PDFs usam esse endereço no link. As variáveis novas são opcionais:
|
||||
|
||||
```dotenv
|
||||
# INVITE_LINK_BASE_URL=https://convite.n1b3.com.br # só se o link do PDF tiver de ser diferente do APP_URL
|
||||
INVITE_LINK_PATTERN=com.br/r/ # padrão; pode omitir
|
||||
```
|
||||
|
||||
4. **Construa e suba tudo** (o `pdf-service` é novo):
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
5. **Confira:** `docker compose ps` deve mostrar o `pdf-service` como `healthy`, e os logs do app devem mostrar `Migrations aplicadas.`.
|
||||
6. **Valide:**
|
||||
- abra um evento e envie o PDF modelo em **PDF do convite**;
|
||||
- gere o PDF de um convite e baixe o arquivo;
|
||||
- abra o PDF e clique no link: deve abrir `<APP_URL>/r/<token do convite>`;
|
||||
- clique em **Gerar PDFs de todos os convites** e acompanhe o progresso;
|
||||
- rode `docker compose restart` e confira que os PDFs continuam disponíveis para download.
|
||||
|
||||
---
|
||||
|
||||
## 14. Versão de 07/10/2026 — Mensagem do WhatsApp personalizada
|
||||
|
||||
### 14.1 Resumo
|
||||
|
||||
| # | Alteração | Para quem muda algo | Exige ação no deploy? |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Campo **Mensagem do WhatsApp** no evento, com variáveis e formatação do WhatsApp | Organizador | Não |
|
||||
| 2 | Pré-visualização da mensagem no formulário do evento | Organizador | Não |
|
||||
| 3 | Migration `0006_event_whatsapp_message` (coluna `whatsapp_message` em `events`) | — | Automática na subida |
|
||||
| 4 | Manual de uso separado em `MANUAL-USUARIO.md` | Organizador e administrador | Não |
|
||||
|
||||
Eventos já existentes ficam com o campo vazio e passam a usar a **nova mensagem padrão** (ver 14.2) a partir dos próximos cliques no botão. Mensagens já enviadas não mudam.
|
||||
|
||||
---
|
||||
|
||||
### 14.2 O que mudou
|
||||
|
||||
**Antes:** a mensagem do botão **WhatsApp** era fixa no código: "Olá, *Família*! Vocês estão convidados para o *festa* *ícone*", seguida do link.
|
||||
|
||||
**Agora:**
|
||||
|
||||
- O formulário do evento tem o campo **Mensagem do WhatsApp**, com:
|
||||
- botões para inserir as variáveis `{nome}`, `{evento}`, `{data}`, `{hora}`, `{local}`, `{endereco}`, `{icone}` e `{link}`;
|
||||
- uma linha de ajuda com a formatação do WhatsApp;
|
||||
- três contadores: **Modelo**, **Texto final (exemplo)** e **Link do WhatsApp (exemplo)**;
|
||||
- uma **pré-visualização** no estilo do WhatsApp, que aplica negrito, itálico, riscado, monoespaçado e código. Em navegadores sem suporte (Safari < 16.4), a prévia aparece sem formatação, sem quebrar o formulário.
|
||||
- O botão **WhatsApp** de cada convite monta o texto a partir do modelo do evento.
|
||||
- Botão novo **Copiar mensagem** em cada convite: copia o mesmo texto do botão WhatsApp, para enviar por outro aplicativo.
|
||||
- **Nova mensagem padrão**, usada com o campo vazio:
|
||||
|
||||
```
|
||||
Olá, {nome}! Chegou o seu convite para: *{evento}* {icone}
|
||||
Confirme a presença: {link}
|
||||
```
|
||||
|
||||
A anterior ("…convidados para **o** *festa*") só combinava com nomes de festa masculinos no singular e com convites de família: "para o Festa da Maria" e "Vocês" num convite de uma pessoa só. A nova não depende do nome da festa nem de quantas pessoas há no convite.
|
||||
- **Outros textos com a mesma correção:**
|
||||
|
||||
| Onde | Antes | Agora |
|
||||
| --- | --- | --- |
|
||||
| Título do card do link no WhatsApp | "*Família*, você está convidado!" | "*Família*, chegou o seu convite!" |
|
||||
| Imagem do card | "*Família*, você está convidado para" | "*Família*, chegou o seu convite para" |
|
||||
| Topo da página do convite (e prévia do tema no formulário do evento) | "esperamos vocês para celebrar!" | "estamos esperando por você(s)!" |
|
||||
|
||||
Links já compartilhados podem continuar mostrando o card antigo por um tempo, porque o WhatsApp guarda o card (ver 12.6).
|
||||
- Sem `{link}` na mensagem, o link é adicionado no final.
|
||||
- **Limite de 950 caracteres**, com margem abaixo de 1000 para o link do `wa.me`. Ele vale para o modelo e também para o texto final, já com as variáveis trocadas.
|
||||
- O texto final é conferido no servidor ao criar ou editar o evento, com os nomes de todos os convites, e ao criar ou editar um convite.
|
||||
- Nessa conferência, o link é estimado com o `APP_URL` e um código de 8 caracteres.
|
||||
- A mensagem padrão (campo vazio) não é limitada.
|
||||
- **Limite de 2300 caracteres no link codificado** (`https://wa.me/?text=...`), conferido nos mesmos momentos. Ele existe porque, no link, acentos ocupam 6 caracteres e emojis até 12. O WhatsApp não publica um limite oficial; os dois valores são margens de segurança.
|
||||
- **Mensagem por evento:** não há mensagem diferente por convite. Para personalizar o texto de uma família, o organizador edita a mensagem no próprio WhatsApp antes de enviar.
|
||||
|
||||
Uso detalhado na [seção 2.5.1 do MANUAL-USUARIO.md](MANUAL-USUARIO.md#251-personalizar-a-mensagem-do-whatsapp).
|
||||
|
||||
**Arquivos:**
|
||||
|
||||
- **Novos:**
|
||||
- `src/lib/whatsapp-message.ts` (mensagem padrão, variáveis, limite e montagem do texto);
|
||||
- `src/components/admin/WhatsAppMessageField.tsx` (campo, botões de variáveis e pré-visualização);
|
||||
- `drizzle/0006_event_whatsapp_message.sql`.
|
||||
- **Alterados:**
|
||||
- `src/db/schema.ts` (coluna `whatsappMessage`);
|
||||
- `src/app/admin/actions.ts` (leitura e validação do campo; conferência do texto final ao salvar evento e convite);
|
||||
- `src/components/admin/EventForm.tsx` (campo novo; data, horário, local e endereço alimentam a pré-visualização);
|
||||
- `src/components/admin/ShareInvitation.tsx` (usa o modelo do evento; botão Copiar mensagem);
|
||||
- `src/app/r/[token]/page.tsx`, `src/app/r/[token]/opengraph-image.tsx` e `src/components/invitation/InvitationHero.tsx` (frases neutras);
|
||||
- página do evento e página de edição do evento.
|
||||
|
||||
**Documentação:**
|
||||
|
||||
- O manual de uso, que era a antiga Parte 2 deste arquivo, foi movido para o [MANUAL-USUARIO.md](MANUAL-USUARIO.md). Ele foi reescrito sem termos técnicos e ganhou a seção **"Quando falar com o administrador"**.
|
||||
- Este arquivo ficou só com o deploy (Parte 1) e o histórico, que passou a ser a Parte 2. As seções do histórico foram renumeradas de 16–18 para 12–14.
|
||||
|
||||
---
|
||||
|
||||
### 14.3 Roteiro de atualização desta versão
|
||||
|
||||
1. **Backup do banco** (esta versão adiciona uma coluna):
|
||||
|
||||
```bash
|
||||
docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > backup-$(date +%F).sql
|
||||
```
|
||||
|
||||
2. **Copie o código novo** para o servidor (mesmo `rsync` da seção 3).
|
||||
3. **Reconstrua e suba o app:**
|
||||
|
||||
```bash
|
||||
docker compose up -d --build app
|
||||
```
|
||||
|
||||
4. **Confira os logs** (`docker compose logs -f app`): deve aparecer `Migrations aplicadas.`.
|
||||
5. **Valide:**
|
||||
- em um evento existente com o campo vazio, clique em **WhatsApp** num convite: deve aparecer a nova mensagem padrão ("Chegou o seu convite para: …");
|
||||
- em **Editar evento**, escreva uma mensagem com `*negrito*` e `{nome}` e confira a pré-visualização;
|
||||
- salve, clique em **WhatsApp** e confira no WhatsApp o nome da família, a formatação e o link.
|
||||
|
||||
Reference in New Issue
Block a user