Skip to content

Latest commit

 

History

History
466 lines (390 loc) · 24.7 KB

File metadata and controls

466 lines (390 loc) · 24.7 KB

Features — Mapa Canônico

Este documento é o mapa vivo de cada feature implementada no PetCircle. Para cada domínio funcional você encontra: rotas, Server Actions, componentes principais, tabelas Supabase, triggers de notificação e badge, e edge cases já tratados.

Fonte de verdade do código, não de planejamento. Para roadmap veja README.md; para spec detalhada de cada feature, veja specs/features/.


Índice

  1. Autenticação
  2. Onboarding
  3. Feed social
  4. Perfil do pet
  5. Co-tutoria e convites
  6. Saúde e vacinas
  7. Lembretes e food tracking
  8. Mensagens diretas
  9. Explore e busca
  10. Persona switcher
  11. Notificações
  12. Badges e conquistas
  13. Listas sociais (fãs, petmigos, favoritos)
  14. Settings e conta
  15. i18n
  16. PWA

1. Autenticação

Login com Google OAuth e email/senha, callback SSR que direciona para onboarding quando o usuário ainda não tem pet.

  • Rotas:
    • src/app/(auth)/login/page.tsx — tela de login
    • src/app/auth/callback/route.ts — OAuth callback
    • src/app/page.tsx — root (redirect para /feed ou /login)
  • Componentes:
    • src/app/(auth)/login/LoginButtons.tsx — botão Google
    • src/app/(auth)/login/EmailAuthForm.tsx — form email/senha com signup / forgot password
  • Server Actions: nenhuma dedicada; usa supabase.auth.signInWithOAuth() e signInWithPassword() direto no client.
  • Tabelas: auth.users (gerida pelo Supabase) e public.users (criada via trigger handle_new_user na migration 20260410000001_user_profile_trigger.sql).
  • Edge cases tratados:
    • Conta existente com mesmo email vinda de provider diferente → unificada no auth.users do Supabase
    • Callback recebe callbackError na query string se falha → redireciona de volta para /login com mensagem
    • Se usuário confirmou email mas ainda não tem pet → /auth/callback redireciona para /onboarding

2. Onboarding

Flow de 3 telas para criar o primeiro pet após signup, com animações spring. Também cria o User profile se ainda não existir.

  • Rotas: src/app/(main)/onboarding/page.tsx
  • Componentes:
    • src/components/onboarding/OnboardingFlow.tsx — 3 steps (nome+foto → espécie+raça → aniversário+cidade)
  • Server Actions (src/lib/actions/onboarding.ts):
    • createPet(formData) — cria pet + popula pet_tutors (o próprio criador como tutor owner)
    • skipPetCreation() — marca onboarding_skipped_at no user; libera acesso ao feed sem pet
  • Tabelas: users, pets, pet_tutors
  • Triggers de badge: nenhum (primeira criação não dispara badge; badge first_post só depois do primeiro post)
  • Edge cases:
    • Se usuário pular onboarding, pode criar pet depois em /profile via AddPetSheet
    • Slug é gerado automaticamente a partir do nome do pet (slugify() em src/lib/utils/slugify.ts), com retry numérico se colidir

3. Feed social

