O menu Comunicações (/dashboard/communications) reúne tudo o que o casal
envia aos convidados:
| Módulo | Rota | Para quê |
|---|---|---|
| Save the Date | /dashboard/communications/save-the-date |
Anunciar a data para todos (save-the-date.md) |
| Convites Oficiais | /dashboard/communications/invitations |
Convite formal com PIN e RSVP (invitations.md) |
| Avisos | /dashboard/communications/announcements |
Lembretes e informações com texto livre, para um público escolhido |
As rotas antigas /dashboard/save-the-date e /dashboard/invitations
redirecionam com 308 (redirects() em next.config.ts).
O hub mostra o status do último envio de cada módulo e as abas no topo
(communications/layout.tsx) permitem alternar entre eles.
Um aviso é uma mensagem livre — nenhuma tag é obrigatória — enviada a um público filtrado. Caso típico: lembrar só os confirmados do horário e do traje.
Broadcast com kind = "ANNOUNCEMENT" (sem tabela nova).Broadcast em status = "DRAFT" e abre o editor
em /announcements/[id]. O worker só processa SENDING, então rascunhos
nunca são enviados sozinhos.DRAFT para SENDING
(updateMany filtrado por status). Depois disso o aviso é somente leitura;
use Duplicar para reaproveitar (o anexo é copiado para o novo aviso).DRAFT → SENDING → DONE (ou CANCELLED).Tags opcionais (mesmos significados do Save the Date):
| Tag | Valor |
|---|---|
{nomes} |
nomes do casal |
{convidados} |
integrantes da família que recebem o aviso (só os que batem com o filtro) |
{data} |
data do casamento por extenso, no idioma do destinatário |
{local} |
primeiro local cadastrado (pré-selecionados primeiro) |
{site} / {site-presentes} |
links do site e da lista de presentes |
{link-rsvp} |
link de RSVP (nativo ou externo, conforme a configuração dos convites) |
{pin} |
PIN do RSVP — se usado, famílias sem PIN ficam de fora (NO_PIN) |
validateArtFile (src/lib/art-upload.ts) e salvo
em uploads/announcement/<id>/. O caminho do arquivo é definido só pelo servidor.O filtro é aplicado convidado a convidado e depois agrupado por família (announcement-recipients.ts):
INVITED), Talvez,
Recusaram. NOT_INVITED nunca recebe.excludedRefs no rascunho.Regras de envio:
GuestGroup), no contato do grupo, citando só
os integrantes elegíveis. Família sem nenhum elegível não aparece.contactPhone/contactEmail do grupo → primeiro
integrante elegível com contato → qualquer integrante. O contato do grupo
recebe mesmo que a pessoa dona dele não bata com o filtro.Escolhido por aviso (channel-policy.ts):
| Canal | Comportamento | Fica de fora |
|---|---|---|
| WhatsApp com e-mail de reserva (padrão) | Tenta WhatsApp; se falhar ou não houver telefone, e-mail | sem telefone e sem e-mail |
| Somente WhatsApp | Só WhatsApp, sem fallback | sem telefone válido |
| Somente e-mail | Só e-mail | sem e-mail |
payloadJson do Broadcast no início; cada BroadcastRecipient guarda seus
nomes, idioma, contato, link e PIN. Alterações posteriores no evento não
mudam um aviso em andamento.DELIVERY_STATE_UNKNOWN), cancelar o que ainda está na fila, exportar CSV e
duplicar.| Ação | ADMIN / GROOM / BRIDE | PLANNER | FAMILY / VIEWER |
|---|---|---|---|
| Ver hub, histórico e relatórios | ✅ | ✅ | 🚫 redirect |
| Criar/editar/duplicar/excluir rascunho, enviar teste | ✅ | ✅ (denyIfNoEdit) |
🚫 |
| Iniciar, reenviar falhas, cancelar | ✅ | 🚫 (denyIfNoManage) |
🚫 |
src/app/actions/announcementActions.ts:
createAnnouncementDraft, saveAnnouncementDraft, deleteAnnouncementDraft,
duplicateAnnouncement, sendTestAnnouncement, startAnnouncement,
getAnnouncementProgress, getAnnouncementRecipients,
resendFailedAnnouncement, cancelAnnouncement, exportAnnouncementCsv.
O anexo é servido por GET /api/announcements/[id]/art (sessão + canEdit).
/api/backup) não inclui broadcasts (vale para os três
módulos); o backup .wfpbackup inclui banco e uploads/.INVITE_*, independentemente do módulo.