initial: bootstrap from BukidBountyApp base
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
{{-- Admin: Manage Subscription Plans --}}
|
||||
<div class="card" id="main-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Subscription Plans</h3>
|
||||
<div class="card-tools">
|
||||
<button class="btn btn-sm btn-primary" onclick="SubAdmin_showCreateForm()">
|
||||
<i class="fas fa-plus"></i> New Plan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="sub-admin-plans-loading" class="text-center" style="padding:20px;">
|
||||
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
||||
</div>
|
||||
<table class="table table-bordered table-hover" id="sub-admin-plans-table" style="display:none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Price</th>
|
||||
<th>Duration</th>
|
||||
<th>Expiry Action</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sub-admin-plans-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Create / Edit Plan Form --}}
|
||||
<div class="card" id="sub-admin-plan-form-card" style="display:none;">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title" id="sub-admin-form-title">New Plan</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input type="hidden" id="sub-plan-hashkey">
|
||||
<div class="form-group">
|
||||
<label>Name</label>
|
||||
<input type="text" class="form-control" id="sub-plan-name-field" placeholder="e.g. Monthly Basic">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<textarea class="form-control" id="sub-plan-desc-field" rows="2" placeholder="Optional description"></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label>Price (₱)</label>
|
||||
<input type="number" class="form-control" id="sub-plan-price-field" min="0" step="0.01" placeholder="299.00">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label>Duration (days)</label>
|
||||
<input type="number" class="form-control" id="sub-plan-duration-field" min="1" placeholder="30">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label>On Expiry</label>
|
||||
<select class="form-control" id="sub-plan-expiry-field">
|
||||
<option value="warn">Warn user (show notice)</option>
|
||||
<option value="restrict">Restrict access</option>
|
||||
<option value="auto_deduct">Auto-deduct & renew</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-success" onclick="SubAdmin_savePlan()">
|
||||
<i class="fas fa-save"></i> Save Plan
|
||||
</button>
|
||||
<button class="btn btn-secondary ml-2" onclick="SubAdmin_cancelForm()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- All User Subscriptions --}}
|
||||
<div class="card" id="secondary-card">
|
||||
<div class="card-header ui-sortable-handle" style="cursor:move;">
|
||||
<h3 class="card-title">All User Subscriptions</h3>
|
||||
<div class="card-tools">
|
||||
<select class="form-control form-control-sm" id="sub-admin-status-filter" onchange="SubAdmin_loadSubscriptions()">
|
||||
<option value="">All Statuses</option>
|
||||
<option value="active">Active</option>
|
||||
<option value="expired">Expired</option>
|
||||
<option value="cancelled">Cancelled</option>
|
||||
<option value="pending">Pending</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="sub-admin-subs-loading" class="text-center" style="padding:20px; display:none;">
|
||||
<i class="fas fa-spinner fa-spin"></i> Loading...
|
||||
</div>
|
||||
<table class="table table-sm table-striped" id="sub-admin-subs-table" style="display:none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Mobile</th>
|
||||
<th>Plan</th>
|
||||
<th>Status</th>
|
||||
<th>Starts</th>
|
||||
<th>Expires</th>
|
||||
<th>Method</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sub-admin-subs-body"></tbody>
|
||||
</table>
|
||||
<div id="sub-admin-subs-empty" class="text-muted" style="display:none;">No subscriptions found.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
|
||||
SubAdmin_loadPlans();
|
||||
SubAdmin_loadSubscriptions();
|
||||
|
||||
// ── Load plans list ──────────────────────────────────────────────────
|
||||
function SubAdmin_loadPlans() {
|
||||
$('#sub-admin-plans-loading').show();
|
||||
$('#sub-admin-plans-table').hide();
|
||||
$('#sub-admin-plans-body').empty();
|
||||
|
||||
$.get('/admin/subscription/plans', function (plans) {
|
||||
$('#sub-admin-plans-loading').hide();
|
||||
|
||||
if (!plans || plans.length === 0) {
|
||||
$('#sub-admin-plans-body').append('<tr><td colspan="6" class="text-center text-muted">No plans yet.</td></tr>');
|
||||
$('#sub-admin-plans-table').show();
|
||||
return;
|
||||
}
|
||||
|
||||
plans.forEach(function (plan) {
|
||||
const activeBadge = plan.active
|
||||
? '<span class="badge badge-success">Active</span>'
|
||||
: '<span class="badge badge-secondary">Inactive</span>';
|
||||
|
||||
const expiryLabels = {
|
||||
'warn': '<span class="badge badge-warning">Warn</span>',
|
||||
'restrict': '<span class="badge badge-danger">Restrict</span>',
|
||||
'auto_deduct': '<span class="badge badge-info">Auto-Renew</span>',
|
||||
};
|
||||
|
||||
const row = `<tr>
|
||||
<td>${escHtml(plan.name)}</td>
|
||||
<td>₱${parseFloat(plan.price).toFixed(2)}</td>
|
||||
<td>${parseInt(plan.duration_days)} days</td>
|
||||
<td>${expiryLabels[plan.expiry_action] || plan.expiry_action}</td>
|
||||
<td>${activeBadge}</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-info mr-1" onclick="SubAdmin_editPlan(${JSON.stringify(plan)})">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</button>
|
||||
<button class="btn btn-xs ${plan.active ? 'btn-secondary' : 'btn-success'}"
|
||||
onclick="SubAdmin_togglePlan('${escHtml(plan.hashkey)}', this)">
|
||||
<i class="fas fa-power-off"></i> ${plan.active ? 'Disable' : 'Enable'}
|
||||
</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
$('#sub-admin-plans-body').append(row);
|
||||
});
|
||||
|
||||
$('#sub-admin-plans-table').show();
|
||||
}).fail(function () {
|
||||
$('#sub-admin-plans-loading').html('<span class="text-danger">Failed to load plans.</span>');
|
||||
});
|
||||
}
|
||||
|
||||
// ── Create form ──────────────────────────────────────────────────────
|
||||
window.SubAdmin_showCreateForm = function () {
|
||||
$('#sub-plan-hashkey').val('');
|
||||
$('#sub-plan-name-field').val('');
|
||||
$('#sub-plan-desc-field').val('');
|
||||
$('#sub-plan-price-field').val('');
|
||||
$('#sub-plan-duration-field').val('30');
|
||||
$('#sub-plan-expiry-field').val('warn');
|
||||
$('#sub-admin-form-title').text('New Plan');
|
||||
$('#sub-admin-plan-form-card').show();
|
||||
$('html, body').animate({ scrollTop: $('#sub-admin-plan-form-card').offset().top - 20 }, 300);
|
||||
};
|
||||
|
||||
// ── Edit form ────────────────────────────────────────────────────────
|
||||
window.SubAdmin_editPlan = function (plan) {
|
||||
$('#sub-plan-hashkey').val(plan.hashkey);
|
||||
$('#sub-plan-name-field').val(plan.name);
|
||||
$('#sub-plan-desc-field').val(plan.description || '');
|
||||
$('#sub-plan-price-field').val(parseFloat(plan.price).toFixed(2));
|
||||
$('#sub-plan-duration-field').val(parseInt(plan.duration_days));
|
||||
$('#sub-plan-expiry-field').val(plan.expiry_action);
|
||||
$('#sub-admin-form-title').text('Edit Plan: ' + escHtml(plan.name));
|
||||
$('#sub-admin-plan-form-card').show();
|
||||
$('html, body').animate({ scrollTop: $('#sub-admin-plan-form-card').offset().top - 20 }, 300);
|
||||
};
|
||||
|
||||
window.SubAdmin_cancelForm = function () {
|
||||
$('#sub-admin-plan-form-card').hide();
|
||||
};
|
||||
|
||||
// ── Save (create or update) ──────────────────────────────────────────
|
||||
window.SubAdmin_savePlan = function () {
|
||||
const hashkey = $('#sub-plan-hashkey').val();
|
||||
const isEdit = !!hashkey;
|
||||
const payload = {
|
||||
name: $('#sub-plan-name-field').val().trim(),
|
||||
description: $('#sub-plan-desc-field').val().trim(),
|
||||
price: $('#sub-plan-price-field').val(),
|
||||
duration_days: $('#sub-plan-duration-field').val(),
|
||||
expiry_action: $('#sub-plan-expiry-field').val(),
|
||||
};
|
||||
|
||||
if (!payload.name || !payload.price || !payload.duration_days) {
|
||||
ModalQuickDismiss('Validation', 'Please fill in Name, Price, and Duration.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEdit) payload.hashkey = hashkey;
|
||||
|
||||
const url = isEdit ? '/admin/subscription/plan/update' : '/admin/subscription/plan/create';
|
||||
|
||||
$.post(url, payload, function (data) {
|
||||
ModalQuickDismiss('Saved', 'Plan saved successfully.');
|
||||
$('#sub-admin-plan-form-card').hide();
|
||||
SubAdmin_loadPlans();
|
||||
}).fail(function (xhr) {
|
||||
ModalQuickDismiss('Error', xhr.responseJSON || xhr.responseText || 'Save failed.');
|
||||
});
|
||||
};
|
||||
|
||||
// ── Toggle active/inactive ───────────────────────────────────────────
|
||||
window.SubAdmin_togglePlan = function (hashkey, btn) {
|
||||
$.post('/admin/subscription/plan/toggle', { hashkey: hashkey }, function (data) {
|
||||
SubAdmin_loadPlans();
|
||||
}).fail(function (xhr) {
|
||||
ModalQuickDismiss('Error', xhr.responseJSON || xhr.responseText || 'Toggle failed.');
|
||||
});
|
||||
};
|
||||
|
||||
// ── Load all user subscriptions ──────────────────────────────────────
|
||||
window.SubAdmin_loadSubscriptions = function () {
|
||||
const status = $('#sub-admin-status-filter').val();
|
||||
$('#sub-admin-subs-loading').show();
|
||||
$('#sub-admin-subs-table').hide();
|
||||
$('#sub-admin-subs-empty').hide();
|
||||
$('#sub-admin-subs-body').empty();
|
||||
|
||||
$.post('/admin/subscription/list', { status: status }, function (subs) {
|
||||
$('#sub-admin-subs-loading').hide();
|
||||
|
||||
if (!subs || subs.length === 0) {
|
||||
$('#sub-admin-subs-empty').show();
|
||||
return;
|
||||
}
|
||||
|
||||
subs.forEach(function (sub) {
|
||||
const statusBadges = {
|
||||
'active': '<span class="badge badge-success">Active</span>',
|
||||
'expired': '<span class="badge badge-danger">Expired</span>',
|
||||
'cancelled': '<span class="badge badge-secondary">Cancelled</span>',
|
||||
'pending': '<span class="badge badge-warning">Pending</span>',
|
||||
};
|
||||
const row = `<tr>
|
||||
<td>${escHtml(sub.user?.name || '—')}</td>
|
||||
<td>${escHtml(sub.user?.mobile || '—')}</td>
|
||||
<td>${escHtml(sub.plan?.name || '—')}</td>
|
||||
<td>${statusBadges[sub.status] || sub.status}</td>
|
||||
<td>${sub.starts_at ? sub.starts_at.substring(0, 10) : '—'}</td>
|
||||
<td>${sub.expires_at ? sub.expires_at.substring(0, 10) : '—'}</td>
|
||||
<td>${escHtml(sub.payment_method)}</td>
|
||||
</tr>`;
|
||||
$('#sub-admin-subs-body').append(row);
|
||||
});
|
||||
|
||||
$('#sub-admin-subs-table').show();
|
||||
}).fail(function () {
|
||||
$('#sub-admin-subs-loading').html('<span class="text-danger">Failed to load subscriptions.</span>');
|
||||
});
|
||||
};
|
||||
|
||||
function escHtml(str) {
|
||||
const d = document.createElement('div');
|
||||
d.appendChild(document.createTextNode(str ?? ''));
|
||||
return d.innerHTML;
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
@@ -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>
|
||||
(<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>
|
||||
Reference in New Issue
Block a user