Scroll infinito com posts de pets que o usuário segue + seus próprios pets. Likes persona-aware (como tutor ou como pet), comentários com threading 1 nível, mood, double-tap e compartilhamento.

  • Rotas:
    • src/app/(main)/feed/page.tsx — feed principal
    • src/app/(main)/post/[id]/page.tsx — landing page dedicada para sharing (com generateMetadata)
  • Componentes principais:
    • src/components/feed/FeedList.tsx — scroll infinito, paginação
    • src/components/feed/PostCard.tsx — card individual com like, comment, share
    • src/components/feed/CreatePostSheet.tsx — bottom sheet de criação com compressão de imagem
    • src/components/feed/EditPostSheet.tsx — edição de caption e mood
    • src/components/feed/CommentSheet.tsx — thread de comentários + replies
    • src/components/feed/LikeButton.tsx — com animação spring e patinha 🐾
    • src/components/feed/SharePostButton.tsx — Web Share API + copy link
    • src/components/feed/MoodSelector.tsx — 7 moods (feliz, triste, animado, etc. — definidos em src/lib/constants/moods.ts)
  • Server Actions:
    • src/lib/actions/feed.ts: fetchFeedPage(page), fetchFavoriteFeed(page)
    • src/lib/actions/post.ts: createPost(), editPost(), deletePost(), toggleLike()
    • src/lib/actions/comment.ts: createComment(), deleteComment(), getComments(), getReplies()
  • Tabelas: posts (foto + caption + mood), likes (com actor_pet_id nullable para persona), comments (com parent_comment_id para threading)
  • Storage: bucket pet-images (público)
  • Triggers de notificação:
    • Like em post → notif tipo "like" para tutor(es) do pet dono do post
    • Comment em post → notif tipo "comment" para tutor(es)
    • Reply em comment → notif tipo "reply" para autor do comment parent
  • Triggers de badge:
    • createPost → checa first_post
    • toggleLike (quando recebe) → checa loved_10
    • createComment → checa social_butterfly
  • Edge cases:
    • Reply de reply é achatado para irmão do parent (threading apenas 1 nível)
    • Upload de imagem passa por compress-image.ts (browser-image-compression, max 1920px, qualidade 0.8) antes de subir pro Storage
    • Self-like não notifica tutor; mas pet A comentando em pet B do mesmo tutor notifica (regra em src/lib/notifications/create.ts)

4. Perfil do pet

Perfil público do pet com galeria, badges, co-tutores, botões de follow/favorite. SEO via generateMetadata com Open Graph. URLs canonicalizadas por slug.

  • Rotas: src/app/(main)/pet/[id]/page.tsx — aceita UUID ou slug; se UUID e pet tem slug, redireciona para canonical URL
  • Componentes principais:
    • src/components/pet/pet-profile-header.tsx — avatar, nome, contadores
    • src/components/pet/pet-info-section.tsx — bio, personalidade, idade, cidade
    • src/components/pet/PetProfileStats.tsx — posts / fãs / petmigos
    • src/components/pet/pet-gallery.tsx — grid de posts
    • src/components/pet/PetEditDrawer.tsx — edição (nome, raça, bio, avatar, data, cidade)
    • src/components/pet/PetDeleteSection.tsx — soft/restore/hard delete
    • src/components/pet/FollowButton.tsx — follow/unfollow
    • src/components/pet/FavoriteButton.tsx — marcar pet como petmigo
    • src/components/pet/TutorSection.tsx — lista de co-tutores + invites
    • src/components/badges/BadgeSection.tsx — carrossel de badges earned
  • Server Actions (src/lib/actions/pet.ts):
    • addPet(formData) — criar pet novo
    • updatePet(petId, formData) — editar
    • checkSlugAvailability(slug) — validação durante edição
    • deletePet(petId) — soft-delete (30 dias de grace)
    • restorePet(petId) — desfaz soft-delete
    • hardDeletePet(petId) — deleta permanentemente (admin ou após 30d)
  • Follow: src/lib/actions/follow.tstoggleFollow(petId)
  • Tabelas: pets, pet_tutors, follows, pet_badges
  • Storage: bucket pet-images para avatars
  • Triggers de notificação: follow recebido → notif tipo "follow" para tutor(es) do pet
  • Triggers de badge: follow recebido → checa popular_50, influencer_100
  • Edge cases:
    • generateMetadata busca pet por ID ou slug; retorna OpenGraph com avatar como imagem
    • Soft-delete marca deleted_at; purge_cron (migration 20260415000007) remove após 30 dias
    • Se pet já tem slug e acessado por UUID → redirect para URL canônica
    • Count cacheado em colunas (followers_count, posts_count) via triggers Postgres, atualizado automaticamente

5. Co-tutoria e convites

