Files
pagodera/docs/PLANO_MELHORIAS_MARKETPLACE.md
Evandro Fonseca Junior ab37b477df feat: implement market page with QueryProvider and dynamic listings
- wrap root layout with QueryProvider for React Query
- replace market UnderConstruction placeholder with MarketClient and real data fetching
- implement search, pagination, and sort via search params using getActiveListings
- add necessary client components and queries
- update dependencies (e.g., prisma, pkg-types)
2025-11-05 01:58:03 -03:00

22 KiB

📋 Plano Detalhado: Melhorias do Marketplace

🎯 Objetivo Geral

Implementar um sistema completo de intenções de compra, melhorias na interface de gerenciamento de anúncios, sistema de notificações visual, exibição de vendedores, sistema de lances e telemetria completa.


📊 Análise de Requisitos

Requisitos Explícitos

  1. Sistema de Intenção de Compra

    • Compra não remove item do mercado
    • Criar "intenção de compra" com método de pagamento escolhido
    • Notificar vendedor sobre cada intenção
  2. Melhorias na Página "Meus Anúncios"

    • Exibir dados de compradores interessados
    • Mostrar informações de cada intenção de compra
  3. Ícone de Notificações

    • Badge com contador de notificações não lidas
    • Visual no header/navbar
  4. Exibição de Vendedor

    • Nome do vendedor nos cards
    • Avatar/imagem do vendedor (preparar para integração futura)
  5. Sistema de Lances (Bids)

    • Permitir que usuários deem lances
    • Atualizar valor do item conforme lances
    • Integração completa no frontend
  6. Telemetria Completa

    • Rastrear lances (valor, horário, usuário)
    • Acessos por dia/hora
    • Anúncios mais clicados
    • Análises de comportamento

Requisitos Implícitos

  • Sistema deve ser escalável
  • Performance não deve degradar com muitos dados
  • Interface responsiva e intuitiva
  • Notificações em tempo real (ou polling eficiente)

🗂️ Arquivos Afetados

Backend (API + Queries)

  1. Novos Arquivos:

    • lib/db/migrations/015_add_purchase_intentions.sql
    • lib/prisma/queries/purchase-intentions.ts
    • app/api/listings/[id]/intentions/route.ts
    • app/api/listings/[id]/intentions/[intentionId]/route.ts
    • app/api/notifications/unread-count/route.ts
  2. Arquivos Modificados:

    • app/api/listings/[id]/purchase/route.ts (mudar para criar intenção)
    • lib/prisma/queries/listings.ts (adicionar queries relacionadas)
    • lib/prisma/queries/notifications.ts (melhorar queries)
    • lib/prisma/queries/telemetry.ts (adicionar eventos de lance)

Frontend (Components)

  1. Novos Arquivos:

    • components/notifications/NotificationBell.tsx
    • components/notifications/NotificationDropdown.tsx
    • components/marketplace/PurchaseIntentionModal.tsx
    • components/marketplace/MyAdsIntentionList.tsx
    • components/marketplace/SellerInfo.tsx
  2. Arquivos Modificados:

    • components/marketplace/PurchaseModal.tsx (mudar para intenção)
    • components/marketplace/MyAdsPanel.tsx (adicionar seção de intenções)
    • components/marketplace/AdCard.tsx (adicionar SellerInfo, melhorar bids)
    • components/Header.tsx (adicionar NotificationBell)
    • components/marketplace/BidModal.tsx (melhorar UI e telemetria)

🗄️ Estrutura de Banco de Dados

Nova Tabela: purchase_intentions

CREATE TABLE purchase_intentions (
  intention_id SERIAL PRIMARY KEY,
  listing_id INTEGER NOT NULL REFERENCES listings(listing_id) ON DELETE CASCADE,
  buyer_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
  payment_method VARCHAR(20) NOT NULL CHECK (payment_method IN ('pix', 'tibia_coins', 'gold_coins', 'trade')),
  status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'rejected', 'expired', 'completed')),
  buyer_message TEXT,
  seller_response TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  expires_at TIMESTAMP,
  accepted_at TIMESTAMP,
  rejected_at TIMESTAMP,
  
  UNIQUE(listing_id, buyer_id, status) WHERE status = 'pending',
  INDEX idx_listing_id (listing_id),
  INDEX idx_buyer_id (buyer_id),
  INDEX idx_status (status),
  INDEX idx_created_at (created_at)
);

