fix: billing cleanup, PayPal guard, docs nav prefix, coupon dark theme, XML guide link
Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/52e5015e-f5cf-42e2-bc32-b1c77193a13f Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
a45d102845
commit
9944b59332
6 changed files with 158 additions and 37 deletions
|
|
@ -55,8 +55,6 @@ function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
|||
<a class="gsw-btn" href="admin_payments.php">Transaction Log</a>
|
||||
<a class="gsw-btn" href="admin_coupons.php">Manage Coupons</a>
|
||||
<a class="gsw-btn" href="admin_config.php">Edit Site Config</a>
|
||||
<a class="gsw-btn" href="admin_xml_editor.php">XML Config Editor</a>
|
||||
<a class="gsw-btn" href="docs/xml_notes.php">XML Config Guide</a>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
|
|
|||
|
|
@ -160,29 +160,120 @@ $coupons_result = mysqli_query($db, "SELECT * FROM {$table_prefix}billing_coupon
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/header.css">
|
||||
<style>
|
||||
.coupon-form { background: #f5f5f5; padding: 20px; margin: 20px 0; border-radius: 5px; }
|
||||
/* Coupon admin — dark-theme overrides */
|
||||
.coupon-form {
|
||||
background: rgba(0,0,0,0.35);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.form-group { margin-bottom: 15px; }
|
||||
.form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
|
||||
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 8px; box-sizing: border-box; }
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
color: #e8e8e8;
|
||||
}
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 9px 10px;
|
||||
box-sizing: border-box;
|
||||
background: #11141f;
|
||||
color: #f0f0f0;
|
||||
border: 1px solid rgba(255,255,255,0.18);
|
||||
border-radius: 5px;
|
||||
font-size: 0.97rem;
|
||||
}
|
||||
.form-group input::placeholder,
|
||||
.form-group textarea::placeholder {
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
.form-group input:focus,
|
||||
.form-group select:focus,
|
||||
.form-group textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 2px rgba(102,126,234,0.25);
|
||||
}
|
||||
.form-group textarea { min-height: 60px; }
|
||||
.game-checkboxes { max-height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background: white; }
|
||||
.game-checkboxes label { display: block; margin: 5px 0; font-weight: normal; }
|
||||
.coupon-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
||||
.coupon-table th, .coupon-table td { border: 1px solid #ddd; padding: 10px; text-align: left; }
|
||||
.coupon-table th { background: #4CAF50; color: white; }
|
||||
.coupon-table tr:nth-child(even) { background: #f9f9f9; }
|
||||
.btn { padding: 8px 16px; margin: 2px; cursor: pointer; border: none; border-radius: 3px; }
|
||||
.btn-primary { background: #4CAF50; color: white; }
|
||||
.btn-warning { background: #ff9800; color: white; }
|
||||
.btn-danger { background: #f44336; color: white; }
|
||||
.status { padding: 10px; margin: 10px 0; border-radius: 3px; }
|
||||
.status.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
||||
.status.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
||||
.badge { padding: 3px 8px; border-radius: 3px; font-size: 0.85em; }
|
||||
.badge-active { background: #28a745; color: white; }
|
||||
.badge-inactive { background: #6c757d; color: white; }
|
||||
.badge-onetime { background: #17a2b8; color: white; }
|
||||
.badge-permanent { background: #ffc107; color: black; }
|
||||
.game-checkboxes {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
padding: 10px;
|
||||
background: rgba(0,0,0,0.4);
|
||||
border-radius: 5px;
|
||||
}
|
||||
.game-checkboxes label {
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
font-weight: normal;
|
||||
color: #d0d0d0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.game-checkboxes input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin-right: 6px;
|
||||
background: #11141f;
|
||||
border: 1px solid rgba(255,255,255,0.25);
|
||||
}
|
||||
.coupon-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.coupon-table th,
|
||||
.coupon-table td {
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
padding: 10px 12px;
|
||||
text-align: left;
|
||||
color: #e8e8e8;
|
||||
}
|
||||
.coupon-table th { background: rgba(76,175,80,0.25); color: #fff; }
|
||||
.coupon-table tr:nth-child(even) td { background: rgba(255,255,255,0.03); }
|
||||
.coupon-table tr:hover td { background: rgba(255,255,255,0.06); }
|
||||
.btn { padding: 8px 16px; margin: 2px; cursor: pointer; border: none; border-radius: 4px; font-weight: 600; }
|
||||
.btn-primary { background: linear-gradient(135deg,#667eea,#764ba2); color: #fff; }
|
||||
.btn-warning { background: #ff9800; color: #fff; }
|
||||
.btn-danger { background: #f44336; color: #fff; }
|
||||
.status { padding: 10px 14px; margin: 10px 0; border-radius: 5px; }
|
||||
.status.success { background: rgba(40,167,69,0.2); color: #8dffb0; border: 1px solid rgba(40,167,69,0.35); }
|
||||
.status.error { background: rgba(220,53,69,0.2); color: #ffb3b8; border: 1px solid rgba(220,53,69,0.35); }
|
||||
.badge { padding: 3px 8px; border-radius: 3px; font-size: 0.85em; font-weight: 600; }
|
||||
.badge-active { background: #28a745; color: #fff; }
|
||||
.badge-inactive { background: #6c757d; color: #fff; }
|
||||
.badge-onetime { background: #17a2b8; color: #fff; }
|
||||
.badge-permanent { background: #ffc107; color: #000; }
|
||||
|
||||
/* Inline select/option elements inside table rows */
|
||||
.coupon-table select { background: #11141f; color: #f0f0f0; border: 1px solid rgba(255,255,255,0.18); border-radius: 4px; padding: 4px 6px; }
|
||||
|
||||
/* Mobile: stack table cells */
|
||||
@media (max-width: 768px) {
|
||||
.coupon-table, .coupon-table thead, .coupon-table tbody,
|
||||
.coupon-table th, .coupon-table td, .coupon-table tr { display: block; }
|
||||
.coupon-table thead tr { display: none; }
|
||||
.coupon-table td {
|
||||
position: relative;
|
||||
padding-left: 45%;
|
||||
border: none;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.07);
|
||||
}
|
||||
.coupon-table td::before {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
width: 40%;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
color: rgba(255,255,255,0.55);
|
||||
font-size: 0.82rem;
|
||||
content: attr(data-label);
|
||||
}
|
||||
.coupon-table tr { border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; margin-bottom: 12px; }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function toggleGameFilter(selectEl) {
|
||||
|
|
@ -313,35 +404,35 @@ include(__DIR__ . '/includes/menu.php');
|
|||
?>
|
||||
<!-- View Row -->
|
||||
<tr id="view-row-<?php echo $coupon['coupon_id']; ?>">
|
||||
<td><strong><?php echo h($coupon['code']); ?></strong></td>
|
||||
<td><?php echo h($coupon['name']); ?></td>
|
||||
<td><?php echo h($coupon['discount_percent']); ?>%</td>
|
||||
<td>
|
||||
<td data-label="Code"><strong><?php echo h($coupon['code']); ?></strong></td>
|
||||
<td data-label="Name"><?php echo h($coupon['name']); ?></td>
|
||||
<td data-label="Discount"><?php echo h($coupon['discount_percent']); ?>%</td>
|
||||
<td data-label="Type">
|
||||
<span class="badge badge-<?php echo $coupon['usage_type'] === 'permanent' ? 'permanent' : 'onetime'; ?>">
|
||||
<?php echo h(ucfirst(str_replace('_', ' ', $coupon['usage_type']))); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<td data-label="Games">
|
||||
<?php if ($coupon['game_filter_type'] === 'all_games'): ?>
|
||||
All Games
|
||||
<?php else: ?>
|
||||
<?php echo count((array)$games_filtered); ?> specific games
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<td data-label="Uses">
|
||||
<?php if ($coupon['max_uses']): ?>
|
||||
<?php echo h($coupon['current_uses']); ?> / <?php echo h($coupon['max_uses']); ?>
|
||||
<?php else: ?>
|
||||
<?php echo h($coupon['current_uses']); ?> (unlimited)
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo $coupon['expires'] ? h($coupon['expires']) : 'Never'; ?></td>
|
||||
<td>
|
||||
<td data-label="Expires"><?php echo $coupon['expires'] ? h($coupon['expires']) : 'Never'; ?></td>
|
||||
<td data-label="Status">
|
||||
<span class="badge badge-<?php echo $coupon['is_active'] ? 'active' : 'inactive'; ?>">
|
||||
<?php echo $coupon['is_active'] ? 'Active' : 'Inactive'; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<td data-label="Actions">
|
||||
<button onclick="editCoupon(<?php echo $coupon['coupon_id']; ?>)" class="btn btn-warning">Edit</button>
|
||||
<form method="POST" style="display:inline;" onsubmit="return confirm('Delete this coupon?');">
|
||||
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
|
||||
|
|
|
|||
|
|
@ -509,7 +509,7 @@ $siteBase = $protocol . $host;
|
|||
<!-- Favicon -->
|
||||
<link rel="icon" href="images/logo-sm.png" type="image/png">
|
||||
<link rel="apple-touch-icon" href="images/logo-sm.png">
|
||||
<?php if (!$cart_empty): ?>
|
||||
<?php if (!$cart_empty && !empty($client_id)): ?>
|
||||
<script src="https://www.paypal.com/sdk/js?client-id=<?php echo htmlspecialchars($client_id); ?>¤cy=USD&intent=capture"></script>
|
||||
<?php endif; ?>
|
||||
</head>
|
||||
|
|
@ -643,11 +643,30 @@ $siteBase = $protocol . $host;
|
|||
<?php else: ?>
|
||||
<div class="checkout-section">
|
||||
<h3>Checkout with PayPal</h3>
|
||||
<?php if (empty($client_id)): ?>
|
||||
<div class="alert alert-error">
|
||||
<strong>Checkout Unavailable:</strong> PayPal has not been configured for this site.
|
||||
Please contact the site administrator or try again later.
|
||||
<?php
|
||||
// Admin hint: only show config link if the current user is an admin
|
||||
$cart_user_id_check = intval($_SESSION['website_user_id'] ?? 0);
|
||||
$cart_is_admin = false;
|
||||
if ($cart_user_id_check > 0 && $db) {
|
||||
$ar = mysqli_query($db, "SELECT users_role FROM {$table_prefix}users WHERE user_id = " . $cart_user_id_check . " LIMIT 1");
|
||||
if ($ar && ($arow = mysqli_fetch_assoc($ar))) {
|
||||
$cart_is_admin = strtolower($arow['users_role'] ?? '') === 'admin';
|
||||
}
|
||||
}
|
||||
if ($cart_is_admin):
|
||||
?>
|
||||
<br><small><em>Admin: set <code>$paypal_client_id</code> in <a href="/admin_config.php" style="color:inherit;text-decoration:underline;">Site Config</a>.</em></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p>Click the button below to complete your purchase securely through PayPal.</p>
|
||||
|
||||
<div id="paypal-button-container"></div>
|
||||
<div id="status-message" class="status-message"></div>
|
||||
|
||||
<?php endif; ?>
|
||||
<div class="action-buttons">
|
||||
<a href="/order.php" class="btn btn-secondary">Continue Shopping</a>
|
||||
<a href="/my_account.php" class="btn btn-secondary">My Account</a>
|
||||
|
|
@ -665,7 +684,7 @@ $siteBase = $protocol . $host;
|
|||
}
|
||||
</script>
|
||||
|
||||
<?php if ($final_amount > 0.00): ?>
|
||||
<?php if ($final_amount > 0.00 && !empty($client_id)): ?>
|
||||
<script>
|
||||
paypal.Buttons({
|
||||
createOrder: function(data, actions) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ $nav_prefix = '';
|
|||
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
|
||||
if (is_string($scriptName) && $scriptName !== '') {
|
||||
if (preg_match('#/modules/billing/(.*)$#', $scriptName, $match)) {
|
||||
// Panel-embedded or non-root deployment: depth relative to modules/billing/
|
||||
$subPath = $match[1];
|
||||
if ($subPath !== '') {
|
||||
$depth = substr_count($subPath, '/');
|
||||
|
|
@ -23,6 +24,17 @@ if (is_string($scriptName) && $scriptName !== '') {
|
|||
$nav_prefix = str_repeat('../', $depth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Root deployment: compute prefix from the script's directory depth so that
|
||||
// links such as index.php correctly resolve to /index.php even when the
|
||||
// current page lives in a subdirectory like /docs/.
|
||||
$dir = dirname($scriptName);
|
||||
if ($dir !== '/' && $dir !== '' && $dir !== '.') {
|
||||
$segments = array_filter(explode('/', $dir), static function ($s) { return $s !== ''; });
|
||||
if (!empty($segments)) {
|
||||
$nav_prefix = str_repeat('../', count($segments));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$nav_prefix = $nav_prefix ?: '';
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Last Updated at 5:10pm on 2026-05-04
|
||||
Last Updated at 3:18pm on 2026-05-06
|
||||
|
|
|
|||
|
|
@ -550,6 +550,7 @@ function exec_ogp_module() {
|
|||
$game_cfgs = $db->getGameCfgs();
|
||||
echo "<h2>".get_lang('game_config_setup')."</h2>\n
|
||||
<p>".get_lang_f("modify_configs_info",SERVER_CONFIG_LOCATION)."</p>\n
|
||||
<p><a href='https://gameservers.world/docs/xml_notes.php' target='_blank' rel='noopener noreferrer' class='xml-jump-link'>📖 XML Config Reference Guide</a></p>\n
|
||||
<form action='?m=config_games' method='post'>\n
|
||||
<p><input id='reset_old_configs' type='checkbox' name='clear_old' value='yes' /><label for='reset_old_configs'>".get_lang('reset_old_configs')."</label></p>\n
|
||||
<p class='note'>".get_lang('note').": ".get_lang('config_reset_warning')."</p>\n
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue