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

+34
View File
@@ -0,0 +1,34 @@
import sqlite3
import json
from pathlib import Path
# Caminhos
db_path = Path('data/tibiawiki.db')
backup_path = Path('data/tibiawiki.db.backup')
print('🔍 Analisando estrutura do banco...')
# Conectar ao banco
db = sqlite3.connect(str(db_path))
db.row_factory = sqlite3.Row
cursor = db.cursor()
# 1. Listar todas as tabelas
cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
tables = [row[0] for row in cursor.fetchall()]
print(f'✅ Encontradas {len(tables)} tabelas: {', '.join(tables[:5])}...')
# 2. Analisar estrutura de uma tabela para entender as colunas comuns
sample_table = tables[0]
cursor.execute(f'PRAGMA table_info({sample_table})')
sample_columns = [row[1] for row in cursor.fetchall()]
print(f'📋 Colunas da tabela {sample_table}: {', '.join(sample_columns[:10])}...')
# 3. Verificar uma amostra de dados
cursor.execute(f'SELECT * FROM {sample_table} LIMIT 1')
sample = cursor.fetchone()
if sample:
print(f'📦 Exemplo de item: {dict(sample)}')
db.close()
print('✅ Análise concluída!')
+220
View File
@@ -0,0 +1,220 @@
/**
* Script para popular a tabela items unificada a partir do SQLite
*
* Este script:
* 1. Lê todos os itens de todas as tabelas do SQLite
* 2. Insere na tabela items do PostgreSQL com IDs únicos
* 3. Mapeia os dados para o formato unificado
*/
import { config } from "dotenv";
import { resolve } from "path";
import path from "node:path";
// Carregar variáveis de ambiente
config({ path: resolve(process.cwd(), ".env.local") });
config({ path: resolve(process.cwd(), ".env") });
import { getPool, query } from "../lib/db/index";
import { enToPtCategoryMap } from "../lib/item-images";
// Mapeamento de tabelas PT para categorias EN
const ptToEnCategoryMap: Record<string, string> = {
espadas: "weapon",
armaduras: "armor",
capacetes: "helmet",
calcas: "legs",
botas: "boots",
escudos: "shield",
amuletos: "amulet",
aneis: "ring",
runas: "rune",
ferramentas: "tool",
municao: "ammunition",
};
function getDatabase() {
let Database: any;
try {
Database = require("better-sqlite3");
} catch (err) {
console.error("better-sqlite3 não está instalado");
return null;
}
const dbPath = path.join(process.cwd(), "data", "tibiawiki.db");
const db = new Database(dbPath, { readonly: true, fileMustExist: true });
return db;
}
interface ItemRow {
id: number;
nome: string;
image_url?: string;
image_local_path?: string;
rarity?: string;
lvl?: number;
peso?: number;
peso_em_oz?: number;
slots?: number;
skill_para_usar?: string;
[key: string]: any;
}
async function populateItemsTable() {
console.log("🚀 Iniciando população da tabela items...\n");
const db = getDatabase();
if (!db) {
console.error("❌ Não foi possível conectar ao SQLite");
process.exit(1);
}
try {
// Obter todas as tabelas
const tables = db
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
.all() as { name: string }[];
console.log(`📋 Encontradas ${tables.length} tabelas\n`);
let totalInserted = 0;
let totalSkipped = 0;
for (const table of tables) {
const tableName = table.name;
const categoryEn = ptToEnCategoryMap[tableName] || "other";
console.log(`📦 Processando tabela: ${tableName} (categoria: ${categoryEn})...`);
try {
// Verificar se a tabela tem as colunas necessárias
const columns = db
.prepare(`PRAGMA table_info(${tableName})`)
.all() as { name: string; type: string }[];
const colNames = new Set(columns.map((c) => c.name.toLowerCase()));
if (!colNames.has("id") || !colNames.has("nome")) {
console.log(` ⚠️ Tabela ${tableName} não tem colunas id/nome, pulando...`);
continue;
}
// Buscar todos os itens da tabela
const items = db.prepare(`SELECT * FROM ${tableName}`).all() as ItemRow[];
console.log(` 📊 Encontrados ${items.length} itens`);
for (const item of items) {
try {
// Verificar se já existe (baseado em source_table_name + source_table_id)
const existing = await query<{ item_id: number }>(
`SELECT item_id FROM items
WHERE source_table_name = $1 AND source_table_id = $2`,
[tableName, item.id]
);
if (existing.rows.length > 0) {
totalSkipped++;
continue;
}
// Preparar dados do item
const itemName = item.nome || "";
const imageUrl = item.image_url || null;
const imageLocalPath = item.image_local_path || null;
const rarity = item.rarity || null;
const level = item.lvl || null;
// Peso (pode ser em peso ou peso_em_oz)
const weight = item.peso || item.peso_em_oz || null;
const slots = item.slots || null;
const skill = item.skill_para_usar || null;
// Metadata com campos extras
const metadata: Record<string, any> = {};
// Adicionar campos extras ao metadata
const extraFields = [
"voc",
"volume",
"bonus_de_skill",
"protecao",
"dropa_de",
];
for (const field of extraFields) {
if (colNames.has(field) && item[field] !== undefined && item[field] !== null) {
metadata[field] = item[field];
}
}
// Inserir na tabela items
await query(
`INSERT INTO items (
source_table_id,
source_table_name,
item_name,
category,
image_local_path,
image_url,
rarity,
level,
weight,
slots,
skill,
metadata
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
[
item.id,
tableName,
itemName,
categoryEn,
imageLocalPath,
imageUrl,
rarity,
level,
weight,
slots,
skill,
JSON.stringify(metadata),
]
);
totalInserted++;
} catch (error: any) {
if (error.code === "23505") {
// Unique constraint violation - já existe
totalSkipped++;
} else {
console.error(` ❌ Erro ao inserir item ${item.nome}:`, error.message);
}
}
}
console.log(` ✅ Tabela ${tableName} processada\n`);
} catch (error: any) {
console.error(` ❌ Erro ao processar tabela ${tableName}:`, error.message);
}
}
console.log("\n📊 Resumo:");
console.log(` ✅ Itens inseridos: ${totalInserted}`);
console.log(` ⏭️ Itens ignorados (já existentes): ${totalSkipped}`);
console.log(` 📦 Total processado: ${totalInserted + totalSkipped}\n`);
console.log("✨ População concluída com sucesso!");
} finally {
db.close();
const pool = getPool();
await pool.end();
}
}
// Executar
populateItemsTable().catch((error) => {
console.error("❌ Erro fatal:", error);
process.exit(1);
});
+38
View File
@@ -0,0 +1,38 @@
# Script PowerShell seguro para executar migração mantendo dados
Write-Host "🚀 Iniciando migração segura do banco de dados..." -ForegroundColor Cyan
Write-Host ""
Write-Host "📋 Esta migração irá:" -ForegroundColor Yellow
Write-Host " ✅ Adicionar colunas opcionais (min_bid_increment, initial_price_*)" -ForegroundColor Green
Write-Host " ✅ Preencher dados de leilões existentes" -ForegroundColor Green
Write-Host " ✅ NÃO remover nenhum dado" -ForegroundColor Green
Write-Host ""
# Executar migração
Write-Host "📝 Executando migração..." -ForegroundColor Cyan
pnpm run db:migrate
# Verificar se foi bem-sucedida
if ($LASTEXITCODE -eq 0) {
Write-Host ""
Write-Host "✅ Migração executada com sucesso!" -ForegroundColor Green
Write-Host ""
Write-Host "🔄 Regenerando Prisma Client..." -ForegroundColor Cyan
pnpm run prisma:generate
if ($LASTEXITCODE -eq 0) {
Write-Host ""
Write-Host "🎉 Migração completa! Todos os dados foram preservados." -ForegroundColor Green
} else {
Write-Host ""
Write-Host "⚠️ Migração executada, mas falha ao regenerar Prisma Client" -ForegroundColor Yellow
Write-Host " Execute manualmente: pnpm run prisma:generate" -ForegroundColor Yellow
}
} else {
Write-Host ""
Write-Host "❌ Erro ao executar migração" -ForegroundColor Red
exit 1
}
+39
View File
@@ -0,0 +1,39 @@
#!/bin/bash
# Script seguro para executar migração mantendo dados
echo "🚀 Iniciando migração segura do banco de dados..."
echo ""
echo "📋 Esta migração irá:"
echo " ✅ Adicionar colunas opcionais (min_bid_increment, initial_price_*)"
echo " ✅ Preencher dados de leilões existentes"
echo " ✅ NÃO remover nenhum dado"
echo ""
# Executar migração
echo "📝 Executando migração..."
pnpm run db:migrate
# Verificar se foi bem-sucedida
if [ $? -eq 0 ]; then
echo ""
echo "✅ Migração executada com sucesso!"
echo ""
echo "🔄 Regenerando Prisma Client..."
pnpm run prisma:generate
if [ $? -eq 0 ]; then
echo ""
echo "🎉 Migração completa! Todos os dados foram preservados."
else
echo ""
echo "⚠️ Migração executada, mas falha ao regenerar Prisma Client"
echo " Execute manualmente: pnpm run prisma:generate"
fi
else
echo ""
echo "❌ Erro ao executar migração"
exit 1
fi