initial: bootstrap from BukidBountyApp base

This commit is contained in:
Jonathan Sykes
2026-06-06 18:43:00 +08:00
commit eb4a5731fb
5674 changed files with 160857 additions and 0 deletions

View File

@@ -0,0 +1,270 @@
{{-- My Subscription Page --}}
<div class="card" id="main-card">
<div class="card-header">
<h3 class="card-title">My Subscription</h3>
</div>
<div class="card-body">
{{-- Current Subscription Status --}}
<div id="subscription-status-section">
<div id="subscription-loading" class="text-center" style="padding: 20px;">
<i class="fas fa-spinner fa-spin fa-2x"></i>
</div>
{{-- Active subscription panel (shown when subscribed) --}}
<div id="subscription-active-panel" style="display:none;">
<div class="info-box mb-3">
<span class="info-box-icon bg-success"><i class="fas fa-check-circle"></i></span>
<div class="info-box-content">
<span class="info-box-text">Active Plan</span>
<span class="info-box-number" id="sub-plan-name"></span>
<div class="progress"><div class="progress-bar bg-success" id="sub-progress-bar"></div></div>
<span class="progress-description">
Expires: <strong id="sub-expires-at"></strong>
&nbsp;(<span id="sub-days-remaining"></span> days left)
</span>
</div>
</div>
{{-- Expiry action badge --}}
<div id="sub-expiry-action-notice" class="alert" style="display:none;"></div>
<button class="btn btn-outline-primary btn-sm" onclick="SubPage_loadPlans()">
<i class="fas fa-redo"></i> Renew / Upgrade
</button>
</div>
{{-- No subscription panel --}}
<div id="subscription-none-panel" style="display:none;">
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle"></i>
You do not have an active subscription.
</div>
</div>
</div>
<hr>
{{-- Wallet Balance --}}
<div class="row mb-3">
<div class="col-6">
<div class="small-box bg-info">
<div class="inner">
<h4 id="sub-wallet-balance">₱0.00</h4>
<p>Wallet Balance</p>
</div>
<div class="icon"><i class="fas fa-wallet"></i></div>
</div>
</div>
</div>
{{-- Available Plans --}}
<h5>Available Plans</h5>
<div id="subscription-plans-loading" class="text-center" style="padding: 10px; display:none;">
<i class="fas fa-spinner fa-spin"></i> Loading plans...
</div>
<div id="subscription-plans-container" class="row" style="display:none;"></div>
<hr>
{{-- Invoice History --}}
<h5>Invoice History</h5>
<div id="subscription-invoices-loading" class="text-center" style="padding:10px; display:none;">
<i class="fas fa-spinner fa-spin"></i> Loading invoices...
</div>
<div id="subscription-invoices-container">
<table class="table table-sm table-striped" id="subscription-invoices-table" style="display:none;">
<thead>
<tr>
<th>Date</th>
<th>Plan</th>
<th>Amount</th>
<th>Method</th>
<th>Status</th>
<th>Reference</th>
</tr>
</thead>
<tbody id="subscription-invoices-body"></tbody>
</table>
<div id="subscription-invoices-empty" class="text-muted" style="display:none;">No invoices yet.</div>
</div>
</div>
</div>
<script>
(function () {
// ── Bootstrap ────────────────────────────────────────────────────────
SubPage_loadStatus();
SubPage_loadInvoices();
// ── Load current subscription ────────────────────────────────────────
function SubPage_loadStatus() {
$.get('/subscription/my', function (data) {
$('#subscription-loading').hide();
const balance = parseFloat(data.balance || 0);
$('#sub-wallet-balance').text('₱' + balance.toFixed(2));
if (data.has_subscription && data.subscription) {
const sub = data.subscription;
const plan = sub.plan || {};
$('#sub-plan-name').text(plan.name || '—');
$('#sub-expires-at').text(sub.expires_at ? sub.expires_at.substring(0, 10) : '—');
$('#sub-days-remaining').text(sub.days_remaining ?? '—');
const pct = plan.duration_days
? Math.max(0, Math.min(100, Math.round((sub.days_remaining / plan.duration_days) * 100)))
: 0;
$('#sub-progress-bar').css('width', pct + '%');
// Expiry action notice
const notices = {
'restrict': { cls: 'alert-danger', msg: '<i class="fas fa-lock"></i> When your subscription expires, access to protected features will be restricted until you renew.' },
'warn': { cls: 'alert-warning', msg: '<i class="fas fa-bell"></i> You will receive a warning when your subscription expires.' },
'auto_deduct': { cls: 'alert-info', msg: '<i class="fas fa-sync"></i> Your subscription will automatically renew by deducting from your wallet balance.' },
};
const notice = notices[plan.expiry_action];
if (notice) {
$('#sub-expiry-action-notice')
.addClass(notice.cls)
.html(notice.msg)
.show();
}
$('#subscription-active-panel').show();
} else {
$('#subscription-none-panel').show();
SubPage_loadPlans();
}
}).fail(function () {
$('#subscription-loading').html('<span class="text-danger">Failed to load subscription status.</span>');
});
}
// ── Load available plans ─────────────────────────────────────────────
window.SubPage_loadPlans = function () {
$('#subscription-plans-loading').show();
$('#subscription-plans-container').hide().empty();
$.get('/subscription/plans', function (plans) {
$('#subscription-plans-loading').hide();
if (!plans || plans.length === 0) {
$('#subscription-plans-container').html('<p class="text-muted">No plans available.</p>').show();
return;
}
const balance = parseFloat($('#sub-wallet-balance').text().replace('₱', '')) || 0;
plans.forEach(function (plan) {
const canAfford = balance >= parseFloat(plan.price);
const card = `
<div class="col-md-4 mb-3">
<div class="card card-outline card-primary">
<div class="card-header text-center">
<h5 class="card-title">${escHtml(plan.name)}</h5>
</div>
<div class="card-body text-center">
<h3>₱${parseFloat(plan.price).toFixed(2)}</h3>
<p class="text-muted">${parseInt(plan.duration_days)} days</p>
<p class="small">${escHtml(plan.description || '')}</p>
${SubPage_expiryBadge(plan.expiry_action)}
<button
class="btn btn-primary btn-block mt-2"
${canAfford ? '' : 'disabled title="Insufficient balance"'}
onclick="SubPage_confirmPay('${escHtml(plan.hashkey)}', '${escHtml(plan.name)}', ${parseFloat(plan.price)})">
${canAfford ? '<i class="fas fa-wallet"></i> Pay with Wallet' : '<i class="fas fa-lock"></i> Insufficient Balance'}
</button>
</div>
</div>
</div>`;
$('#subscription-plans-container').append(card);
});
$('#subscription-plans-container').show();
}).fail(function () {
$('#subscription-plans-loading').html('<span class="text-danger">Failed to load plans.</span>');
});
};
function SubPage_expiryBadge(action) {
const badges = {
'restrict': '<span class="badge badge-danger">Restricts on expiry</span>',
'warn': '<span class="badge badge-warning">Warning on expiry</span>',
'auto_deduct': '<span class="badge badge-info">Auto-renews on expiry</span>',
};
return badges[action] || '';
}
// ── Confirm + pay ────────────────────────────────────────────────────
window.SubPage_confirmPay = function (planHashkey, planName, price) {
const balance = parseFloat($('#sub-wallet-balance').text().replace('₱', '')) || 0;
ModalQuickDismiss(
'Confirm Payment',
`Pay <strong>₱${price.toFixed(2)}</strong> for <strong>${escHtml(planName)}</strong>?<br>
<small class="text-muted">Your wallet balance: ₱${balance.toFixed(2)}</small>`,
function () { SubPage_doWalletPay(planHashkey); }
);
};
function SubPage_doWalletPay(planHashkey) {
$.post('/subscription/pay/wallet', { plan_hashkey: planHashkey }, function (data) {
if (data && data.success) {
ModalQuickDismiss('Payment Successful', 'Your subscription is now active. Refreshing...');
setTimeout(function () { location.reload(); }, 1500);
} else {
ModalQuickDismiss('Payment Failed', typeof data === 'string' ? data : 'An error occurred.');
}
}).fail(function (xhr) {
ModalQuickDismiss('Payment Failed', xhr.responseJSON || xhr.responseText || 'An error occurred.');
});
}
// ── Load invoice history ─────────────────────────────────────────────
function SubPage_loadInvoices() {
$('#subscription-invoices-loading').show();
$.get('/subscription/invoices', function (invoices) {
$('#subscription-invoices-loading').hide();
if (!invoices || invoices.length === 0) {
$('#subscription-invoices-empty').show();
return;
}
invoices.forEach(function (inv) {
const statusBadge = {
'paid': '<span class="badge badge-success">Paid</span>',
'pending': '<span class="badge badge-warning">Pending</span>',
'failed': '<span class="badge badge-danger">Failed</span>',
}[inv.status] || inv.status;
const row = `<tr>
<td>${inv.created_at ? inv.created_at.substring(0, 10) : '—'}</td>
<td>${escHtml(inv.plan_name || '—')}</td>
<td>₱${parseFloat(inv.amount).toFixed(2)}</td>
<td>${escHtml(inv.payment_method)}</td>
<td>${statusBadge}</td>
<td>${escHtml(inv.payment_reference || '—')}</td>
</tr>`;
$('#subscription-invoices-body').append(row);
});
$('#subscription-invoices-table').show();
}).fail(function () {
$('#subscription-invoices-loading').html('<span class="text-danger">Failed to load invoices.</span>');
});
}
function escHtml(str) {
const d = document.createElement('div');
d.appendChild(document.createTextNode(str ?? ''));
return d.innerHTML;
}
})();
</script>