- 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)
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
-
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
-
Melhorias na Página "Meus Anúncios"
- Exibir dados de compradores interessados
- Mostrar informações de cada intenção de compra
-
Ícone de Notificações
- Badge com contador de notificações não lidas
- Visual no header/navbar
-
Exibição de Vendedor
- Nome do vendedor nos cards
- Avatar/imagem do vendedor (preparar para integração futura)
-
Sistema de Lances (Bids)
- Permitir que usuários deem lances
- Atualizar valor do item conforme lances
- Integração completa no frontend
-
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)
-
Novos Arquivos:
lib/db/migrations/015_add_purchase_intentions.sqllib/prisma/queries/purchase-intentions.tsapp/api/listings/[id]/intentions/route.tsapp/api/listings/[id]/intentions/[intentionId]/route.tsapp/api/notifications/unread-count/route.ts
-
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)
-
Novos Arquivos:
components/notifications/NotificationBell.tsxcomponents/notifications/NotificationDropdown.tsxcomponents/marketplace/PurchaseIntentionModal.tsxcomponents/marketplace/MyAdsIntentionList.tsxcomponents/marketplace/SellerInfo.tsx
-
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 únicolisting_id: Anúncio relacionadobuyer_id: Comprador interessadopayment_method: Método de pagamento escolhidostatus: Estado da intenção (pending, accepted, rejected, expired, completed)buyer_message: Mensagem opcional do compradorseller_response: Resposta do vendedorexpires_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
purchaseIntentionsno modelListing - Adicionar relação
purchaseIntentionsno modelUser(comoBuyerIntentions)
Etapa 1.3: Queries Prisma
Arquivo: lib/prisma/queries/purchase-intentions.ts
Funções a criar:
createPurchaseIntention(data)- Criar intençãogetPurchaseIntentionsByListing(listingId, filters)- Listar intenções de um anúnciogetPurchaseIntentionsByBuyer(buyerId, filters)- Listar intenções de um compradorgetPurchaseIntentionById(intentionId)- Buscar intenção específicaupdatePurchaseIntentionStatus(intentionId, status, sellerResponse)- Atualizar statusgetPendingIntentionsCount(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 compraGET /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çãoPATCH /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_statuspara "sold", criar umaPurchaseIntention - 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: numberisOpen: booleannotifications: 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
NotificationBellno 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
SellerInfono 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_atestiver 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 realizadobid_viewed- Histórico de lances visualizadopurchase_intention_created- Intenção de compra criadapurchase_intention_viewed- Intenção visualizadaseller_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)
- ✅ Migração de banco de dados (Fase 1.1)
- ✅ Schema Prisma (Fase 1.2)
- ✅ Queries Prisma (Fase 1.3)
- ✅ API Routes básicas (Fase 1.4)
Sprint 2: Frontend Core
- ✅ Componente SellerInfo (Fase 3.1)
- ✅ Integrar SellerInfo no AdCard (Fase 3.2)
- ✅ NotificationBell e Dropdown (Fase 2.1-2.3)
- ✅ Integrar no Header (Fase 2.4)
Sprint 3: Sistema de Intenções
- ✅ PurchaseIntentionModal (Fase 1 - Frontend)
- ✅ Modificar PurchaseModal para criar intenção
- ✅ MyAdsIntentionList (Fase 6.1)
- ✅ Atualizar MyAdsPanel (Fase 6.2)
Sprint 4: Sistema de Lances
- ✅ API de histórico de lances (Fase 4.2)
- ✅ Melhorar BidModal com histórico (Fase 4.1, 4.3)
- ✅ Melhorar AdCard para bids (Fase 4.4)
Sprint 5: Telemetria
- ✅ Expandir eventos (Fase 5.1)
- ✅ Tracking de lances (Fase 5.2)
- ✅ Tracking de intenções (Fase 5.3)
- ✅ Tracking de vendedor (Fase 5.4)
🎨 Design e UX
Componentes Visuais
-
NotificationBell:
- Ícone:
Belldo lucide-react - Badge vermelho com contador
- Animação de pulse quando há novas
- Dropdown com sombra e bordas arredondadas
- Ícone:
-
SellerInfo:
- Avatar: 40x40px, circular
- Nome: texto médio, bold
- Badge verificado: ícone de check, verde
- Layout: flex horizontal, espaçamento adequado
-
IntentionList:
- Cards compactos por intenção
- Badge de status (pendente: amarelo, aceita: verde, rejeitada: vermelho)
- Avatar do comprador
- Método de pagamento com ícone
-
BidHistory:
- Lista vertical com timestamps
- Destaque para lance atual
- Indicador de "seu lance"
⚠️ Considerações e Riscos
Riscos Identificados
-
Performance:
- Muitas intenções por anúncio pode degradar performance
- Mitigação: Paginação e lazy loading
-
Notificações:
- Polling pode sobrecarregar servidor
- Mitigação: Polling inteligente (mais frequente quando há novas notificações)
-
Concorrência:
- Múltiplos lances simultâneos
- Mitigação: Transações e validação de incremento mínimo
-
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
-
Funcionalidade:
- ✅ Intenções de compra funcionando
- ✅ Notificações aparecendo corretamente
- ✅ Lances atualizando valor
- ✅ Vendedores aparecendo nos cards
-
Performance:
- Página de marketplace carrega em < 2s
- Notificações atualizam em < 1s
- Queries de intenções em < 500ms
-
UX:
- Interface intuitiva e responsiva
- Feedback visual claro
- Mensagens de erro compreensíveis
🚀 Próximos Passos
- Revisar este plano e ajustar conforme necessário
- Priorizar fases baseado em necessidade do negócio
- Iniciar Fase 1 (Backend de intenções)
- 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