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)
This commit is contained in:
Evandro Fonseca Junior committed 2025-11-05 01:58:03 -03:00
1 parent 55eebde0ba
commit ab37b477df
106 files changed
+15261 -301

No files matched your search

+163
View File
@@ -0,0 +1,163 @@
# 🔍 Análise dos Erros Encontrados
## 📋 Problemas Identificados
### 1. ❌ Erro: "Decimal objects are not supported" (Linha 77-78)
**Causa:**
- O Prisma retorna campos `Decimal` (PostgreSQL) como objetos `Prisma.Decimal`, não como números JavaScript
- O Next.js não consegue serializar objetos `Decimal` ao passar dados de Server Components para Client Components
- Apenas objetos "plain" (números, strings, arrays, objetos simples) podem ser serializados
**Campos afetados:**
- `price_pix` (Decimal)
- `current_bid_amount` (Decimal)
- `min_bid_increment` (Decimal)
- `initial_price_pix` (Decimal)
**Solução aplicada:**
✅ Converter todos os campos `Decimal` para `number` usando `Number()` antes de retornar os dados
---
### 2. ⚠️ Warning: "Package postcss can't be external" (Linhas 15-20, 85-90, etc.)
**Causa:**
- Dependência transitiva (`sanitize-html` → `postcss`) não está instalada diretamente
- Next.js/Turbopack tenta externalizar o pacote mas não consegue resolvê-lo
**Impacto:**
- ⚠️ Apenas um warning, não quebra a aplicação
- Pode causar problemas em builds de produção
**Soluções possíveis:**
1. **Instalar postcss diretamente** (recomendado):
```bash
pnpm add postcss
```
2. **Adicionar ao package.json** explicitamente:
```json
"dependencies": {
"postcss": "^8.4.0"
}
```
---
### 3. ✅ Erro resolvido: "column does not exist" (Linha 45)
**Status:** ✅ **RESOLVIDO**
- A migração 014 foi executada com sucesso
- As colunas `min_bid_increment`, `initial_price_pix`, etc. foram adicionadas
- O erro não aparece mais nos logs recentes (linhas 75-76, 115-116 mostram queries funcionando)
---
## 🔧 Correções Implementadas
### Arquivo: `lib/prisma/queries/listings.ts`
**Antes:**
```typescript
return {
...listing,
price_gold_coins: listing.price_gold_coins ? Number(listing.price_gold_coins) : null,
// ❌ Campos Decimal não eram convertidos
};
```
**Depois:**
```typescript
// Converter todos os campos Decimal e BigInt para números JavaScript
return {
...listing,
price_pix: listing.price_pix ? Number(listing.price_pix) : null,
current_bid_amount: listing.current_bid_amount ? Number(listing.current_bid_amount) : null,
min_bid_increment: listing.min_bid_increment ? Number(listing.min_bid_increment) : null,
initial_price_pix: listing.initial_price_pix ? Number(listing.initial_price_pix) : null,
price_gold_coins: listing.price_gold_coins ? Number(listing.price_gold_coins) : null,
initial_price_gold_coins: listing.initial_price_gold_coins ? Number(listing.initial_price_gold_coins) : null,
seller_character_name: character?.character_name || null,
} as Listing;
```
**Funções corrigidas:**
- ✅ `createListing()`
- ✅ `getListingById()`
- ✅ `getListings()`
- ✅ `updateListing()`
- ✅ `placeBid()`
---
## 📊 Resultado
### ✅ Resolvido:
1. Erro de coluna não existente (migração executada)
2. Erro de serialização Decimal (conversão implementada)
### ⚠️ Pendente (não crítico):
1. Warning do postcss (pode ser ignorado ou resolvido instalando postcss)
---
## 🎯 Próximos Passos Recomendados
1. **Instalar postcss** (opcional, resolve warning):
```bash
pnpm add postcss
```
2. **Testar a aplicação:**
- Verificar se a página do marketplace carrega corretamente
- Verificar se os preços são exibidos corretamente
- Testar criação de anúncios
- Testar leilões e lances
3. **Monitorar logs:**
- Verificar se o erro "Decimal objects are not supported" não aparece mais
- Verificar se as queries estão funcionando corretamente
---
## 🔍 Por que isso acontece?
### Prisma + PostgreSQL + Decimal
O Prisma usa o tipo `Decimal` do Prisma para representar valores `DECIMAL` do PostgreSQL. Isso garante precisão decimal, mas cria objetos que não são serializáveis pelo React/Next.js.
**Fluxo do problema:**
```
PostgreSQL DECIMAL → Prisma.Decimal → ❌ Não serializável → Erro no React
```
**Solução:**
```
PostgreSQL DECIMAL → Prisma.Decimal → Number() → ✅ number (serializável)
```
### Serialização no Next.js
O Next.js precisa serializar dados ao passar de Server Components para Client Components. Ele usa JSON serialization, que não suporta:
- Classes customizadas
- Objetos `Decimal` do Prisma
- Funções
- Símbolos
- etc.
Apenas tipos primitivos JSON são suportados:
- ✅ `string`, `number`, `boolean`
- ✅ `null`, `undefined`
- ✅ Arrays e objetos simples
---
## 📚 Referências
- [Prisma Decimal Type](https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-decimal-numbers)
- [Next.js Serialization](https://nextjs.org/docs/app/building-your-application/data-fetching/patterns#async-server-components)
- [React Server Components](https://react.dev/reference/rsc/server-components)
+14
View File
@@ -0,0 +1,14 @@
## 📋 ANÁLISE DO PROBLEMA
### Problema Identificado
- O sistema usa SQLite com múltiplas tabelas (espadas, armaduras, etc)
- Cada tabela tem seus próprios IDs que podem se repetir entre tabelas
- Quando salvamos `item_id=1` no listing, não sabemos de qual tabela veio
- Ao buscar com `getItemById(1)`, pode retornar o item errado
### Solução Proposta
1. Criar tabela unificada `items` no PostgreSQL com IDs únicos
2. Popular a tabela a partir do SQLite
3. Usar JOINs nas queries para garantir consistência
4. Atualizar sistema para usar sempre o ID unificado
+798
View File
@@ -0,0 +1,798 @@
# 📋 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
+288
View File
@@ -0,0 +1,288 @@
# 📊 Prisma Query Logs - Explicação Completa
## 🔍 O que são os logs `prisma:query`?
Os logs `prisma:query` são **queries SQL que o Prisma executa automaticamente** no banco de dados PostgreSQL. Eles aparecem no console durante o desenvolvimento para ajudar você a:
1. **Debugar** - Ver exatamente quais queries estão sendo executadas
2. **Otimizar** - Identificar queries lentas ou desnecessárias
3. **Entender** - Compreender o que o Prisma está fazendo "por baixo dos panos"
---
## ⚙️ Configuração Atual
No arquivo `lib/prisma/app.ts`, o Prisma está configurado assim:
```typescript
export const prisma = new PrismaClient({
log:
process.env.NODE_ENV === "development"
? ["query", "error", "warn"] // ← Loga queries em desenvolvimento
: ["error"], // ← Apenas erros em produção
});
```
**Isso significa:**
- ✅ **Em desenvolvimento**: Mostra todas as queries SQL
- ✅ **Em produção**: Apenas mostra erros (não mostra queries)
---
## 📝 Análise dos Logs do Seu Terminal
### 1. **INSERT de Telemetria** (Linhas 24-26, 58-59, 93-94)
```sql
INSERT INTO "public"."telemetry_events"
("event_type","ad_id","user_id","session_id","metadata","timestamp")
VALUES ($1,$2,$3,$4,$5,$6)
RETURNING ...
```
**O que é:**
- Registra eventos de telemetria (cliques, visualizações, etc.)
- Executado quando o usuário interage com a página
**Por que aparece:**
- O sistema de telemetria está ativo e registrando eventos
---
### 2. **SELECT de Listings** (Linhas 35-36, 79-80)
```sql
SELECT COUNT(*) AS "_count$_all"
FROM (SELECT "public"."listings"."listing_id"
FROM "public"."listings"
WHERE "public"."listings"."listing_status" = $1
OFFSET $2) AS "sub"
```
**O que é:**
- Conta quantos anúncios ativos existem
- Usado para paginação
**E depois:**
```sql
SELECT "public"."listings"."listing_id",
"public"."listings"."seller_id",
"public"."listings"."item_id",
-- ... todos os campos ...
FROM "public"."listings"
WHERE "public"."listings"."listing_status" = $1
ORDER BY "public"."listings"."created_at" DESC
LIMIT $2 OFFSET $3
```
**O que é:**
- Busca os anúncios ativos com paginação
- Ordena por data de criação (mais recentes primeiro)
- Limita a 20 resultados por página
**Por que aparece:**
- A página do marketplace (`/en/market`) está carregando
- Está buscando anúncios para exibir
---
### 3. **SELECT de Characters** (Linhas 37, 81)
```sql
SELECT "public"."user_characters"."character_id",
"public"."user_characters"."user_id",
"public"."user_characters"."character_name",
"public"."user_characters"."server_name",
"public"."user_characters"."is_main",
"public"."user_characters"."verified",
"public"."user_characters"."created_at"
FROM "public"."user_characters"
WHERE ("public"."user_characters"."user_id" = $1
AND "public"."user_characters"."server_name" = $2
AND "public"."user_characters"."is_main" = $3)
LIMIT $4 OFFSET $5
```
**O que é:**
- Busca o personagem principal do vendedor
- Usado para exibir o nome do personagem nos cards de anúncio
**Por que aparece:**
- Para cada anúncio, busca o nome do personagem do vendedor
- Isso acontece porque implementamos a busca de `seller_character_name`
---
### 4. **SELECT de Servers** (Linha 66)
```sql
SELECT "public"."servers"."server_id",
"public"."servers"."server_name",
-- ... outros campos ...
FROM "public"."servers"
WHERE "public"."servers"."is_active" = $1
ORDER BY "public"."servers"."server_name" ASC
OFFSET $2
```
**O que é:**
- Busca todos os servidores ativos
- Usado para popular o filtro de servidores no marketplace
**Por que aparece:**
- O componente de filtros está carregando a lista de servidores
---
### 5. **BEGIN / COMMIT** (Linhas 23, 27, 57, 60, 92, 95)
```sql
BEGIN
-- ... queries ...
COMMIT
```
**O que é:**
- **Transações** do banco de dados
- Garante que múltiplas operações sejam executadas atomicamente
- Se uma falhar, todas são revertidas (ROLLBACK)
**Por que aparece:**
- Operações que precisam de atomicidade (ex: inserir múltiplos eventos de telemetria em batch)
---
## 🎯 Por que isso é útil?
### ✅ Benefícios:
1. **Debugging:**
- Ver exatamente quais queries estão sendo executadas
- Identificar queries N+1 (muitas queries desnecessárias)
- Verificar se os índices estão sendo usados
2. **Performance:**
- Identificar queries lentas
- Ver quantas queries estão sendo executadas
- Otimizar queries que são executadas repetidamente
3. **Entendimento:**
- Aprender como o Prisma traduz suas operações para SQL
- Ver como os relacionamentos são resolvidos
---
## ⚙️ Como Controlar os Logs
### Opção 1: Desabilitar completamente (não recomendado em dev)
```typescript
// lib/prisma/app.ts
export const prisma = new PrismaClient({
log: ["error"], // Apenas erros
});
```
### Opção 2: Apenas erros e warnings
```typescript
export const prisma = new PrismaClient({
log: ["error", "warn"],
});
```
### Opção 3: Logs mais detalhados (com informações de tempo)
```typescript
export const prisma = new PrismaClient({
log: [
{ level: "query", emit: "event" },
{ level: "error", emit: "stdout" },
{ level: "warn", emit: "stdout" },
],
});
```
### Opção 4: Manter como está (recomendado)
```typescript
// Atual (já está assim)
export const prisma = new PrismaClient({
log:
process.env.NODE_ENV === "development"
? ["query", "error", "warn"] // ← Bom para desenvolvimento
: ["error"], // ← Otimizado para produção
});
```
**✅ Recomendação:** Manter como está! Os logs são úteis em desenvolvimento e não aparecem em produção.
---
## 🔍 Análise de Performance dos Seus Logs
### Queries que aparecem frequentemente:
1. **SELECT listings** (2 queries):
- Uma para contar total
- Uma para buscar os itens
- **Status:** ✅ Normal (paginação requer ambas)
2. **SELECT user_characters** (1 por anúncio):
- Busca o personagem do vendedor
- **Status:** ⚠️ Potencial otimização
- **Sugestão:** Já está otimizado com `Map` para evitar queries duplicadas
3. **INSERT telemetry_events** (em batch):
- Múltiplos eventos inseridos em uma transação
- **Status:** ✅ Otimizado (usa transação)
---
## 📊 Níveis de Log do Prisma
O Prisma suporta diferentes níveis de log:
```typescript
log: [
"query", // Todas as queries SQL
"info", // Informações gerais
"warn", // Avisos
"error", // Erros
]
```
**Ou com eventos:**
```typescript
log: [
{ level: "query", emit: "event" }, // Evento customizado
{ level: "error", emit: "stdout" }, // Console padrão
]
```
---
## 🎯 Conclusão
**Os logs `prisma:query` são:**
- ✅ **Normais** - Esperados em desenvolvimento
- ✅ **Úteis** - Ajudam a debugar e otimizar
- ✅ **Automáticos** - Não afetam performance
- ✅ **Desabilitados em produção** - Não aparecem em produção
**Você deve:**
- ✅ **Manter** os logs em desenvolvimento
- ✅ **Usar** para identificar problemas de performance
- ✅ **Ignorar** se não estiver debuggando (são apenas informativos)
---
## 🔗 Referências
- [Prisma Logging Documentation](https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/logging)
- [Prisma Query Performance](https://www.prisma.io/docs/guides/performance-and-optimization/query-optimization-performance)