Panel/modules/billing/adminserverlist.php
copilot-swe-agent[bot] b3d677035b
feat: update wording (GitHub Stable/Unstable) + billing matrix redesign
Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/4a9c8aab-3782-44a8-a5e4-01b50a813cc0

Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
2026-05-02 14:50:25 +00:00

289 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Server / Game Matrix - GameServers.World</title>
<style>
.matrix-table { border-collapse: collapse; width: 100%; }
.matrix-table th, .matrix-table td { border: 1px solid #ddd; padding: 6px 8px; vertical-align: middle; text-align: center; }
.matrix-table th { background: #f5f5f5; white-space: nowrap; }
.matrix-table td.game-name { text-align: left; white-space: nowrap; }
.override-input { width: 72px; margin-top: 4px; }
.muted { color: #999; font-size: 0.85em; }
.flash-ok { background: #d4edda; border: 1px solid #c3e6cb; padding: 8px 12px; margin-bottom: 10px; border-radius: 4px; }
.flash-err { background: #f8d7da; border: 1px solid #f5c6cb; padding: 8px 12px; margin-bottom: 10px; border-radius: 4px; }
</style>
</head>
<body>
<?php
// Admin matrix page: game × server availability + price overrides
require_once(__DIR__ . '/bootstrap.php');
require_once(__DIR__ . '/includes/admin_auth.php');
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
$db = mysqli_connect($db_host, $db_user, $db_pass, $db_name, isset($db_port) ? (int)$db_port : null);
if (!$db) {
die("Connection failed: " . mysqli_connect_error());
}
include(__DIR__ . '/includes/top.php');
include(__DIR__ . '/includes/menu.php');
// Ensure the mapping table exists with the override_price column
$db->query(
"CREATE TABLE IF NOT EXISTS `{$table_prefix}billing_service_remote_servers` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`service_id` INT(11) NOT NULL,
`remote_server_id` INT(11) NOT NULL,
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
`override_price` DECIMAL(10,2) NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `svc_rs` (`service_id`, `remote_server_id`),
KEY `service_id` (`service_id`),
KEY `remote_server_id` (`remote_server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
// Add override_price if this is an older install that already has the table without it
$chk = $db->query("SHOW COLUMNS FROM `{$table_prefix}billing_service_remote_servers` LIKE 'override_price'");
if ($chk && $chk->num_rows === 0) {
$db->query("ALTER TABLE `{$table_prefix}billing_service_remote_servers` ADD COLUMN `override_price` DECIMAL(10,2) NULL");
}
$flash = [];
$flashType = 'ok';
/* -----------------------------------------------------------------------
SAVE: matrix form submitted
----------------------------------------------------------------------- */
if (isset($_POST['save_matrix'])) {
$postedServices = $_POST['svc'] ?? [];
$postedMappings = $_POST['map'] ?? [];
foreach ((array)$postedServices as $sid => $svcData) {
$sid = (int)$sid;
$enabled = isset($svcData['enabled']) ? 1 : 0;
$base_price = number_format((float)($svcData['base_price'] ?? 0), 2, '.', '');
$period = in_array($svcData['period'] ?? 'monthly', ['daily','monthly','yearly'], true)
? $svcData['period'] : 'monthly';
$price_col = $period === 'daily' ? 'price_daily' : ($period === 'yearly' ? 'price_year' : 'price_monthly');
$base_esc = $db->real_escape_string($base_price);
$period_esc = $db->real_escape_string($period);
$db->query(
"UPDATE `{$table_prefix}billing_services`
SET enabled = {$enabled},
`{$price_col}` = '{$base_esc}'
WHERE service_id = {$sid}"
);
}
// Upsert mappings: for every service x server pair post data received
$allServerIds = [];
$rsRes = $db->query("SELECT remote_server_id FROM `{$table_prefix}remote_servers`");
while ($rsRes && ($rsRow = $rsRes->fetch_assoc())) {
$allServerIds[] = (int)$rsRow['remote_server_id'];
}
foreach ((array)$postedServices as $sid => $ignored) {
$sid = (int)$sid;
foreach ($allServerIds as $rid) {
$mapEnabled = isset($postedMappings[$sid][$rid]['enabled']) ? 1 : 0;
$ovRaw = $postedMappings[$sid][$rid]['override_price'] ?? '';
$override = (trim($ovRaw) === '') ? 'NULL' : "'" . $db->real_escape_string(number_format((float)$ovRaw, 2, '.', '')) . "'";
$db->query(
"INSERT INTO `{$table_prefix}billing_service_remote_servers`
(service_id, remote_server_id, enabled, override_price)
VALUES ({$sid}, {$rid}, {$mapEnabled}, {$override})
ON DUPLICATE KEY UPDATE
enabled = VALUES(enabled),
override_price = VALUES(override_price)"
);
}
}
$flash[] = "Matrix saved successfully.";
}
/* -----------------------------------------------------------------------
Remove a service
----------------------------------------------------------------------- */
if (isset($_POST['remove_service'], $_POST['service_id_remove'])) {
$sid = (int)$_POST['service_id_remove'];
$db->query("DELETE FROM `{$table_prefix}billing_service_remote_servers` WHERE service_id = {$sid}");
$db->query("DELETE FROM `{$table_prefix}billing_services` WHERE service_id = {$sid}");
$flash[] = "Service #{$sid} removed.";
}
/* -----------------------------------------------------------------------
Load data
----------------------------------------------------------------------- */
$remoteServers = [];
$rsRes = $db->query("SELECT remote_server_id, remote_server_name FROM `{$table_prefix}remote_servers` ORDER BY remote_server_name");
while ($rsRes && ($row = $rsRes->fetch_assoc())) {
$remoteServers[] = $row;
}
$services = [];
$svcRes = $db->query(
"SELECT service_id, service_name, enabled, price_daily, price_monthly, price_year
FROM `{$table_prefix}billing_services`
ORDER BY service_name"
);
while ($svcRes && ($row = $svcRes->fetch_assoc())) {
$services[] = $row;
}
// Load existing mappings into a lookup: $mappings[$service_id][$remote_server_id] = ['enabled'=>..,'override_price'=>..]
$mappings = [];
$mapRes = $db->query(
"SELECT service_id, remote_server_id, enabled, override_price
FROM `{$table_prefix}billing_service_remote_servers`"
);
while ($mapRes && ($row = $mapRes->fetch_assoc())) {
$mappings[(int)$row['service_id']][(int)$row['remote_server_id']] = [
'enabled' => (int)$row['enabled'],
'override_price' => $row['override_price'],
];
}
?>
<?php foreach ((array)$flash as $msg): ?>
<div class="flash-<?php echo $flashType; ?>"><?php echo h($msg); ?></div>
<?php endforeach; ?>
<h2>Game &times; Server Matrix</h2>
<p class="muted">
Enable or disable each game for billing, set its base price and billing period, then
toggle availability per server and optionally override the price for that location.
Leave override blank to use the base price.
</p>
<?php if (empty($services)): ?>
<p>No billing services found. Add services first via the database or the panel.</p>
<?php else: ?>
<form method="post" action="">
<input type="hidden" name="save_matrix" value="1">
<div style="overflow-x:auto;">
<table class="matrix-table">
<thead>
<tr>
<th class="game-name">Game</th>
<th>Enabled</th>
<th>Base Price ($)</th>
<th>Period</th>
<?php foreach ((array)$remoteServers as $rs): ?>
<th><?php echo h($rs['remote_server_name']); ?><br>
<span class="muted">#<?php echo (int)$rs['remote_server_id']; ?></span>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ((array)$services as $svc):
$sid = (int)$svc['service_id'];
$svcEnabled = (int)$svc['enabled'];
// Determine current base price and period from existing columns
if ((float)$svc['price_monthly'] > 0) {
$basePrice = number_format((float)$svc['price_monthly'], 2, '.', '');
$period = 'monthly';
} elseif ((float)$svc['price_daily'] > 0) {
$basePrice = number_format((float)$svc['price_daily'], 2, '.', '');
$period = 'daily';
} elseif ((float)$svc['price_year'] > 0) {
$basePrice = number_format((float)$svc['price_year'], 2, '.', '');
$period = 'yearly';
} else {
$basePrice = '0.00';
$period = 'monthly';
}
?>
<tr>
<td class="game-name">
<?php echo h($svc['service_name']); ?>
<div class="muted">ID: <?php echo $sid; ?></div>
</td>
<td>
<input type="hidden" name="svc[<?php echo $sid; ?>][enabled]" value="0">
<input type="checkbox" name="svc[<?php echo $sid; ?>][enabled]" value="1"
<?php echo $svcEnabled ? 'checked' : ''; ?>>
</td>
<td>
<input type="number" step="0.01" min="0"
name="svc[<?php echo $sid; ?>][base_price]"
value="<?php echo h($basePrice); ?>"
style="width:90px;">
</td>
<td>
<select name="svc[<?php echo $sid; ?>][period]">
<option value="monthly" <?php echo $period === 'monthly' ? 'selected' : ''; ?>>Monthly</option>
<option value="daily" <?php echo $period === 'daily' ? 'selected' : ''; ?>>Daily</option>
<option value="yearly" <?php echo $period === 'yearly' ? 'selected' : ''; ?>>Yearly</option>
</select>
</td>
<?php foreach ((array)$remoteServers as $rs):
$rid = (int)$rs['remote_server_id'];
$mapEntry = $mappings[$sid][$rid] ?? ['enabled' => 0, 'override_price' => null];
$mapEnabled = (int)$mapEntry['enabled'];
$ovPrice = $mapEntry['override_price'];
?>
<td>
<input type="hidden" name="map[<?php echo $sid; ?>][<?php echo $rid; ?>][enabled]" value="0">
<input type="checkbox" name="map[<?php echo $sid; ?>][<?php echo $rid; ?>][enabled]" value="1"
<?php echo $mapEnabled ? 'checked' : ''; ?>>
<br>
<input type="number" step="0.01" min="0" placeholder="override"
class="override-input"
name="map[<?php echo $sid; ?>][<?php echo $rid; ?>][override_price]"
value="<?php echo ($ovPrice !== null ? h(number_format((float)$ovPrice, 2, '.', '')) : ''); ?>">
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div style="margin-top:14px;">
<button type="submit">Save Matrix</button>
</div>
</form>
<h3 style="margin-top:28px;">Remove a Service</h3>
<form method="post" action="" style="display:flex;gap:8px;align-items:center;">
<input type="hidden" name="remove_service" value="1">
<select name="service_id_remove">
<?php foreach ((array)$services as $s): ?>
<option value="<?php echo (int)$s['service_id']; ?>">
<?php echo h($s['service_name']); ?> (ID: <?php echo (int)$s['service_id']; ?>)
</option>
<?php endforeach; ?>
</select>
<button type="submit" onclick="return confirm('Remove this service and all its server mappings? This cannot be undone.')">Remove</button>
</form>
<?php endif; ?>
<div class="panel" style="margin-top:20px;">
<p><strong>Legend:</strong> Checkbox = server is available for this game.
Override price = customer pays this amount instead of the base price for that location.
Leave override blank to use the game base price.</p>
<p class="muted">
Availability is controlled entirely by <code><?php echo h("{$table_prefix}billing_service_remote_servers"); ?></code>.
No entry or <code>enabled = 0</code> means the server is not offered for that game.
</p>
</div>
<?php billing_maybe_close_db($db); ?>
</body>
</html>