Campos:

  • intention_id: ID único
  • listing_id: Anúncio relacionado
  • buyer_id: Comprador interessado
  • payment_method: Método de pagamento escolhido
  • status: Estado da intenção (pending, accepted, rejected, expired, completed)
  • buyer_message: Mensagem opcional do comprador
  • seller_response: Resposta do vendedor
  • expires_at: Quando a intenção expira (opcional)
  • Timestamps para rastreamento

📝 Plano de Implementação

FASE 1: Sistema de Intenções de Compra (Backend)

Etapa 1.1: Migração de Banco de Dados

Arquivo: lib/db/migrations/015_add_purchase_intentions.sql

-- Migration 015: Sistema de Intenções de Compra
-- Criado em: 2025-01-29

CREATE TABLE IF NOT EXISTS purchase_intentions (
  intention_id SERIAL PRIMARY KEY,
  listing_id INTEGER NOT NULL REFERENCES listings(listing_id) ON DELETE CASCADE,
  buyer_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
  payment_method VARCHAR(20) NOT NULL CHECK (payment_method IN ('pix', 'tibia_coins', 'gold_coins', 'trade')),
  status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'rejected', 'expired', 'completed')),
  buyer_message TEXT,
  seller_response TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  expires_at TIMESTAMP,
  accepted_at TIMESTAMP,
  rejected_at TIMESTAMP
);

-- Índices para performance
CREATE INDEX IF NOT EXISTS idx_purchase_intentions_listing_id ON purchase_intentions(listing_id);
CREATE INDEX IF NOT EXISTS idx_purchase_intentions_buyer_id ON purchase_intentions(buyer_id);
CREATE INDEX IF NOT EXISTS idx_purchase_intentions_status ON purchase_intentions(status);
CREATE INDEX IF NOT EXISTS idx_purchase_intentions_created_at ON purchase_intentions(created_at DESC);

-- Constraint: apenas uma intenção pendente por listing+buyer
CREATE UNIQUE INDEX IF NOT EXISTS idx_purchase_intentions_unique_pending 
ON purchase_intentions(listing_id, buyer_id) 
WHERE status = 'pending';

-- Trigger para atualizar updated_at
CREATE OR REPLACE FUNCTION update_purchase_intentions_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = CURRENT_TIMESTAMP;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_update_purchase_intentions_updated_at
  BEFORE UPDATE ON purchase_intentions
  FOR EACH ROW
  EXECUTE FUNCTION update_purchase_intentions_updated_at();

-- Comentários para documentação
COMMENT ON TABLE purchase_intentions IS 'Intenções de compra de anúncios - permite múltiplos compradores interessados';
COMMENT ON COLUMN purchase_intentions.status IS 'pending: aguardando resposta do vendedor, accepted: aceita, rejected: rejeitada, expired: expirada, completed: concluída';

Tarefas:

  • Criar arquivo de migração
  • Adicionar à lista de migrations em run-migrations.ts
  • Executar migração
  • Atualizar schema Prisma

Etapa 1.2: Schema Prisma

Arquivo: prisma/schema.prisma

model PurchaseIntention {
  intention_id      Int       @id @default(autoincrement())
  listing_id        Int
  buyer_id          Int
  payment_method    String    @db.VarChar(20)
  status            String    @default("pending") @db.VarChar(20)
  buyer_message     String?   @db.Text
  seller_response   String?   @db.Text
  created_at        DateTime  @default(now())
  updated_at        DateTime  @updatedAt
  expires_at        DateTime?
  accepted_at       DateTime?
  rejected_at       DateTime?

  listing           Listing   @relation(fields: [listing_id], references: [listing_id], onDelete: Cascade)
  buyer             User      @relation("BuyerIntentions", fields: [buyer_id], references: [user_id], onDelete: Cascade)

  @@index([listing_id])
  @@index([buyer_id])
  @@index([status])
  @@index([created_at(sort: Desc)])
  @@unique([listing_id, buyer_id], where: { status: "pending" })
  @@map("purchase_intentions")
}

