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,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>

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>

View File

@@ -0,0 +1,171 @@
<html lang="en">
<head>
<script>
window.APP_BUILD_PATH = '/build/assets/';
</script>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, viewport-fit=cover">
@php
$user = Auth::user();
$isUltimate = $user && ($user->acct_type === \App\Enums\UserTypes::ULTIMATE || $user->acct_type === \App\Enums\UserTypes::ULTIMATE->value);
$appName = \App\Support\SystemSettingsHelper::appName();
$appDescription = \App\Support\SystemSettingsHelper::appDescription();
$logoUrl = \App\Support\SystemSettingsHelper::logoUrl();
$appleTouchIcon = $logoUrl;
@endphp
<title>{{ $isUltimate ? "Ultimate Console" : $appName }}</title>
<meta name="description" content="{{ $isUltimate ? "Premium Administrative Management" : $appDescription }}">
<!-- Favicon and Touch Icons -->
<link rel="shortcut icon" href="{{ $logoUrl }}">
<link rel="apple-touch-icon-precomposed" href="{{ $logoUrl }}">
<!-- Preload Critical Resources -->
<link rel="preload" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/586604538260.css" as="style" crossorigin="anonymous">
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" as="style" crossorigin="anonymous">
<link rel="preload" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/ce9d07500ad9.js" as="script" crossorigin="anonymous">
<!-- Critical Stylesheets (render-blocking, kept minimal) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/586604538260.css" crossorigin="anonymous">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/f0d2b4c93c87.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/c8c4e91fed96.css" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/4659a6e480c8.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/79a3e8f17e14.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/81af51576fb2.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/47dd690f8f31.css" type="text/css" crossorigin="anonymous">
<!-- Deferred Stylesheets (non-critical, loaded after paint) -->
<link rel="stylesheet" media="print" onload="this.media='all'" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/4a7641c6c583.css" crossorigin="anonymous">
<link rel="stylesheet" media="print" onload="this.media='all'" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/9ff0aecab9be.css" crossorigin="anonymous">
<link rel="stylesheet" media="print" onload="this.media='all'" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/f098c4da9839.css" crossorigin="anonymous">
<link rel="stylesheet" media="print" onload="this.media='all'" href="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/e6e2a25c4c1b.css" crossorigin="anonymous">
<link rel="manifest" href="/manifest.json" data-pwa-version="set_in_manifest_and_pwa_js">
<!-- iOS PWA Support -->
<link rel="apple-touch-icon" sizes="192x192" href="{{ $appleTouchIcon }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ $appleTouchIcon }}">
<link rel="apple-touch-icon" sizes="167x167" href="{{ $appleTouchIcon }}">
<link rel="apple-touch-icon" sizes="152x152" href="{{ $appleTouchIcon }}">
<link rel="apple-touch-icon" sizes="120x120" href="{{ $appleTouchIcon }}">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="{{ $isUltimate ? 'Ultimate' : $appName }}">
<!-- Core Scripts (synchronous needed by app) -->
<script src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/ce9d07500ad9.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/d876b078facc.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/95da8df7f39c.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/9a9bb350c174.js" crossorigin="anonymous"></script>
<!-- Deferred Scripts (loaded in background, execute after HTML parse) -->
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/c541ef063278.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/2bc473e85a3b.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/9765fc464341.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/2b7a237936a4.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/1e29ab3fbd7c.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/b82753e852e4.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/7bf98c15d2f5.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/602eb5c4c25c.js" crossorigin="anonymous"></script>
<!-- Inline Styles (dark mode, etc.) -->
<style>
body {
background-color: #ffffff;
color: #000000;
transition: background-color 0.3s, color 0.3s;
}
body.dark-mode {
background-color: #121212;
color: #e0e0e0;
}
.dark-mode .tf-balance-box,
.dark-mode .tf-statusbar,
.dark-mode .tf-navigation-bar,
.dark-mode .bottom-navigation-bar {
background-color: #24272c;
color: #e0e0e0;
}
.dark-mode a {
color: #e0e0e0;
}
</style>
@php
$manifestPath = public_path('build/.vite/manifest.json');
$manifest = file_exists($manifestPath) ? json_decode(file_get_contents($manifestPath), true) : [];
$appJSData = $manifest['resources/js/app.js'];
$appJs = $appJSData['file'] ?? 'build/assets/app.js';
$appCss = $appJSData['css'][0] ?? null;
@endphp
@if ($appCss)
<link rel="stylesheet" href="/build/{{ $appCss }}">
@endif
</head>
<body id="body-global">
<div id="app">
<!-- Top Header -->
<TopHeader @navigate="$navigate($event)" />
<main id="main-body" data-page='@json($page)' style>
<!-- Spinner overlay -->
<div v-if="loading" class="loading-overlay">
<div class="spinner"></div>
</div>
</main>
<!-- Bottom Navigation -->
<BottomNav @navigate="$navigate($event)" />
</div>
<!-- Vue App -->
<script type="module" src="/build/{{ $appJs }}"></script>
<style>
/* Loading overlay */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.7);
z-index: 99999;
display: flex;
justify-content: center;
align-items: center;
}
/* Spinner style */
.spinner {
width: 50px;
height: 50px;
border: 5px solid #ccc;
border-top-color: #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
</body>
</html>