Múltiplos tutores podem compartilhar um mesmo pet. Invites são links com código que expiram em 30 dias.

  • Rotas:
    • src/app/invite/[code]/page.tsx — landing de aceitar convite
  • Componentes:
    • src/components/pet/TutorSection.tsx — UI principal dentro do pet profile
    • src/components/invite/AcceptInviteCard.tsx — card de aceitar convite
  • Server Actions (src/lib/actions/tutor.ts):
    • createInvite(petId) — gera código, salva em pet_invites com expires_at = now() + 30d
    • getInviteByCode(code) — busca invite válido
    • acceptInvite(code) — cria linha em pet_tutors, marca invite como usado
    • removeTutor(petId, userId) — remove relação (se owner ou self)
    • getTutors(petId) — lista tutores do pet
    • updateShowOnProfile(petId, show) — toggle de exibição na lista pública
    • getVisibleTutors(petId) — lista filtrada para perfil público
  • Tabelas: pet_tutors, pet_invites
  • Edge cases:
    • Invite expirado → rota /invite/[code] mostra mensagem "convite expirado"
    • Invite já usado → mensagem similar
    • Owner não pode ser removido (precisa transferir ownership primeiro — não implementado; limitação conhecida)

6. Saúde e vacinas

Carteirinha de vacinação com data de aplicação, próxima dose, veterinário, lote e receita em PDF/imagem.

  • Rotas: integrado ao pet profile (sem rota dedicada)
  • Componentes:
    • src/components/health/HealthTimeline.tsx — timeline de eventos
    • src/components/health/TimelineEventItem.tsx — card de evento
    • src/components/health/VaccineList.tsx — lista de vacinas
    • src/components/health/VaccineCard.tsx — card de vacina
    • src/components/health/CreateVaccineSheet.tsx — form de criação
    • src/components/health/EditVaccineSheet.tsx — edição
    • src/components/health/VaccineReceiptUpload.tsx — upload de receita
  • Server Actions:
    • src/lib/actions/vaccination.ts: getVaccinations(petId), createVaccination(), updateVaccination(), deleteVaccination(), uploadVaccineReceipt(), deleteVaccineReceipt()
    • src/lib/actions/health-timeline.ts: getHealthTimeline(petId) — combina vacinas + lembretes em ordem cronológica
  • Tabelas: vaccinations
  • Storage: bucket vaccine-proofs (privado — policies RLS)
  • Triggers de badge: vaccine_updated → checa vaccinated (todas vacinas em dia)
  • Edge cases:
    • Upload de receita sobrescreve anterior (deleta storage path antigo antes)
    • Validação Zod: data aplicada não pode ser futura; próxima dose tem que ser após data aplicada

7. Lembretes e food tracking

Lembretes per-pet, all-pets ou personal, com frequências variadas. Food tracking especializado com sugestão baseada em histórico de compras.

  • Rotas: src/app/(main)/reminders/page.tsx
  • Componentes:
    • src/components/reminders/RemindersPageClient.tsx — shell com tabs
    • src/components/reminders/ReminderList.tsx — lista agrupada por tipo
    • src/components/reminders/CreateReminderSheet.tsx — form de criação (tipo, escopo, frequência, horários)
    • src/components/reminders/FoodRemindersSection.tsx — seção especializada
    • src/components/reminders/FoodPurchaseSheet.tsx — registrar compra de ração
  • Server Actions:
    • src/lib/actions/reminder.ts: getReminders(), createReminder(), completeReminder(), toggleReminderActive()
    • src/lib/actions/food.ts: createFoodPurchase(), deleteFoodPurchase(), getFoodHistory(petId), getSmartSuggestion(petId), getActiveFoodReminders()
  • Tabelas: reminders, reminder_completions, food_purchases
  • Triggers de badge: reminder_completed → checa consistent_7 (7+ lembretes completados)
  • Edge cases:
    • Escopo all-pets cria 1 lembrete com pet_id = null mas is_all_pets = true; na listagem, aparece em cada pet
    • getSmartSuggestion estima próxima compra com base em intervalo médio de compras anteriores
    • Completion é tracked em reminder_completions com (reminder_id, date) único — 1 completion por dia

8. Mensagens diretas