Também atualizar:

  • Adicionar relação purchaseIntentions no model Listing
  • Adicionar relação purchaseIntentions no model User (como BuyerIntentions)

Etapa 1.3: Queries Prisma

Arquivo: lib/prisma/queries/purchase-intentions.ts

Funções a criar:

  1. createPurchaseIntention(data) - Criar intenção
  2. getPurchaseIntentionsByListing(listingId, filters) - Listar intenções de um anúncio
  3. getPurchaseIntentionsByBuyer(buyerId, filters) - Listar intenções de um comprador
  4. getPurchaseIntentionById(intentionId) - Buscar intenção específica
  5. updatePurchaseIntentionStatus(intentionId, status, sellerResponse) - Atualizar status
  6. getPendingIntentionsCount(listingId) - Contar intenções pendentes

Detalhamento:

export interface CreatePurchaseIntentionData {
  listing_id: number;
  buyer_id: number;
  payment_method: "pix" | "tibia_coins" | "gold_coins" | "trade";
  buyer_message?: string;
  expires_at?: Date;
}

export interface PurchaseIntention {
  intention_id: number;
  listing_id: number;
  buyer_id: number;
  payment_method: string;
  status: "pending" | "accepted" | "rejected" | "expired" | "completed";
  buyer_message: string | null;
  seller_response: string | null;
  created_at: Date;
  updated_at: Date;
  expires_at: Date | null;
  accepted_at: Date | null;
  rejected_at: Date | null;
  // Relacionamentos
  listing?: Listing;
  buyer?: User;
}

Etapa 1.4: API Routes

Arquivo: app/api/listings/[id]/intentions/route.ts

Endpoints:

  • POST /api/listings/[id]/intentions - Criar intenção de compra
  • GET /api/listings/[id]/intentions - Listar intenções (apenas vendedor)

Arquivo: app/api/listings/[id]/intentions/[intentionId]/route.ts

Endpoints:

  • GET /api/listings/[id]/intentions/[intentionId] - Buscar intenção
  • PATCH /api/listings/[id]/intentions/[intentionId] - Atualizar status (aceitar/rejeitar)

Arquivo: app/api/listings/[id]/purchase/route.ts (MODIFICAR)

Mudança:

  • Ao invés de mudar listing_status para "sold", criar uma PurchaseIntention
  • Notificar vendedor sobre nova intenção

FASE 2: Sistema de Notificações Visual

Etapa 2.1: API de Contagem de Notificações Não Lidas

Arquivo: app/api/notifications/unread-count/route.ts

// GET /api/notifications/unread-count
// Retorna: { count: number }

Já existe função getUnreadCount() em lib/prisma/queries/notifications.ts - apenas criar rota


Etapa 2.2: Componente NotificationBell

Arquivo: components/notifications/NotificationBell.tsx

Funcionalidades:

  • Badge com contador de não lidas
  • Ícone de sino (Bell do lucide-react)
  • Dropdown ao clicar (usar NotificationDropdown)
  • Polling automático a cada 30 segundos
  • Animações quando há novas notificações

Props:

interface NotificationBellProps {
  userId?: number;
  className?: string;
}

Estado:

  • unreadCount: number
  • isOpen: boolean
  • notifications: Notification[]

Etapa 2.3: Componente NotificationDropdown

Arquivo: components/notifications/NotificationDropdown.tsx

Funcionalidades:

  • Lista de notificações recentes (últimas 10)
  • Link "Ver todas" para página completa
  • Marcar como lida ao clicar
  • Diferentes tipos de notificação (compra, lance, etc.)
  • Timestamps formatados

