client/addons/admin/ui/_site/script.js
Jacob Schmidt 90476345db feat: Enhance admin and bank systems with event handling and UI improvements
This commit introduces significant updates to the admin and bank systems, focusing on improved event handling and user interface enhancements. Key changes include:

- Refactored event handling for player data requests, paygrade updates, and message broadcasting in the admin panel.
- Implemented new event types for handling player funds and transaction history in the bank system.
- Updated JavaScript functions for better interaction with the web-based UI, including dynamic data requests and improved user feedback.
- Removed deprecated functions and streamlined code for better maintainability.

These enhancements aim to provide a more efficient and user-friendly experience for administrators and players alike.
2025-05-10 17:50:11 -05:00

488 lines
15 KiB
JavaScript

/**
* Admin Panel Management Script
* This script handles the admin panel functionality for the Arma 3 game interface.
* It provides player management, money operations, messaging, and other admin functions.
*/
//=============================================================================
// #region DATA STRUCTURES AND VARIABLES
//=============================================================================
/**
* Admin data structure - will be populated from the game
* Contains player information and payday amount configuration
*/
let adminData = {
players: [], // List of all players with their details
paydayAmounts: {} // Map of paygrade to bonus amount
};
/**
* Currently selected player ID for operations that require a player selection
* @type {string|null}
*/
let selectedPlayerId = null;
// #endregion
//=============================================================================
// #region INITIALIZATION AND DATA REQUESTS
//=============================================================================
/**
* Initialize the admin panel
* Sets up the UI, requests initial data from the game engine
*/
function initializeAdmin() {
updateStats();
setupFilterListeners();
requestPlayerData();
requestPaygradeData();
}
/**
* Request player data from the game engine
* Sends an event to fetch current player information
*/
function requestPlayerData() {
const message = {
event: "REQUEST::PLAYER::DATA",
data: {}
};
// Send request to the game engine
A3API.SendAlert(JSON.stringify(message));
}
/**
* Request paygrade data from the game engine
* Sends an event to fetch current paygrade configuration
*/
function requestPaygradeData() {
const message = {
event: "REQUEST::PAYGRADE::DATA",
data: {}
};
// Send request to the game engine
A3API.SendAlert(JSON.stringify(message));
}
/**
* Set up a timer to periodically refresh player data
* Ensures the admin panel shows up-to-date information
*/
function setupRefreshTimer() {
setInterval(requestPlayerData, 30000); // Refresh every 30 seconds
}
// #endregion
//=============================================================================
// #region DATA HANDLERS
//=============================================================================
/**
* Handle paygrade data received from the game engine
* Processes the paygrade list and updates the UI accordingly
*
* @param {Array} paygradeList - List of paygrade objects with paygrade and bonus properties
*/
function handlePaygradeDataRequest(paygradeList) {
try {
// Convert the paygrade list to a map for easier lookup
const paygradeMap = {};
paygradeList.forEach(item => {
paygradeMap[item.paygrade] = item.bonus;
});
adminData.paydayAmounts = paygradeMap;
// Update the player list if we already have player data
if (adminData.players.length > 0) {
updatePlayerList();
}
console.log("Paygrade data updated successfully");
} catch (error) {
console.error("Error updating paygrade data:", error);
}
}
/**
* Handle player data received from the game engine
* Updates the admin panel with current player information
*
* @param {Array} playerList - List of player objects with their details
*/
function handlePlayerDataRequest(playerList) {
adminData.players = playerList;
updateStats();
updatePlayerList();
}
// #endregion
//=============================================================================
// #region UI UPDATES AND DISPLAY
//=============================================================================
/**
* Update header statistics
* Shows counts of online players and staff
*/
function updateStats() {
const onlinePlayers = adminData.players.length;
const onlineStaff = adminData.players.filter(p => p.paygrade !== "E1").length;
document.getElementById('playerCount').textContent = onlinePlayers;
document.getElementById('staffCount').textContent = onlineStaff;
}
/**
* Set up filter button listeners
* Configures the filter buttons and search functionality
*/
function setupFilterListeners() {
const filterButtons = document.querySelectorAll('.filter-btn');
// Set up filter button click handlers
filterButtons.forEach(button => {
button.addEventListener('click', () => {
filterButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
filterPlayers(button.dataset.filter);
});
});
// Set up search functionality
const searchInput = document.getElementById('playerSearch');
searchInput.addEventListener('input', () => {
const activeFilter = document.querySelector('.filter-btn.active').dataset.filter;
filterPlayers(activeFilter, searchInput.value);
});
}
/**
* Filter players based on category and search term
*
* @param {string} filter - The filter category (all, staff, blufor, etc.)
* @param {string} searchTerm - Optional search term to filter by name
*/
function filterPlayers(filter, searchTerm = '') {
let filteredPlayers = adminData.players;
// Apply category filter
if (filter === 'staff') {
filteredPlayers = filteredPlayers.filter(p => p.paygrade !== "E1");
} else if (filter === 'blufor') {
filteredPlayers = filteredPlayers.filter(p => p.side === "WEST");
} else if (filter === 'opfor') {
filteredPlayers = filteredPlayers.filter(p => p.side === "EAST");
} else if (filter === 'independent') {
filteredPlayers = filteredPlayers.filter(p => p.side === "GUER");
} else if (filter === 'civilian') {
filteredPlayers = filteredPlayers.filter(p => p.side === "CIV");
}
// Apply search filter
if (searchTerm) {
const term = searchTerm.toLowerCase();
filteredPlayers = filteredPlayers.filter(p =>
p.name.toLowerCase().includes(term)
);
}
updatePlayerList(filteredPlayers);
}
/**
* Update the player list display
* Renders the filtered player list with all relevant information
*
* @param {Array} players - List of player objects to display, defaults to all players
*/
function updatePlayerList(players = adminData.players) {
const playerList = document.getElementById('playerList');
playerList.innerHTML = players.map(player => {
const paydayAmount = adminData.paydayAmounts[player.paygrade] || 1000;
const rankClass = getRankClass(player.paygrade);
return `
<div class="player-item" data-id="${player.uid}">
<div class="player-info">
<span class="player-name">${player.name}</span>
<span class="player-rank rank-${rankClass}">${player.paygrade}</span>
<span class="player-money">${parseInt(player.funds).toLocaleString()}</span>
<span class="player-payday">Payday: ${paydayAmount.toLocaleString()}</span>
<span class="player-side side-${player.side.toLowerCase()}">${player.side}</span>
</div>
<div class="player-actions">
<button class="action-btn promote-btn" onclick="updatePaygrade('${player.uid}', true)">
Promote
</button>
<button class="action-btn demote-btn" onclick="updatePaygrade('${player.uid}', false)">
Demote
</button>
<button class="action-btn message-btn" onclick="openMessageModal('${player.uid}', '${player.name}')">Message</button>
<button class="action-btn" onclick="openMoneyModal('${player.uid}')">Modify Money</button>
</div>
</div>
`}).join('');
}
/**
* Helper function to determine rank class based on paygrade
* Used for styling different ranks with appropriate CSS classes
*
* @param {string} paygrade - The player's paygrade code
* @returns {string} CSS class name for the rank
*/
function getRankClass(paygrade) {
if (paygrade.startsWith('E')) {
return 'enlisted';
} else if (paygrade.startsWith('WO')) {
return 'warrant';
} else if (paygrade.startsWith('O') ||
paygrade.startsWith('1') ||
paygrade.startsWith('2') ||
paygrade.startsWith('C') ||
paygrade.startsWith('M')) {
return 'officer';
} else {
return 'enlisted'; // Default
}
}
// #endregion
//=============================================================================
// #region RANK MANAGEMENT
//=============================================================================
/**
* Update a player's paygrade (promote or demote)
*
* @param {string} uid - Player's unique identifier
* @param {boolean} isPromotion - True for promotion, false for demotion
*/
function updatePaygrade(uid, isPromotion) {
const player = adminData.players.find(p => p.uid === uid);
if (!player) return;
// Use the paygrades from the configuration
const paygrades = Object.keys(adminData.paydayAmounts);
paygrades.sort((a, b) => adminData.paydayAmounts[a] - adminData.paydayAmounts[b]); // Sort by payment amount
const currentIndex = paygrades.indexOf(player.paygrade);
let newPaygrade;
if (isPromotion && currentIndex < paygrades.length - 1) {
newPaygrade = paygrades[currentIndex + 1];
} else if (!isPromotion && currentIndex > 0) {
newPaygrade = paygrades[currentIndex - 1];
} else {
return; // Can't promote/demote further
}
const message = {
event: "UPDATE::PAYGRADE",
data: [uid, newPaygrade]
};
A3API.SendAlert(JSON.stringify(message));
// Optimistic update
player.paygrade = newPaygrade;
updatePlayerList();
}
// #endregion
//=============================================================================
// #region MONEY MANAGEMENT
//=============================================================================
/**
* Open the money modification modal for a player
*
* @param {string} uid - Player's unique identifier
*/
function openMoneyModal(uid) {
selectedPlayerId = uid;
const modal = document.getElementById('moneyModal');
modal.style.display = 'block';
}
/**
* Close the money modification modal
*/
function closeMoneyModal() {
const modal = document.getElementById('moneyModal');
modal.style.display = 'none';
document.getElementById('moneyAmount').value = '';
selectedPlayerId = null;
}
/**
* Give money to the selected player
*/
function giveMoney() {
const amount = parseInt(document.getElementById('moneyAmount').value);
if (amount && selectedPlayerId) {
handleTransferFunds("advance", amount, selectedPlayerId);
closeMoneyModal();
}
}
/**
* Give money to all players
*/
function giveAllMoney() {
const amount = parseInt(document.getElementById('giveAllAmount').value);
const message = {
event: "ADVANCE::ALL",
data: [amount]
}
A3API.SendAlert(JSON.stringify(message));
// Request updated player data after giving money to all players
setTimeout(requestPlayerData, 500); // Short delay to allow server processing
}
/**
* Take money from the selected player
*/
function takeMoney() {
const amount = parseInt(document.getElementById('moneyAmount').value);
if (amount && selectedPlayerId) {
handleTransferFunds("deduct", amount, selectedPlayerId);
closeMoneyModal();
}
}
/**
* Handle funds transfer for a player
*
* @param {string} condition - "advance" to give money, "deduct" to take money
* @param {number} amount - Amount of money to transfer
* @param {string} uid - Player's unique identifier
*/
function handleTransferFunds(condition, amount, uid) {
const message = {
event: "HANDLE::TRANSFER",
data: [condition, amount, uid]
};
A3API.SendAlert(JSON.stringify(message));
// Optimistic update
const player = adminData.players.find(p => p.uid === uid);
if (player) {
if (condition === "advance") {
player.funds = parseInt(player.funds) + amount;
} else if (condition === "deduct") {
player.funds = Math.max(0, parseInt(player.funds) - amount);
}
updatePlayerList();
}
}
// #endregion
//=============================================================================
// #region MESSAGE SYSTEM
//=============================================================================
/**
* Open the message modal for a player
*
* @param {string} uid - Player's unique identifier
* @param {string} playerName - Player's name for display
*/
function openMessageModal(uid, playerName) {
selectedPlayerId = uid;
const modal = document.getElementById('messageModal');
document.getElementById('messagePlayerName').textContent = playerName;
modal.style.display = 'block';
}
/**
* Close the message modal
*/
function closeMessageModal() {
const modal = document.getElementById('messageModal');
modal.style.display = 'none';
document.getElementById('messageInput').value = '';
selectedPlayerId = null;
}
/**
* Send a message to the selected player
*/
function sendPlayerMessage() {
const message = document.getElementById('messageInput').value;
if (message && selectedPlayerId) {
const messageData = {
event: "SEND::MESSAGE",
data: [selectedPlayerId, message]
};
A3API.SendAlert(JSON.stringify(messageData));
closeMessageModal();
}
}
/**
* Broadcast a message to all players
*/
function broadcastMessage() {
const message = document.getElementById('broadcastMessage').value;
if (message) {
const messageData = {
event: "BROADCAST::MESSAGE",
data: ["", message]
};
A3API.SendAlert(JSON.stringify(messageData));
document.getElementById('broadcastMessage').value = '';
}
}
// #endregion
//=============================================================================
// #region GLOBAL ACTIONS
//=============================================================================
/**
* Trigger a payday for all players
*/
function Payday() {
const message = {
event: "HANDLE::PAYDAY",
data: []
};
A3API.SendAlert(JSON.stringify(message));
// Request updated player data after payday
setTimeout(requestPlayerData, 500); // Short delay to allow server processing
}
// #endregion
//=============================================================================
// #region EVENT LISTENERS
//=============================================================================
/**
* Initialize when DOM is loaded
*/
document.addEventListener('DOMContentLoaded', () => {
initializeAdmin();
setupRefreshTimer();
});
// #endregion