Files
pagodera/docs/PLANO_MELHORIAS_MARKETPLACE.md
T
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

799 lines
22 KiB
Markdown

# 📋 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`
```sql
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`
```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`
```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:**
```typescript
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`
```typescript
// 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:**
```typescript
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:**
```typescript
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:**
```typescript
{
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:**
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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:**
```typescript
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
5. ✅ Componente SellerInfo (Fase 3.1)
6. ✅ Integrar SellerInfo no AdCard (Fase 3.2)
7. ✅ NotificationBell e Dropdown (Fase 2.1-2.3)
8. ✅ Integrar no Header (Fase 2.4)
### Sprint 3: Sistema de Intenções
9. ✅ PurchaseIntentionModal (Fase 1 - Frontend)
10. ✅ Modificar PurchaseModal para criar intenção
11. ✅ MyAdsIntentionList (Fase 6.1)
12. ✅ Atualizar MyAdsPanel (Fase 6.2)
### Sprint 4: Sistema de Lances
13. ✅ API de histórico de lances (Fase 4.2)
14. ✅ Melhorar BidModal com histórico (Fase 4.1, 4.3)
15. ✅ Melhorar AdCard para bids (Fase 4.4)
### Sprint 5: Telemetria
16. ✅ Expandir eventos (Fase 5.1)
17. ✅ Tracking de lances (Fase 5.2)
18. ✅ Tracking de intenções (Fase 5.3)
19. ✅ 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
- [Prisma Relations](https://www.prisma.io/docs/concepts/components/prisma-schema/relations)
- [Next.js Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations)
- [React Query/Polling](https://tanstack.com/query/latest/docs/react/guides/window-focus-refetching)
---
**Plano criado em:** 2025-01-29
**Versão:** 1.0
**Status:** Pronto para implementação