Blog
Feature
8 de março de 202610 min

Como construir uma agenda colaborativa com filtros

A agenda do TAMSIV começou como um componente simples: uma lista de eventos, um calendário, uma visualização diária. E então os grupos colaborativos chegaram. De repente, um evento não era mais pessoal — podia ter participantes, convites, status. E com 15 pessoas em um grupo ativo, a agenda tornou-se ilegível sem filtros.

Tive que repensar tudo: sistema de participantes com convites e notificações, filtros avançados para navegar no fluxo, eventos de vários dias e uma sobreposição privada para ver seus compromissos pessoais no contexto do grupo. A agenda tornou-se o componente mais complexo do TAMSIV — mas também o mais útil no dia a dia.

Pontos-chave a reter:
- Uma agenda colaborativa requer um sistema de participantes com status (convidado, aceito, recusado)
- Filtros avançados são indispensáveis assim que a atividade do grupo excede 10 eventos/semana
- Eventos de vários dias exigem um cálculo de intervalos com gerenciamento de fusos horários
- A sobreposição privada na agenda do grupo preserva a privacidade e evita conflitos
- 5 visualizações de calendário (dia, 3 dias, semana, mês, lista) cobrem todos os casos de uso

Por que uma agenda pessoal não é suficiente em um aplicativo colaborativo?

Enquanto o TAMSIV era um aplicativo estritamente pessoal, a agenda era simples. Tu crias um evento, tu o vês no teu calendário, ponto. Mas quando adicionei os grupos hierárquicos, tudo mudou.

Em um grupo (família, equipe, associação), os eventos são compartilhados. Uma reunião, um jantar em família, um jogo esportivo — todos devem vê-lo. Mas nem todos querem ver todos os eventos. O gerente quer ver as reuniões da sua equipe, não as saídas do grupo de boliche. O pai quer ver as atividades das crianças, não as reuniões profissionais do cônjuge.

É aí que os filtros e o sistema de participantes se tornam essenciais. Sem eles, a agenda colaborativa é um burburinho inútil.

Equipe de profissionais olhando para um grande calendário digital com blocos de eventos coloridos e avatares de participantes
Uma agenda compartilhada sem filtros rapidamente se torna inutilizável em um grupo ativo.

Como funciona o sistema de participantes no TAMSIV?

Cada evento no TAMSIV pode ter participantes convidados entre os membros do grupo. A arquitetura baseia-se numa tabela de ligação no esquema collaborative:

-- Tabela simplificada
calendar_event_participants (
  id UUID PRIMARY KEY,
  event_id UUID REFERENCES privat.calendar_events,
  user_id UUID REFERENCES auth.users,
  status TEXT CHECK (status IN ('invited', 'accepted', 'declined')),
  invited_at TIMESTAMPTZ,
  responded_at TIMESTAMPTZ
)

O fluxo de convite é o seguinte:

  1. O criador adiciona participantes durante a criação ou edição do evento. Ele escolhe entre os membros do grupo através de um seletor.
  2. Notificação push: Cada participante convidado recebe uma notificação FCM com o título do evento, a data e um botão para aceitar ou recusar.
  3. Resposta: O participante aceita ou recusa. O seu status é atualizado em tempo real na base de dados.
  4. Visibilidade: O evento aparece na agenda de todos os participantes (convidados, aceites). Os recusados não o veem mais (a menos que mudem de ideia).

É um fluxo semelhante ao que tu encontras no Google Calendar, mas adaptado ao contexto de grupos hierárquicos do TAMSIV. A principal diferença: os participantes são limitados aos membros do grupo, não a qualquer endereço de e-mail.

Quais filtros avançados são indispensáveis em uma agenda de grupo?

A FilterBar da agenda oferece três modos de filtragem:

  • "Todos": Exibe todos os eventos do grupo. Útil para ter uma visão geral, mas rapidamente barulhento em um grupo ativo.
  • "Minhas criações": Exibe apenas os eventos que tu criaste. Útil para verificar se os teus próprios eventos estão bem planejados.
  • "Participando": Exibe apenas os eventos em que tu és participante (convidado ou aceito). Este é o modo mais usado — ele responde à pergunta "Em que devo estar presente?".

Esses filtros são aplicados em tempo real, sem recarregar a página. O ContentCacheService armazena os eventos em cache, e os filtros operam no lado do cliente sobre os dados em cache. O resultado: uma mudança de filtro é instantânea, mesmo com centenas de eventos.

