Files
budget/views/onboarding-layout.php
Ty Clifford 6686388834 - Implemented the complete first-run experience:
Account creation and guarded onboarding flow.
Current-cash starting balance with automatic rollover enabled.
Dynamic bill generation, shared/custom categories, recurring targets, 
and optional same-day onboarding transactions.
Optional dormant reserve and additional categories.
Mailgun email or authenticator-app 2FA configuration.
Responsive dark/light UI with no overflow at 390px.
Fixed administrator session-ID creation bug.
Core implementation: [OnboardingService.php (line 
59)](/Users/tyemeclifford/Documents/GH/budget/app/OnboardingService.php:59), 
[onboarding.php (line 
19)](/Users/tyemeclifford/Documents/GH/budget/views/onboarding.php:19), 
and [index.php (line 
143)](/Users/tyemeclifford/Documents/GH/budget/public/index.php:143).
Verification passed: financial self-test, onboarding integration test, 
full browser walkthrough, mobile layout, routing guards, and zero 
browser console errors. Live email delivery requires actual Mailgun 
credentials and was not sent during testing.
2026-06-15 14:51:44 -04:00

50 lines
2.3 KiB
PHP

<?php
$appName = (string) $settings->get('app.name', 'Neon Ledger');
$defaultTheme = (string) $settings->get('theme.default', 'dark');
?>
<!doctype html>
<html lang="en" data-theme="<?= h($defaultTheme) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<meta name="theme-color" content="#090b12">
<title><?= h($title ?? 'Welcome') ?> · <?= h($appName) ?></title>
<link rel="stylesheet" href="/assets/app.css?v=<?= (int) filemtime(APP_ROOT . '/public/assets/app.css') ?>">
<script>document.documentElement.dataset.theme=localStorage.getItem('budget-theme')||<?= json_encode($defaultTheme) ?>;</script>
</head>
<body class="onboarding-body">
<div class="onboarding-shell">
<header class="onboarding-header">
<div class="brand onboarding-brand">
<span class="brand-mark"><i></i><b>N</b></span>
<span class="brand-copy"><strong><?= h($appName) ?></strong><small>Personal finance planner</small></span>
</div>
<div class="onboarding-account">
<span><small>Signed in as</small><strong><?= h($user['email'] ?? '') ?></strong></span>
<button class="icon-control" type="button" data-theme-toggle aria-label="Toggle light and dark mode"><?= icon('sun') ?></button>
<form method="post">
<?= csrf_field() ?><input type="hidden" name="action" value="logout">
<button class="icon-control" type="submit" aria-label="Sign out"><?= icon('logout') ?></button>
</form>
</div>
</header>
<?php foreach ($flashes as $flash): ?>
<div class="flash onboarding-flash <?= h($flash['type']) ?>" role="status">
<span><?= $flash['type'] === 'success' ? '✓' : '!' ?></span>
<p><?= h($flash['message']) ?></p>
<button type="button" aria-label="Dismiss"><?= icon('close') ?></button>
</div>
<?php endforeach; ?>
<main class="onboarding-main"><?= $content ?></main>
<footer class="onboarding-footer">
<span>Private by default</span>
<span>SQLite powered · Your data stays under your control</span>
</footer>
</div>
<script src="/assets/app.js?v=<?= (int) filemtime(APP_ROOT . '/public/assets/app.js') ?>"></script>
</body>
</html>