Etapa 2.4: Integrar no Header

Arquivo: components/Header.tsx

Mudanças:

  • Adicionar NotificationBell no navbar
  • Posicionar ao lado do menu de usuário
  • Apenas exibir se usuário estiver logado

FASE 3: Exibição de Vendedor

Etapa 3.1: Componente SellerInfo

Arquivo: components/marketplace/SellerInfo.tsx

Funcionalidades:

  • Avatar do vendedor (placeholder inicial, preparado para imagem)
  • Nome do personagem
  • Badge "Verificado" se aplicável
  • Link para perfil do vendedor (futuro)

Props:

interface SellerInfoProps {
  sellerId: number;
  sellerName: string;
  sellerImage?: string | null;
  verified?: boolean;
  serverName: string;
}

Design:

  • Avatar circular com fallback (iniciais do nome)
  • Layout horizontal compacto
  • Responsivo

Etapa 3.2: Integrar no AdCard

Arquivo: components/marketplace/AdCard.tsx

Mudanças:

  • Adicionar SellerInfo no card (modo grid e list)
  • Posicionar acima ou abaixo do preço
  • Estilizar consistentemente

FASE 4: Sistema de Lances (Bids) - Frontend Completo

Etapa 4.1: Melhorar BidModal

Arquivo: components/marketplace/BidModal.tsx

Melhorias:

  • Exibir histórico de lances (últimos 5)
  • Mostrar "Seu lance atual" se usuário já deu lance
  • Indicador visual de lance mínimo
  • Feedback imediato após dar lance
  • Refresh automático do valor do anúncio

Novos campos:

  • Histórico de lances
  • Timestamp do lance
  • Informações do último lanceador

Etapa 4.2: API de Histórico de Lances

Arquivo: app/api/listings/[id]/bids/route.ts

Endpoint:

  • GET /api/listings/[id]/bids - Listar lances de um anúncio

Resposta:

{
  bids: Array<{
    bid_id: number;
    bidder_id: number;
    bidder_name: string;
    bid_amount: number;
    bid_type: string | null;
    created_at: Date;
  }>;
  total: number;
}

Etapa 4.3: Atualizar BidModal com Histórico

Mudanças:

  • Buscar histórico ao abrir modal
  • Exibir lista de lances anteriores
  • Mostrar "Você está na liderança" se aplicável
  • Atualizar valor mínimo dinamicamente

Etapa 4.4: Melhorar AdCard para Bids

Arquivo: components/marketplace/AdCard.tsx

Melhorias:

  • Exibir "X lances" ao invés de apenas valor atual
  • Indicador visual de leilão ativo
  • Contador regressivo se auction_ends_at estiver definido
  • Badge "Novo lance" se houver lance recente

FASE 5: Telemetria Completa

Etapa 5.1: Expandir Eventos de Telemetria

Arquivo: lib/prisma/queries/telemetry.ts

Novos tipos de eventos:

  • bid_placed - Lance realizado
  • bid_viewed - Histórico de lances visualizado
  • purchase_intention_created - Intenção de compra criada
  • purchase_intention_viewed - Intenção visualizada
  • seller_profile_viewed - Perfil do vendedor visualizado

Atualizar interface:

export type TelemetryEventType =
  | "ad_view"
  | "ad_click"
  | "ad_exit"
  | "search"
  | "filter"
  | "favorite"
  | "unfavorite"
  | "purchase"
  | "purchase_intention_created"  // NOVO
  | "purchase_intention_viewed"   // NOVO
  | "bid_placed"                  // NOVO
  | "bid_viewed"                  // NOVO
  | "seller_profile_viewed"       // NOVO
  | "inquiry"
  | "page_view"
  | "price_change";

Etapa 5.2: Tracking de Lances

Arquivo: app/api/listings/[id]/bid/route.ts