Além dos filtros por participação, a FilterBar oferece os 3 contextos clássicos do TAMSIV:

  • Privado: Apenas os teus eventos pessoais
  • Compartilhado: Eventos dos grupos aos quais tu pertences
  • Tudo: Fusão das duas visualizações

Esses filtros são consistentes com os do feed e das tarefas. O usuário encontra os mesmos paradigmas de navegação em todo o aplicativo.

Como gerenciar eventos de vários dias em um componente de calendário?

Um evento "Conferência segunda→quarta" deve aparecer por 3 dias na visualização do calendário. Parece trivial, mas a implementação está cheia de armadilhas.

O componente AgendaView calcula os spans — a duração em dias de cada evento. Para cada dia exibido, ele determina quais eventos o cobrem e os posiciona visualmente:

  • Visualização diária: O evento de vários dias aparece como uma faixa na parte superior, acima dos eventos por hora.
  • Visualização semanal: O evento se estende horizontalmente pelas colunas correspondentes aos dias cobertos.
  • Visualização mensal: Mesmo princípio, com um sistema de truncamento se muitos eventos se sobrepõem.
  • Visualização em lista: O evento aparece apenas uma vez, com suas datas de início e fim.

A principal armadilha: os fusos horários nas fronteiras do dia. Um evento que começa às 23h de segunda-feira em UTC+1 e termina às 2h de terça-feira cobre 1 ou 2 dias? A resposta depende do fuso horário do usuário. Optei por uma lógica simples: um evento cobre um dia se tiver pelo menos uma hora de presença nesse dia, no fuso horário local do usuário.

Tela de telefone móvel exibindo um aplicativo de calendário com eventos de vários dias e botões de filtro
Os eventos de vários dias estendem-se visualmente pelos dias relevantes em cada vista.

O que é a sobreposição privada e por que ela é essencial?

Aqui está o cenário: tu estás na agenda do teu grupo profissional. Tu queres agendar uma reunião na terça-feira às 14h. Mas na terça-feira às 14h, tu tens uma consulta médica pessoal. Como saber sem sair da agenda do grupo?

Este é o papel da sobreposição privada. Na agenda do grupo, os teus eventos pessoais aparecem em transparência, acinzentados. Eles não são clicáveis, não há detalhes visíveis — apenas um bloco para indicar "tu já tens algo aqui".

As regras da sobreposição:

  • Visível apenas para ti: Os outros membros do grupo não veem os teus eventos privados.
  • Não interativo: Sem clique, sem detalhes, sem modificação a partir da vista do grupo.
  • Visual discreto: Opacidade reduzida (30%), cor cinza, sem título visível. Apenas um indicador de "slot ocupado".
  • Desativável: Um botão nas configurações permite ocultar a sobreposição se tu não a quiseres.

Esta sobreposição respeita a privacidade, evitando conflitos de horários. É um padrão que adotei do Google Calendar (que mostra "Ocupado" para eventos de outros), adaptado ao contexto de um grupo onde a vida privada é importante.

Como as 5 visualizações de calendário cobrem todos os casos de uso?

O TAMSIV oferece 5 visualizações de calendário, cada uma adaptada a uma necessidade diferente:

  1. Visualização diária: Linha do tempo hora a hora. Ideal para planejar um dia agitado, ver os horários livres. Usada quando tu procuras "Quando estou livre hoje?".
  2. Visualização de 3 dias: Compromisso entre o detalhe do dia e a visão da semana. A minha preferida para planejamento de curto prazo. Tu vês hoje + os próximos 2 dias.
  3. Visualização semanal: A visualização clássica de negócios. 7 colunas, cada dia visível. Útil para equipes que planejam semanalmente.
  4. Visualização mensal: Visão geral. Os eventos são pontos coloridos. Útil para identificar períodos ocupados e dias livres. Consistente com a visão de produtividade do aplicativo.
  5. Visualização em lista: Lista cronológica simples. Sem grade, sem posicionamento. Útil em dispositivos móveis quando a visualização semanal é muito pequena, ou para exportar/compartilhar.

Cada visualização mantém a data atual ao mudar de visualização. Se tu estás na quarta-feira 15 na visualização diária e passas para a visualização semanal, tu vês a semana do dia 15. O deslize horizontal permite navegar no tempo (dia anterior/seguinte, semana anterior/seguinte).