Chat 1:1 entre tutores (não entre pets). Realtime via Supabase, presença, envio como pet ou como tutor.

  • Rotas:
    • src/app/(main)/messages/page.tsx — lista de conversas
    • src/app/(main)/messages/[id]/page.tsx — chat individual
    • src/app/api/messages/[conversationId]/route.ts — API para paginação de mensagens antigas (scroll up)
  • Componentes:
    • src/components/messages/ChatView.tsx — view principal com scroll, realtime, presence
    • src/components/messages/ChatInput.tsx — textarea com auto-grow + persona selector
    • src/components/messages/MessageBubble.tsx — bubble com avatar
    • src/components/messages/QuickEmojiBar.tsx — emojis rápidos
    • src/components/messages/ConversationList.tsx — lista com unread badge
  • Hooks:
    • src/lib/hooks/useRealtimeMessages.ts — subscribe a postgres_changes em messages
    • src/lib/hooks/usePresence.ts — marca usuário como online na conversa
    • src/lib/hooks/useUnreadMessagesCount.ts — subscription para badge da BottomNav
  • Server Actions (src/lib/actions/message.ts):
    • getOrCreateConversation(otherUserId) — normaliza user_a < user_b, cria se não existe
    • getConversations() — lista com preview da última mensagem
    • sendMessage(conversationId, body, asPetId?) — envia + dispara push
    • markAsRead(conversationId) — zera unread
    • getUnreadCount() — total para badge
  • Presence (src/lib/actions/presence.ts):
    • setPresence(conversationId, online) — atualiza user_presence
    • toggleMessagePush(enabled) / getMessagePushEnabled() — preferência de push para DMs
  • Tabelas: conversations, messages, user_presence
  • Realtime: postgres_changes em messages e notifications
  • Triggers de notificação: nova mensagem → push notification direto (não cria notif DB) se destinatário não está ativo na conversa + tem message_push_enabled
  • Edge cases:
    • Conversation ID é determinístico via ordenação de UUIDs (previne duplicata)
    • Self-message é bloqueado no action
    • Se usuário está em a conversa (presence online) → não envia push (evita notificação enquanto já vê a mensagem)

9. Explore e busca

Grid de pets populares + busca por nome/raça com debounce. Filtros por espécie. Seção "Perto de você" baseada na cidade.

  • Rotas: src/app/(main)/explore/page.tsx
  • Componentes:
    • src/components/explore/ExploreSearch.tsx — search input + filtros + resultados
    • src/components/explore/ExplorePetCard.tsx — card de pet
    • src/components/explore/ExplorePetGrid.tsx — grid paginado
  • Server Actions:
    • src/lib/actions/explore.ts: searchPets(query, species?, page, cityId?) — usa índices pg_trgm para busca fuzzy
    • src/lib/actions/city.ts: searchCities(query) — autocomplete
  • Tabelas: pets (com índices pg_trgm em name, breed), cities
  • Edge cases:
    • Query vazia → lista por followers_count DESC
    • Query > 2 chars → usa ilike + índice trigram
    • Debounce 300ms no client
    • Exclui pets do próprio usuário e pets com deleted_at NOT NULL

10. Persona switcher

Sistema que permite ao usuário interagir como tutor ou como um de seus pets. Afeta likes, comentários e mensagens.

  • Arquivos:
    • src/lib/persona/PersonaContext.tsx — React context
    • src/lib/persona/persona-cookie.ts — leitura/escrita do cookie active-persona
    • src/lib/persona/persona-server.ts — leitura server-side
    • src/components/layout/PersonaSwitcherSheet.tsx — UI de troca
  • Cookie: active-persona = "tutor" ou "pet:{petId}"
  • Uso em actions:
    • toggleLike(postId) → lê persona, se é pet salva actor_pet_id
    • createComment(...) → similar
    • sendMessage(...) → salva sender_pet_id se enviando como pet
  • Edge cases:
    • Persona persiste entre sessões (cookie httpOnly com 1 ano)
    • Ao deletar um pet, se o pet ativo for o deletado, persona volta para "tutor"
    • SSR lê cookie em Server Components para renderizar UI correta antes do hydration

11. Notificações