Mudanças:

  • Após criar lance com sucesso, registrar telemetria:
    await trackEvent({
      event_type: "bid_placed",
      ad_id: listingId,
      user_id: bidderId,
      metadata: {
        bid_amount: amount,
        payment_method: paymentMethod,
        previous_bid: listing.current_bid_amount,
        min_increment: listing.min_bid_increment,
      },
    });
    

Etapa 5.3: Tracking de Intenções de Compra

Arquivo: app/api/listings/[id]/intentions/route.ts

Mudanças:

  • Após criar intenção, registrar telemetria:
    await trackEvent({
      event_type: "purchase_intention_created",
      ad_id: listingId,
      user_id: buyerId,
      metadata: {
        payment_method: paymentMethod,
        listing_price_pix: listing.price_pix,
        listing_price_tc: listing.price_tibia_coins,
        listing_price_gp: listing.price_gold_coins,
      },
    });
    

Etapa 5.4: Tracking de Visualizações de Vendedor

Arquivo: components/marketplace/SellerInfo.tsx

Mudanças:

  • Ao montar componente, registrar telemetria:
    useEffect(() => {
      trackEvent({
        event_type: "seller_profile_viewed",
        ad_id: listingId,
        metadata: {
          seller_id: sellerId,
          seller_name: sellerName,
        },
      });
    }, [sellerId, listingId]);
    

Etapa 5.5: Analytics Dashboard (Opcional - Futuro)

Arquivo: components/marketplace/AnalyticsDashboard.tsx (já existe)

Melhorias futuras:

  • Gráfico de lances ao longo do tempo
  • Anúncios mais clicados
  • Horários de pico de acesso
  • Taxa de conversão de intenções

FASE 6: Melhorias na Página "Meus Anúncios"

Etapa 6.1: Componente MyAdsIntentionList

Arquivo: components/marketplace/MyAdsIntentionList.tsx

Funcionalidades:

  • Lista de intenções de compra para um anúncio específico
  • Filtros (pendentes, aceitas, rejeitadas)
  • Ações: aceitar, rejeitar, ver detalhes
  • Badge com contador de pendentes

Props:

interface MyAdsIntentionListProps {
  listingId: number;
  sellerId: number;
}

Etapa 6.2: Atualizar MyAdsPanel

Arquivo: components/marketplace/MyAdsPanel.tsx

Mudanças:

  • Adicionar seção de intenções em cada card de anúncio
  • Expandir/colapsar intenções
  • Mostrar resumo: "X intenções pendentes"
  • Link para ver todas as intenções

Layout:

┌─────────────────────────────────┐
│ [Anúncio: Item X]               │
│ Status: Active                  │
│                                 │
│ 📊 Intenções de Compra          │
│ └─ 3 pendentes                  │
│    [Ver todas] [Expandir]       │
│                                 │
│ 💰 Lances (se for leilão)       │
│ └─ 5 lances, atual: R$ 100      │
│    [Ver histórico]              │
└─────────────────────────────────┘

Etapa 6.3: Modal de Detalhes de Intenção

Arquivo: components/marketplace/IntentionDetailModal.tsx

Funcionalidades:

  • Exibir informações completas da intenção
  • Dados do comprador (nome, personagem)
  • Método de pagamento escolhido
  • Mensagem do comprador (se houver)
  • Botões: Aceitar / Rejeitar
  • Campo para resposta do vendedor

📊 Sequência de Implementação Recomendada

Sprint 1: Fundação (Backend)

  1. ✅ Migração de banco de dados (Fase 1.1)
  2. ✅ Schema Prisma (Fase 1.2)
  3. ✅ Queries Prisma (Fase 1.3)
  4. ✅ API Routes básicas (Fase 1.4)

Sprint 2: Frontend Core

  1. ✅ Componente SellerInfo (Fase 3.1)
  2. ✅ Integrar SellerInfo no AdCard (Fase 3.2)
  3. ✅ NotificationBell e Dropdown (Fase 2.1-2.3)
  4. ✅ Integrar no Header (Fase 2.4)

