update nice visuel
This commit is contained in:
+145
-39
@@ -35,10 +35,10 @@ export const api = {
|
||||
catalog: () => request('/api/catalog'),
|
||||
prestige: () => request('/api/prestige'),
|
||||
doPrestige: () => request('/api/prestige', { method: 'POST', body: JSON.stringify({}) }),
|
||||
prestigeUpgrade: (skillId) =>
|
||||
prestigeUpgrade: (skillId, count = 1) =>
|
||||
request('/api/prestige/upgrade', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ skillId }),
|
||||
body: JSON.stringify({ skillId, count }),
|
||||
}),
|
||||
openCase: (id, count = 1) =>
|
||||
request(`/api/cases/${id}/open`, {
|
||||
@@ -56,7 +56,6 @@ export const api = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ inventoryItemIds }),
|
||||
}),
|
||||
vaultClaim: () => request('/api/vault/claim', { method: 'POST' }),
|
||||
sellInventory: (inventoryItemIds) =>
|
||||
request('/api/inventory/sell', {
|
||||
method: 'POST',
|
||||
@@ -83,41 +82,65 @@ export const api = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ packageId }),
|
||||
}),
|
||||
shopOsuCheckout: (packId) =>
|
||||
request('/api/shop/osu/checkout', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ packId }),
|
||||
}),
|
||||
shopOsuConfirm: (orderId) =>
|
||||
request('/api/shop/osu/confirm', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ orderId }),
|
||||
}),
|
||||
shopAd: () => request('/api/shop/ad', { method: 'POST' }),
|
||||
feed: () => request('/api/feed'),
|
||||
feedAnnounce: (inventoryItemIds, caseName) => {
|
||||
feedAnnounce: (inventoryItemIds, caseName, opts = {}) => {
|
||||
const ids = Array.isArray(inventoryItemIds)
|
||||
? inventoryItemIds
|
||||
: [inventoryItemIds];
|
||||
: inventoryItemIds != null
|
||||
? [inventoryItemIds]
|
||||
: [];
|
||||
const body = {
|
||||
inventoryItemIds: ids.filter(Boolean),
|
||||
caseName,
|
||||
respin: Boolean(opts.respin),
|
||||
};
|
||||
if (opts.snapshotBest) {
|
||||
body.snapshotBest = opts.snapshotBest;
|
||||
body.openCount = opts.openCount;
|
||||
body.count = opts.count;
|
||||
} else if (opts.snapshotItems) {
|
||||
body.snapshotItems = opts.snapshotItems;
|
||||
}
|
||||
return request('/api/feed/announce', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ inventoryItemIds: ids, caseName }),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
},
|
||||
leaderboard: () => request('/api/leaderboard'),
|
||||
jackpot: () => request('/api/jackpot'),
|
||||
jackpotBet: (inventoryItemIds) =>
|
||||
jackpotRound: (id) => request(`/api/jackpot/${id}`),
|
||||
jackpotCreateBet: (inventoryItemIds) =>
|
||||
request('/api/jackpot/bet', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ inventoryItemIds }),
|
||||
}),
|
||||
jackpotWithdraw: (inventoryItemIds) =>
|
||||
request('/api/jackpot/withdraw', {
|
||||
jackpotBet: (id, inventoryItemIds) =>
|
||||
request(`/api/jackpot/${id}/bet`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ inventoryItemIds }),
|
||||
}),
|
||||
duels: () => request('/api/duels'),
|
||||
duel: (id) => request(`/api/duels/${id}`),
|
||||
createDuel: (body) =>
|
||||
request('/api/duels', { method: 'POST', body: JSON.stringify(body) }),
|
||||
duelBet: (id, inventoryItemIds) =>
|
||||
request(`/api/duels/${id}/bet`, {
|
||||
jackpotWithdraw: (id, inventoryItemIds) =>
|
||||
request(`/api/jackpot/${id}/withdraw`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ inventoryItemIds }),
|
||||
}),
|
||||
// legacy alias
|
||||
jackpotCreate: (inventoryItemIds) =>
|
||||
request('/api/jackpot/bet', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ inventoryItemIds }),
|
||||
}),
|
||||
duelLeave: (id) => request(`/api/duels/${id}/leave`, { method: 'POST' }),
|
||||
duelCancel: (id) => request(`/api/duels/${id}/cancel`, { method: 'POST' }),
|
||||
duelStart: (id) => request(`/api/duels/${id}/start`, { method: 'POST' }),
|
||||
profile: () => request('/api/profile'),
|
||||
playerProfile: (username) =>
|
||||
request(`/api/profile/player/${encodeURIComponent(username)}`),
|
||||
@@ -140,6 +163,12 @@ export const api = {
|
||||
body: JSON.stringify({ username, password }),
|
||||
}),
|
||||
adminStats: () => request('/api/admin/stats'),
|
||||
adminPrestigeServerPreview: () => request('/api/admin/prestige-server'),
|
||||
adminPrestigeServer: (body) =>
|
||||
request('/api/admin/prestige-server', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
adminUsers: () => request('/api/admin/users'),
|
||||
adminUpdateUser: (id, body) =>
|
||||
request(`/api/admin/users/${id}`, {
|
||||
@@ -150,6 +179,17 @@ export const api = {
|
||||
adminItems: () => request('/api/admin/items'),
|
||||
adminRarities: () => request('/api/admin/rarities'),
|
||||
adminCategories: () => request('/api/admin/categories'),
|
||||
adminShopOsuPacks: () => request('/api/admin/shop/osu-packs'),
|
||||
adminShopOsuSection: (enabled) =>
|
||||
request('/api/admin/shop/osu-section', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ enabled }),
|
||||
}),
|
||||
adminUpdateShopOsuPack: (id, body) =>
|
||||
request(`/api/admin/shop/osu-packs/${encodeURIComponent(id)}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
createCategory: (body) =>
|
||||
request('/api/admin/categories', { method: 'POST', body: JSON.stringify(body) }),
|
||||
updateCategory: (id, body) =>
|
||||
@@ -186,17 +226,77 @@ export const api = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Exact credit display (cents → "1 234.56"), for tooltips / confirmations.
|
||||
* Parse money amounts (cents) to BigInt — mirror of server `cents.js`.
|
||||
* Accepts bigint, number, or decimal/int string. Truncates toward zero.
|
||||
*/
|
||||
export function toCentsBigInt(value) {
|
||||
if (typeof value === 'bigint') return value;
|
||||
if (value == null || value === '') return 0n;
|
||||
if (typeof value === 'number') {
|
||||
if (!Number.isFinite(value)) return 0n;
|
||||
return BigInt(Math.trunc(value));
|
||||
}
|
||||
const s = String(value).trim();
|
||||
if (!s) return 0n;
|
||||
const m = s.match(/^([+-]?\d+)/);
|
||||
if (!m) return 0n;
|
||||
try {
|
||||
return BigInt(m[1]);
|
||||
} catch {
|
||||
return 0n;
|
||||
}
|
||||
}
|
||||
|
||||
/** JSON-safe cents string. */
|
||||
export function centsJson(value) {
|
||||
return toCentsBigInt(value).toString();
|
||||
}
|
||||
|
||||
/** Sort/compare helper: negative if a < b, 0 if equal, positive if a > b. */
|
||||
export function compareCents(a, b) {
|
||||
const aa = toCentsBigInt(a);
|
||||
const bb = toCentsBigInt(b);
|
||||
if (aa < bb) return -1;
|
||||
if (aa > bb) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Exact credit display (cents → "1 234.56"), for tooltips / confirmations. */
|
||||
export function formatCreditsExact(cents) {
|
||||
const n = Number(cents);
|
||||
if (!Number.isFinite(n)) return '0.00';
|
||||
const fixed = (n / 100).toFixed(2);
|
||||
const neg = fixed.startsWith('-');
|
||||
const body = neg ? fixed.slice(1) : fixed;
|
||||
const [intPart, dec] = body.split('.');
|
||||
const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, '\u202f');
|
||||
return `${neg ? '-' : ''}${grouped}.${dec}`;
|
||||
let bi = toCentsBigInt(cents);
|
||||
const neg = bi < 0n;
|
||||
if (neg) bi = -bi;
|
||||
const whole = bi / 100n;
|
||||
const frac = (bi % 100n).toString().padStart(2, '0');
|
||||
const grouped = whole.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '\u202f');
|
||||
return `${neg ? '-' : ''}${grouped}.${frac}`;
|
||||
}
|
||||
|
||||
/** Parse credits input (decimal string) → integer cents string. Null if invalid. */
|
||||
export function creditsToCentsString(raw) {
|
||||
if (raw == null || raw === '') return null;
|
||||
const s = String(raw).trim().replace(',', '.');
|
||||
const m = s.match(/^(\d+)(?:\.(\d{1,2})\d*)?$/);
|
||||
if (!m) return null;
|
||||
try {
|
||||
const whole = BigInt(m[1]);
|
||||
const frac = m[2] ? m[2].padEnd(2, '0') : '00';
|
||||
return (whole * 100n + BigInt(frac)).toString();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Cents → credits decimal string without Number precision loss. */
|
||||
export function centsToCreditsString(cents) {
|
||||
let bi = toCentsBigInt(cents);
|
||||
const neg = bi < 0n;
|
||||
const abs = neg ? -bi : bi;
|
||||
const whole = abs / 100n;
|
||||
const frac = (abs % 100n).toString().padStart(2, '0');
|
||||
const body =
|
||||
frac === '00' ? whole.toString() : `${whole}.${frac}`.replace(/0+$/, '').replace(/\.$/, '');
|
||||
return `${neg ? '-' : ''}${body}`;
|
||||
}
|
||||
|
||||
/** Alphabet suffixes after T: aa, ab, … az, ba, … (each step ×1000). */
|
||||
@@ -225,25 +325,31 @@ function tierSuffix(tier) {
|
||||
* K / M / B / T, then aa, ab, … (×1000 each). Input is cents.
|
||||
*/
|
||||
export function formatCredits(cents) {
|
||||
const n = Number(cents);
|
||||
if (!Number.isFinite(n)) return '0.00';
|
||||
const neg = n < 0;
|
||||
const credits = Math.abs(n) / 100;
|
||||
let bi = toCentsBigInt(cents);
|
||||
const neg = bi < 0n;
|
||||
if (neg) bi = -bi;
|
||||
|
||||
if (credits < 1000) {
|
||||
return `${neg ? '-' : ''}${credits.toFixed(2)}`;
|
||||
const creditsWhole = bi / 100n;
|
||||
const creditsFrac = bi % 100n;
|
||||
|
||||
if (creditsWhole < 1000n) {
|
||||
return `${neg ? '-' : ''}${creditsWhole}.${creditsFrac.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
let tier = 0;
|
||||
let scaled = credits;
|
||||
while (scaled >= 1000 && tier < 1000) {
|
||||
scaled /= 1000;
|
||||
let scaled = creditsWhole;
|
||||
let rem = 0n;
|
||||
while (scaled >= 1000n) {
|
||||
rem = scaled % 1000n;
|
||||
scaled /= 1000n;
|
||||
tier += 1;
|
||||
}
|
||||
|
||||
const suffix = tierSuffix(tier);
|
||||
const shown = String(Number(scaled.toFixed(2)));
|
||||
return `${neg ? '-' : ''}${shown}${suffix}`;
|
||||
// scaled < 1000 → safe as Number; rem gives ~3 decimal digits of precision
|
||||
const approx = Number(scaled) + Number(rem) / 1000;
|
||||
if (!Number.isFinite(approx)) return `${neg ? '-' : ''}0${tierSuffix(tier)}`;
|
||||
const shown = String(Number(approx.toFixed(2)));
|
||||
return `${neg ? '-' : ''}${shown}${tierSuffix(tier)}`;
|
||||
}
|
||||
|
||||
export function formatChance(pct) {
|
||||
|
||||
Reference in New Issue
Block a user