Armería: búsqueda insensible a mayúsculas, autocompletar y proxy del visor 3D
Tres arreglos: 1) Búsqueda insensible a mayúsculas/minúsculas. Las consultas usaban LIKE directo (sensible según la colación de la tabla), así que 'test' no encontraba 'Test'. Ahora comparan con LOWER(col) LIKE LOWER(?). 2) Autocompletar tipo wowhead. Nuevo /api/armory/suggest + ArmorySearchBox reescrito: al teclear (≥1 carácter, con debounce) muestra un desplegable con personajes, ítems y hermandades que coinciden; clic va directo a la ficha/wowhead. Enter o el botón siguen haciendo la búsqueda completa. 3) Visor 3D «no disponible». Causa: el contenido de wow.zamimg.com/modelviewer no envía cabeceras CORS (el propio README de wow-model-viewer pide un proxy). Se añade un rewrite en next.config (/modelviewer/* -> zamimg) para servirlo same-origin, y el visor usa esas rutas. Además, si falla la resolución de ítems se renderiza el personaje sin equipo, y los errores se registran en consola para diagnóstico. Verificado: 'test' encuentra 'Test'; el autocompletar responde; el proxy /modelviewer/live/viewer/viewer.min.js sirve 200 same-origin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+19
-6
@@ -46,8 +46,8 @@ const LIMIT = 25
|
||||
export async function searchCharacters(q: string, limit = LIMIT): Promise<CharacterResult[]> {
|
||||
try {
|
||||
const [rows] = await db(DB.characters).query<RowDataPacket[]>(
|
||||
'SELECT guid, name, race, gender, class, level FROM characters WHERE name LIKE ? ORDER BY level DESC, name LIMIT ?',
|
||||
[`%${q}%`, limit],
|
||||
'SELECT guid, name, race, gender, class, level FROM characters WHERE LOWER(name) LIKE ? ORDER BY level DESC, name LIMIT ?',
|
||||
[`%${q.toLowerCase()}%`, limit],
|
||||
)
|
||||
return rows.map((r) => ({
|
||||
guid: r.guid,
|
||||
@@ -66,8 +66,8 @@ export async function searchGuilds(q: string, limit = LIMIT): Promise<GuildResul
|
||||
try {
|
||||
const [rows] = await db(DB.characters).query<RowDataPacket[]>(
|
||||
`SELECT g.guildid, g.name, (SELECT COUNT(*) FROM guild_member gm WHERE gm.guildid = g.guildid) AS members
|
||||
FROM guild g WHERE g.name LIKE ? ORDER BY members DESC LIMIT ?`,
|
||||
[`%${q}%`, limit],
|
||||
FROM guild g WHERE LOWER(g.name) LIKE ? ORDER BY members DESC LIMIT ?`,
|
||||
[`%${q.toLowerCase()}%`, limit],
|
||||
)
|
||||
return rows.map((r) => ({ guildid: r.guildid, name: r.name, members: Number(r.members ?? 0) }))
|
||||
} catch {
|
||||
@@ -80,8 +80,8 @@ export async function searchItems(q: string, locale: string, limit = LIMIT): Pro
|
||||
try {
|
||||
const [rows] = await db(DB.default).query<RowDataPacket[]>(
|
||||
`SELECT entry, COALESCE(${col}, name_en) AS name, quality, item_level, inventory_type
|
||||
FROM item_data WHERE ${col} LIKE ? OR name_en LIKE ? ORDER BY item_level DESC LIMIT ?`,
|
||||
[`%${q}%`, `%${q}%`, limit],
|
||||
FROM item_data WHERE LOWER(${col}) LIKE ? OR LOWER(name_en) LIKE ? ORDER BY item_level DESC LIMIT ?`,
|
||||
[`%${q.toLowerCase()}%`, `%${q.toLowerCase()}%`, limit],
|
||||
)
|
||||
return rows.map((r) => ({
|
||||
entry: r.entry,
|
||||
@@ -95,6 +95,19 @@ export async function searchItems(q: string, locale: string, limit = LIMIT): Pro
|
||||
}
|
||||
}
|
||||
|
||||
/** Sugerencias para el autocompletar: pocos resultados de cada tipo. */
|
||||
export async function suggest(
|
||||
q: string,
|
||||
locale: string,
|
||||
): Promise<{ characters: CharacterResult[]; items: ItemResult[]; guilds: GuildResult[] }> {
|
||||
const [characters, items, guilds] = await Promise.all([
|
||||
searchCharacters(q, 6),
|
||||
searchItems(q, locale, 6),
|
||||
searchGuilds(q, 5),
|
||||
])
|
||||
return { characters, items, guilds }
|
||||
}
|
||||
|
||||
export async function getCharacter(guid: number): Promise<
|
||||
(CharacterResult & { guildName: string | null; guildId: number | null; online: boolean }) | null
|
||||
> {
|
||||
|
||||
Reference in New Issue
Block a user