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:
1 parent
55eebde0ba
commit
ab37b477df
106 files changed
+15261
-301
No files matched your search
@@ -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!')
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user