Clears the 124 CODE_SMELL findings from the SonarQube scan (server, client, and bot). All changes are behaviour-preserving refactors — no route, protocol, schema, or config changes — verified against the full server (381) and client (43) test suites plus a clean client build. By rule: - S3776 (20, cognitive complexity): extract helpers/handlers so each function drops under the threshold — shard model upsert builders, page/wiki update, block validation, notification stream mapping (dispatch table), SSO mobile login, shard ingest deps, uo-link socket backfill/connect, the bot slash- command dispatchers + discord manager, and the Shard/UserDetail/HeroEditor/ CharacterStats React components. - S4624 (34, nested template literals): pull inner templates into locals / a withQs() helper; rewrite shardEvents.describe() as a formatter table. - S3358 (35, nested ternaries): lift to if/else vars, lookup maps, small components, or guarded JSX expressions. - S6479 (12, array-index React keys): key by stable content instead of index (two in-editor lists left as-is; index matches their by-index edit model). - S6353 (6): [0-9]/[^0-9] -> \d/\D. S125 (5): reword state-shape comments that parsed as code. S3800/S3782 (botScore): JSDoc-type PATH_WEIGHTS tuples. - S6481 (2): memoize Auth/Site context values (and SiteContext brand). - S4144: dedupe HeroEditor upload handler into useImageUpload(). - S1126 (2), S6035, S5869 (redundant A-Z under /i), S5843 (town-name regex -> prefix list): assorted one-liners. Co-Authored-By: Claude <noreply@anthropic.com>
42 lines
1.8 KiB
JavaScript
42 lines
1.8 KiB
JavaScript
import { useEffect, useState } from 'react'
|
||
import { ago } from '../lib/format.js'
|
||
|
||
// Owner-private recent player-vendor sales. `fetchSales` is the scope method
|
||
// (api.player.shard.sales / api.admin.shard.sales) — the server only returns
|
||
// sales for accounts linked to the caller.
|
||
export default function VendorSales({ fetchSales }) {
|
||
const [sales, setSales] = useState(null)
|
||
const [error, setError] = useState('')
|
||
|
||
useEffect(() => {
|
||
let active = true
|
||
fetchSales()
|
||
.then((rows) => active && setSales(rows))
|
||
.catch(() => active && setError('Could not load your vendor sales.'))
|
||
return () => { active = false }
|
||
}, [fetchSales])
|
||
|
||
if (error) return null
|
||
if (!sales) return null
|
||
|
||
return (
|
||
<section style={{ borderTop: '1px solid var(--line-soft)', marginTop: 30, paddingTop: 22 }}>
|
||
<div className="field-label" style={{ marginBottom: 12 }}>Recent vendor sales</div>
|
||
{sales.length === 0 ? (
|
||
<p className="sans dim" style={{ margin: 0, fontSize: '0.86rem' }}>No vendor sales recorded yet.</p>
|
||
) : (
|
||
<ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||
{sales.map((s) => (
|
||
<li key={`${s.t}-${s.itemType}-${s.price}`} className="sans" style={{ display: 'flex', justifyContent: 'space-between', gap: 12, fontSize: '0.9rem', color: 'var(--ink)' }}>
|
||
<span style={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||
{s.itemType || 'An item'}{s.amount > 1 ? ` ×${s.amount}` : ''} — {Number(s.price || 0).toLocaleString()}gp
|
||
</span>
|
||
<span className="dim" style={{ flex: 'none', fontSize: '0.78rem' }}>{ago(s.t)}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
)}
|
||
</section>
|
||
)
|
||
}
|