Vista compartilhada mostrando uma sobreposição de calendário pessoal em transparência sobre um calendário de equipe
A sobreposição privada mostra os teus compromissos pessoais em transparência na agenda do grupo.

Como a agenda interage com o pipeline de voz do TAMSIV?

Um dos pontos fortes do TAMSIV é a criação de eventos por voz. Tu dizes "Adiciona uma reunião com o Marc na próxima terça-feira às 14h" e o ditafone cria o evento. O backend usa a ferramenta de função create_calendar_event para extrair a data, hora, título e participantes mencionados.

O sistema de PendingCreation também se aplica aos eventos: a IA cria uma pré-visualização, o utilizador pode editar os detalhes (adicionar participantes, modificar a hora) antes de validar. Isso evita erros de transcrição — "terça-feira" vs "quarta-feira" é um erro STT clássico.

Uma vez criado o evento, o cache Realtime propaga a mudança: todos os participantes veem o evento aparecer na sua agenda sem atualizar. A notificação push é enviada em paralelo. Todo o fluxo — voz → IA → criação → notificação → exibição — leva cerca de 3 segundos.

Quais foram as principais dificuldades técnicas?

A agenda é o componente mais complexo do TAMSIV. Aqui estão os principais desafios que encontrei:

  • Desempenho da renderização: A visualização semanal com mais de 50 eventos e a sobreposição privada devem permanecer fluidas. Tive que usar React.memo agressivamente e virtualizar as listas de eventos na visualização em lista.
  • Gerenciamento de rolagem: O deslize horizontal para navegar entre dias/semanas entra em conflito com a rolagem vertical da linha do tempo. Usei um GestureDetector de react-native-gesture-handler com limites de direção para desambiguar.
  • Eventos empilhados: Quando 3 eventos se sobrepõem no mesmo slot, é preciso organizá-los lado a lado na linha do tempo. O algoritmo de layout de eventos empilhados não é trivial — é um problema de agendamento de intervalos.
  • RLS Supabase: As políticas de Row Level Security para eventos de grupo são complexas. Um evento é visível se: tu o criaste, OU tu és participante, OU tu és membro do grupo e o evento é público. Três condições OR em uma única política RLS.

Como esta arquitetura se estende às futuras necessidades?

A arquitetura atual foi projetada para evoluir. Aqui estão as extensões previstas no roadmap:

  • Recorrência: Eventos recorrentes (todas as segundas-feiras, o 1º de cada mês). O esquema do banco de dados está pronto, o frontend ainda não.
  • Sincronização de calendário externo: Importação/exportação iCal para sincronizar com Google Calendar, Outlook, Apple Calendar.
  • Disponibilidade compartilhada: Mostrar os horários de disponibilidade dos membros do grupo para facilitar o planejamento.
  • Lembretes personalizados: Cada participante pode configurar os seus próprios lembretes (5 min antes, 1h antes, na véspera).

FAQ

É possível convidar participantes de fora do grupo?

Atualmente não. Os participantes estão limitados aos membros do grupo no qual o evento é criado. Esta é uma escolha deliberada para simplificar a gestão de permissões. O convite por e-mail (como o Google Calendar) está previsto para uma versão futura.

Como funcionam as notificações de convite?

Os convites acionam uma notificação push via Firebase Cloud Messaging. O participante recebe o título do evento, a data/hora, e pode aceitar ou recusar diretamente da notificação (botões de ação FCM). A resposta é registrada em tempo real no Supabase.

A sobreposição privada funciona em todas as visualizações?

Sim, a sobreposição privada está ativa nas 5 visualizações (dia, 3 dias, semana, mês, lista). Na visualização mensal, os eventos privados aparecem como pontos cinzentos discretos. Nas visualizações temporais (dia, semana), eles aparecem como blocos transparentes.

A agenda do TAMSIV substitui o Google Calendar?

Não, ela o complementa. A agenda TAMSIV é projetada para gerenciamento de tarefas e projetos colaborativos, com a voz como principal modo de interação. O Google Calendar continua sendo mais adequado para gerenciamento de calendário puro (recorrências complexas, sincronização multi-dispositivos). A sincronização entre os dois está no roadmap.

É possível criar um evento por voz com participantes?

Sim. Tu podes dizer "Reunião com Marc e Sophie sexta-feira às 10h" e a IA detectará os participantes mencionados, desde que sejam membros do grupo ativo. O sistema de PendingCreation permite que tu verifiques e ajustes os participantes antes da validação.