Centro de notificações in-app + Web Push para mensagens. Regras de self-action para não spammar o próprio usuário.

  • Rotas: src/app/(main)/notifications/page.tsx
  • Componentes:
    • src/components/notifications/NotificationList.tsx — lista paginada com infinite scroll
    • src/components/notifications/NotificationItem.tsx — item individual com deep link
  • Server Actions (src/lib/actions/notification.ts):
    • getNotifications(page) — com join de users/pets relacionados
    • getUnreadNotificationCount() — badge
    • markNotificationAsRead(id) — individual
    • markAllNotificationsAsRead() — bulk
    • deleteNotification(id) — remove
  • Push subscription (src/lib/actions/push.ts):
    • savePushSubscription(subscription) — salva em push_subscriptions
    • removePushSubscription(endpoint) — unsubscribe
    • getPushStatus() — verifica se user tem subscription ativa
  • API Routes:
    • src/app/api/push/subscribe/route.ts — POST recebe subscription
    • src/app/api/push/unsubscribe/route.ts — POST remove
  • Lógica centralizada:
    • src/lib/notifications/create.tsshouldCreateNotification() aplica regras de self-action
    • src/lib/notifications/recipients.tsgetNotificationRecipients(petId, actorUserId) lista tutores a notificar
    • src/lib/push/send.tssendPushNotification(userId, payload) envia via web-push + VAPID
  • Tabelas: notifications, push_subscriptions, user_presence (para skip push se online na conversa)
  • Realtime: subscription em notifications para atualizar badge sem refresh
  • Para referência completa: veja docs/NOTIFICATIONS.md

12. Badges e conquistas

8 badges automáticos, desbloqueados em triggers específicos. Notificação "badge" quando earned.

  • Badges definidos: first_post, loved_10, popular_50, influencer_100, vaccinated, consistent_7, birthday, social_butterfly
  • Componentes:
    • src/components/badges/BadgeSection.tsx — carrossel no pet profile
    • src/components/badges/BadgeModal.tsx — modal com todas (earned + locked)
    • src/components/badges/BadgeItem.tsx — item individual
  • Server Actions (src/lib/actions/badge.ts):
    • getPetBadges(petId) — earned
    • getAllBadges() — todas as definições
    • checkAndGrantBadges(petId, trigger) — checa condições e concede; chamada após cada evento relevante
  • Triggers: post_created, like_received, follow_received, vaccine_updated, reminder_completed
  • Tabelas: badges (seed), pet_badges (earned)
  • Para referência completa: veja docs/BADGES.md

13. Listas sociais

Sheets que listam quem curtiu um post, quem são os fãs de um pet, ou quem favoritou ele.

  • Componentes:
    • src/components/lists/LikesSheet.tsx — usuários que curtiram um post
    • src/components/lists/FansSheet.tsx — fãs do pet (quem segue)
    • src/components/lists/PetmigosSheet.tsx — petmigos (quem o pet segue)
    • src/components/lists/FavoritesSheet.tsx — posts favoritados
  • Server Actions (src/lib/actions/user-lists.ts):
    • getPetFans(petId, page) — seguidores do pet
    • getPetFavorites(petId, page) — favoritos do pet
    • getPostLikes(postId, page) — likes em um post
  • Tabelas: follows, likes

14. Settings e conta

Edição de perfil do tutor, preferências (idioma, push), gestão de conta (delete account com grace period).

  • Rotas:
    • src/app/(main)/settings/page.tsx — settings principal
    • src/app/(main)/settings/about/page.tsx — sobre (versão, links)
    • src/app/(main)/profile/page.tsx — perfil do tutor
  • Componentes:
    • src/components/settings/SettingsClient.tsx — shell
    • src/components/settings/LanguageSheet.tsx — switch pt-BR ↔ en
    • src/components/settings/PushNotificationToggle.tsx — toggle global de push
    • src/components/settings/MessagePushToggle.tsx — toggle específico de DMs
    • src/components/profile/AddPetSheet.tsx — criar pet (fora de onboarding)
    • src/components/profile/ProfilePetCard.tsx — card de pet na lista do tutor
  • Server Actions:
    • src/lib/actions/user.ts: updateUserProfile(formData) — nome, avatar do tutor
    • src/lib/actions/locale.ts: setLocaleCookie(locale)
    • src/lib/actions/account.ts: requestAccountDeletion(), cancelAccountDeletion(), hardDeleteAccount()
  • Tabelas: users (com deletion_requested_at para grace period)
  • Edge cases:
    • Request delete marca deletion_requested_at = now(); purge_cron roda após 30 dias
    • Cancel delete zera deletion_requested_at
    • Hard delete imediato disponível (requer confirmação)

