Imagery imported for icons and items. Live updates working.

Hover tooltip working. Features.md added for better tracking.
This commit is contained in:
2025-10-28 08:41:04 +08:00
parent 4ea30cc12e
commit ea8484fca7
16068 changed files with 3097 additions and 6 deletions

View File

@@ -0,0 +1,147 @@
/**
* Transformers to convert Spring backend API responses into frontend data format
*/
import { SKILL_NAMES } from '../models/Skill';
/**
* Transform skills array from API (24 XP values) into object
* @param {Array<number>} skillsArray - Array of 24 XP values
* @returns {Object} Skills object with skill names as keys
*/
export function transformSkills(skillsArray) {
if (!skillsArray || !Array.isArray(skillsArray)) {
return {};
}
const skills = {};
SKILL_NAMES.forEach((skillName, index) => {
if (index < skillsArray.length) {
skills[skillName] = skillsArray[index];
}
});
// Add Overall (total XP)
skills.Overall = skillsArray.reduce((sum, xp) => sum + xp, 0);
return skills;
}
/**
* Transform stats array from API into structured object
* API format: [hp_current, hp_max, prayer_current, prayer_max, energy_current, energy_max, world]
* @param {Array<number>} statsArray - Array of 7 integers
* @returns {Object} Stats object
*/
export function transformStats(statsArray) {
if (!statsArray || !Array.isArray(statsArray) || statsArray.length < 7) {
return {
hitpoints: { current: 10, max: 10 },
prayer: { current: 1, max: 1 },
energy: { current: 10000, max: 10000 },
world: null,
};
}
return {
hitpoints: {
current: statsArray[0],
max: statsArray[1],
},
prayer: {
current: statsArray[2],
max: statsArray[3],
},
energy: {
current: statsArray[4],
max: statsArray[5],
},
world: statsArray[6] || null,
};
}
/**
* Transform inventory/equipment array from API into item objects
* API format: flat array alternating [id1, quantity1, id2, quantity2, ...]
* @param {Array<number>} itemsArray - Flat array of item data
* @returns {Array<Object>} Array of {id, quantity} objects
*/
export function transformItems(itemsArray) {
if (!itemsArray || !Array.isArray(itemsArray)) {
return [];
}
const items = [];
for (let i = 0; i < itemsArray.length; i += 2) {
items.push({
id: itemsArray[i] || 0,
quantity: itemsArray[i + 1] || 0,
});
}
return items;
}
/**
* Transform coordinates array from API
* API format: [x, y, plane]
* @param {Array<number>} coordsArray - Array of 3 integers
* @returns {Object|null} Coordinates object or null
*/
export function transformCoordinates(coordsArray) {
if (!coordsArray || !Array.isArray(coordsArray) || coordsArray.length < 3) {
return null;
}
return {
x: coordsArray[0],
y: coordsArray[1],
plane: coordsArray[2],
};
}
/**
* Transform full member data from API response
* @param {Object} memberData - Raw member data from API
* @returns {Object} Transformed member data
*/
export function transformMemberData(memberData) {
if (!memberData) {
return null;
}
return {
name: memberData.name,
skills: transformSkills(memberData.skills),
stats: transformStats(memberData.stats),
inventory: transformItems(memberData.inventory),
equipment: transformItems(memberData.equipment),
bank: transformItems(memberData.bank),
runePouch: transformItems(memberData.rune_pouch),
seedVault: transformItems(memberData.seed_vault),
coordinates: transformCoordinates(memberData.coordinates),
interacting: memberData.interacting,
last_updated: memberData.last_updated,
};
}
/**
* Transform array of members from API response
* @param {Array<Object>} membersArray - Array of member data from API
* @returns {Array<Object>} Array of transformed member data
*/
export function transformMembersData(membersArray) {
// eslint-disable-next-line no-console
console.log('transformMembersData - input:', membersArray);
if (!Array.isArray(membersArray)) {
// eslint-disable-next-line no-console
console.warn('transformMembersData - not an array!');
return [];
}
const transformed = membersArray.map(transformMemberData).filter(Boolean);
// eslint-disable-next-line no-console
console.log('transformMembersData - output:', transformed);
return transformed;
}

View File

@@ -0,0 +1,82 @@
/**
* Formatting utilities for numbers, quantities, and time
*/
/**
* Format large quantities into short strings (1.5K, 2.3M, 1B)
* @param {number} quantity - The quantity to format
* @returns {string} Formatted string
*/
export function formatShortQuantity(quantity) {
if (quantity < 1000) {
return quantity.toString();
}
if (quantity < 1000000) {
const k = Math.floor(quantity / 100) / 10;
return `${k}K`;
}
if (quantity < 1000000000) {
const m = Math.floor(quantity / 100000) / 10;
return `${m}M`;
}
const b = Math.floor(quantity / 100000000) / 10;
return `${b}B`;
}
/**
* Format very large quantities into very short strings (1K, 2M, 1B)
* @param {number} quantity - The quantity to format
* @returns {string} Formatted string
*/
export function formatVeryShortQuantity(quantity) {
if (quantity < 1000) {
return quantity.toString();
}
if (quantity < 1000000) {
return `${Math.floor(quantity / 1000)}K`;
}
if (quantity < 1000000000) {
return `${Math.floor(quantity / 1000000)}M`;
}
return `${Math.floor(quantity / 1000000000)}B`;
}
/**
* Format time since last update
* @param {Date|string|number} lastUpdated - Last update timestamp
* @returns {string} Formatted string like "2m ago" or "1h ago"
*/
export function timeSinceLastUpdate(lastUpdated) {
const date = lastUpdated instanceof Date ? lastUpdated : new Date(lastUpdated);
const now = new Date();
const secondsAgo = Math.floor((now - date) / 1000);
if (secondsAgo < 60) {
return `${secondsAgo}s ago`;
}
if (secondsAgo < 3600) {
return `${Math.floor(secondsAgo / 60)}m ago`;
}
if (secondsAgo < 86400) {
return `${Math.floor(secondsAgo / 3600)}h ago`;
}
return `${Math.floor(secondsAgo / 86400)}d ago`;
}
/**
* Remove articles from string
* @param {string} str - String to process
* @returns {string} String with articles removed
*/
export function removeArticles(str) {
return str.replace(/^(a|an|the)\s+/i, '');
}
/**
* Format number with commas
* @param {number} num - Number to format
* @returns {string} Formatted number
*/
export function formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}