Sprint 3: Sistema de Intenções

  1. ✅ PurchaseIntentionModal (Fase 1 - Frontend)
  2. ✅ Modificar PurchaseModal para criar intenção
  3. ✅ MyAdsIntentionList (Fase 6.1)
  4. ✅ Atualizar MyAdsPanel (Fase 6.2)

Sprint 4: Sistema de Lances

  1. ✅ API de histórico de lances (Fase 4.2)
  2. ✅ Melhorar BidModal com histórico (Fase 4.1, 4.3)
  3. ✅ Melhorar AdCard para bids (Fase 4.4)

Sprint 5: Telemetria

  1. ✅ Expandir eventos (Fase 5.1)
  2. ✅ Tracking de lances (Fase 5.2)
  3. ✅ Tracking de intenções (Fase 5.3)
  4. ✅ Tracking de vendedor (Fase 5.4)

🎨 Design e UX

Componentes Visuais

  1. NotificationBell:

    • Ícone: Bell do lucide-react
    • Badge vermelho com contador
    • Animação de pulse quando há novas
    • Dropdown com sombra e bordas arredondadas
  2. SellerInfo:

    • Avatar: 40x40px, circular
    • Nome: texto médio, bold
    • Badge verificado: ícone de check, verde
    • Layout: flex horizontal, espaçamento adequado
  3. IntentionList:

    • Cards compactos por intenção
    • Badge de status (pendente: amarelo, aceita: verde, rejeitada: vermelho)
    • Avatar do comprador
    • Método de pagamento com ícone
  4. BidHistory:

    • Lista vertical com timestamps
    • Destaque para lance atual
    • Indicador de "seu lance"

⚠️ Considerações e Riscos

Riscos Identificados

  1. Performance:

    • Muitas intenções por anúncio pode degradar performance
    • Mitigação: Paginação e lazy loading
  2. Notificações:

    • Polling pode sobrecarregar servidor
    • Mitigação: Polling inteligente (mais frequente quando há novas notificações)
  3. Concorrência:

    • Múltiplos lances simultâneos
    • Mitigação: Transações e validação de incremento mínimo
  4. Telemetria:

    • Muitos eventos podem sobrecarregar banco
    • Mitigação: Batch de eventos, agregação periódica

Validações Necessárias

  • ✅ Verificar se método de pagamento escolhido está disponível no anúncio
  • ✅ Validar que comprador não é o vendedor
  • ✅ Validar incremento mínimo de lance
  • ✅ Validar que anúncio ainda está ativo
  • ✅ Validar que leilão não expirou

🧪 Testes Necessários

Testes Unitários

  • Queries de purchase_intentions
  • Validação de métodos de pagamento
  • Cálculo de incremento mínimo de lance

Testes de Integração

  • Fluxo completo de criação de intenção
  • Notificação ao vendedor
  • Aceitar/rejeitar intenção
  • Sistema de lances com múltiplos usuários

Testes E2E

  • Comprador cria intenção → Vendedor recebe notificação
  • Vendedor aceita intenção → Comprador é notificado
  • Múltiplos lances → Valor atualiza corretamente
  • Telemetria registra todos os eventos

📈 Métricas de Sucesso

  1. Funcionalidade:

    • ✅ Intenções de compra funcionando
    • ✅ Notificações aparecendo corretamente
    • ✅ Lances atualizando valor
    • ✅ Vendedores aparecendo nos cards
  2. Performance:

    • Página de marketplace carrega em < 2s
    • Notificações atualizam em < 1s
    • Queries de intenções em < 500ms
  3. UX:

    • Interface intuitiva e responsiva
    • Feedback visual claro
    • Mensagens de erro compreensíveis

🚀 Próximos Passos

  1. Revisar este plano e ajustar conforme necessário
  2. Priorizar fases baseado em necessidade do negócio
  3. Iniciar Fase 1 (Backend de intenções)
  4. Testar incrementalmente cada fase antes de avançar

📚 Referências


Plano criado em: 2025-01-29
Versão: 1.0
Status: Pronto para implementação