// Concepto traducible de una transacción de stripelog, compartido por los // historiales de PD/PV y de transacciones. Devuelve una CLAVE (+ argumentos) que // la página traduce; `conceptRaw` es la reserva para pagos heredados sin servicio. /** Slug de servicio (stripelog.service) -> clave de concepto (History.points.concept.*). */ export const SERVICE_CONCEPT: Record = { rename: 'rename', customize: 'customize', 'change-race': 'changeRace', 'change-faction': 'changeFaction', 'level-up': 'levelUp', gold: 'gold', transfer: 'transfer', 'restore-item': 'restoreItem', 'send-gift': 'sendGift', } /** PD acreditados por un pago (si es una compra de PD), o null si es un servicio/ítem. */ export function purchasedPD(productName: string, service: string | null, metadata: string | null): number | null { const m = /(\d+)\s*PD\b/i.exec(productName || '') if (m) return Number(m[1]) if (service === 'dpoints') { try { const p = metadata ? JSON.parse(metadata).points : null return p ? Number(p) : null } catch { return null } } return null } export interface Concept { conceptKey: string | null conceptArgs: Record conceptRaw: string } /** * Concepto de una fila de stripelog: compra de PD -> "{n} PD"; servicio * conocido -> "{etiqueta}: {personaje}"; en otro caso, texto libre heredado. */ export function buildConcept( productName: string, service: string | null, characterName: string, metadata: string | null, ): Concept { const raw = productName || '—' const pd = purchasedPD(raw, service, metadata) if (pd != null && (service === 'dpoints' || service === null)) { return { conceptKey: 'pd', conceptArgs: { n: pd }, conceptRaw: raw } } if (service && SERVICE_CONCEPT[service]) { return { conceptKey: SERVICE_CONCEPT[service], conceptArgs: { detail: characterName }, conceptRaw: raw } } return { conceptKey: null, conceptArgs: {}, conceptRaw: raw } }