15. i18n

next-intl com pt-BR (default) + en. Cookie-based, sem URL prefix. Zero hardcoded strings (regra obrigatória).

  • Setup:
    • src/i18n/request.ts — config do next-intl
    • src/messages/pt-BR.json + src/messages/en.json — ~500 keys organizadas por namespace
    • Cookie NEXT_LOCALE controla idioma
  • Script de validação: scripts/check-i18n.ts rodado via pnpm i18n:check
    • Garante keys idênticas em ambos locales
    • Valida variáveis de interpolação ({count}, {petName}) coincidem
  • Componentes:
    • Client: useTranslations("namespace") de next-intl
    • Server: getTranslations("namespace") de next-intl/server
  • Regras completas: .claude/rules/i18n.md

16. PWA

Manifest + service worker custom (sem next-pwa/serwist). Ícones maskable, push support, offline cache dos shell assets.

  • Arquivos:
    • public/manifest.json — manifest com display: standalone
    • public/sw.js — service worker (install/activate/fetch/push/notificationclick)
    • public/icons/ — 192/512 em PNG + SVG, maskable
    • src/components/ui/ServiceWorkerRegistration.tsx — registra o SW no mount
    • src/app/layout.tsx — metadata PWA + apple web app
  • Cache strategy: cache-first para STATIC_ASSETS (shell), network-first para tudo mais
  • Push handler: ouve evento push, exibe notificação com title/body/icon/badge/url
  • Para referência completa: veja docs/PWA.md

Matriz features × tabelas

Feature Tabelas principais
Auth auth.users, users
Onboarding users, pets, pet_tutors
Feed posts, likes, comments
Pet profile pets, pet_tutors, follows, pet_badges
Co-tutoria pet_tutors, pet_invites
Saúde vaccinations
Lembretes reminders, reminder_completions, food_purchases
Mensagens conversations, messages, user_presence
Explore pets, cities
Notificações notifications, push_subscriptions
Badges badges, pet_badges

Matriz features × Server Actions

Feature Actions principais Arquivo
Feed fetchFeedPage, fetchFavoriteFeed feed.ts
Posts createPost, editPost, deletePost, toggleLike post.ts
Comentários createComment, deleteComment, getComments, getReplies comment.ts
Pets addPet, updatePet, deletePet, restorePet, hardDeletePet, checkSlugAvailability pet.ts
Onboarding createPet, skipPetCreation onboarding.ts
Follow toggleFollow follow.ts
Co-tutoria createInvite, acceptInvite, removeTutor, getTutors, getInviteByCode, updateShowOnProfile, getVisibleTutors tutor.ts
Saúde getVaccinations, createVaccination, updateVaccination, deleteVaccination, uploadVaccineReceipt, deleteVaccineReceipt vaccination.ts
Health Timeline getHealthTimeline health-timeline.ts
Lembretes getReminders, createReminder, completeReminder, toggleReminderActive reminder.ts
Food createFoodPurchase, deleteFoodPurchase, getFoodHistory, getSmartSuggestion, getActiveFoodReminders food.ts
Mensagens getOrCreateConversation, getConversations, sendMessage, markAsRead, getUnreadCount message.ts
Presence setPresence, toggleMessagePush, getMessagePushEnabled presence.ts
Explore searchPets explore.ts
Cities searchCities city.ts
Notificações getNotifications, getUnreadNotificationCount, markNotificationAsRead, markAllNotificationsAsRead, deleteNotification notification.ts
Push savePushSubscription, removePushSubscription, getPushStatus push.ts
Badges getPetBadges, getAllBadges, checkAndGrantBadges badge.ts
Listas getPetFans, getPetFavorites, getPostLikes user-lists.ts
User updateUserProfile user.ts
Conta requestAccountDeletion, cancelAccountDeletion, hardDeleteAccount account.ts
Locale setLocaleCookie locale.ts

Para assinaturas detalhadas de cada action (parâmetros, retorno, efeitos colaterais), veja docs/API-REFERENCE.md.