Files
dobetternorge-tools/account.php
T
daveadmin 90117fa9de feat(nav): unified navbar, account page, corpus summary widget, and i18n fixes
- New includes/nav.php: sticky site-wide nav with Tools dropdown, Dashboard
  link, compact language switcher, user identity → /account.php, Log out
- New account.php: credits & plan, profile, team, usage sections
- New api/corpus-summary.php: JSON endpoint for corpus doc count + last updated
- Replaces topbar in layout.php, layout_dashboard.php, and dashboard.php
- Fixes hardcoded Norwegian strings in dashboard.php credit cards via dbnToolsT()
- Adds 35 new i18n keys across all 4 languages (en/no/uk/pl) in i18n.php
- CSS: .dbn-nav navbar + .account-* account page styles in tools.css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 19:11:39 +02:00

231 lines
10 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/bootstrap.php';
require_once __DIR__ . '/includes/FreeTier.php';
if (!dbnToolsIsAuthenticated()) {
header('Location: /?return=' . urlencode($_SERVER['REQUEST_URI'] ?? '/account.php'));
exit;
}
$uiLang = dbnToolsCurrentLanguage();
$authUser = dbnToolsAuthenticatedUser();
$isSso = dbnToolsIsFreeTier();
$email = (string)($authUser['email'] ?? '');
$role = (string)($authUser['role'] ?? '');
// Credits & plan (SSO users only)
$detail = $isSso ? FreeTier::balanceDetail((int)$_SESSION['dbn_tools_sso_uid']) : null;
$tier = $detail ? (string)$detail['tier'] : ($isSso ? 'free' : 'caveau');
$tierLabels = [
'free' => ['Free', '#f3f4f6', '#374151'],
'plus' => ['Plus', '#ddd6fe', '#5b21b6'],
'pro' => ['Pro', '#bfdbfe', '#1e40af'],
'caveau' => ['CaveauAI', '#d1fae5', '#065f46'],
];
$tierLabel = $tierLabels[$tier] ?? $tierLabels['free'];
$monthlyAllowance = $detail ? FreeTier::monthlyAllowance($tier) : 0;
$creditsUsed = $detail ? max(0, $monthlyAllowance - (int)$detail['balance']) : 0;
// Team (Caveau sessions only)
$teamMembers = [];
if (!$isSso && !empty($authUser['client_id'])) {
try {
$db = dbnToolsDb();
$stmt = $db->prepare(
"SELECT cu.email, cu.role, cu.created_at
FROM client_users cu
WHERE cu.client_id = ?
ORDER BY FIELD(cu.role,'owner','admin','editor','viewer'), cu.created_at ASC"
);
$stmt->execute([(int)$authUser['client_id']]);
$teamMembers = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
// non-fatal
}
}
// Renewal date label
$renewalLabel = '';
if ($detail) {
if (!empty($detail['trial_active']) && !empty($detail['trial_expires_at'])) {
$renewalLabel = date('d M Y', strtotime((string)$detail['trial_expires_at']))
. ' (' . (int)$detail['trial_days_remaining'] . ' ' . dbnToolsT('trial_days_left', $uiLang) . ')';
} elseif (!empty($detail['subscription_period_end'])) {
$renewalLabel = date('d M Y', strtotime((string)$detail['subscription_period_end']));
}
}
?>
<!doctype html>
<html lang="<?= htmlspecialchars($uiLang) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars(dbnToolsT('account_title', $uiLang)) ?> — Do Better Norge</title>
<meta name="robots" content="noindex, nofollow">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;600;700&family=IBM+Plex+Sans:wght@400;500;600;700&display=swap">
<link rel="stylesheet" href="assets/css/tools.css">
</head>
<body data-authenticated="true" class="lt-app">
<script>
window.DBN_TOOLS_AUTHENTICATED = true;
window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
</script>
<?php include __DIR__ . '/includes/nav.php'; ?>
<div class="account-shell">
<div class="account-hero">
<h1><?= htmlspecialchars(dbnToolsT('account_title', $uiLang)) ?></h1>
<p><?= htmlspecialchars($email) ?></p>
</div>
<?php if ($detail): ?>
<!-- ── Credits & Plan ───────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('account_credits', $uiLang)) ?></p>
<span class="account-tier-pill" style="background:<?= htmlspecialchars($tierLabel[1]) ?>; color:<?= htmlspecialchars($tierLabel[2]) ?>;">
<?= htmlspecialchars($tierLabel[0]) ?>
</span>
<?php if (!empty($detail['trial_active'])): ?>
<span class="account-tier-pill" style="background:#fef3c7;color:#92400e;margin-left:0.4rem;">
<?= htmlspecialchars(dbnToolsT('trial_active_label', $uiLang)) ?>
</span>
<?php endif; ?>
<p class="account-credits-big">
<?= number_format((int)$detail['balance'] + (int)$detail['bonus_balance'], 0, ',', ' ') ?>
</p>
<p class="account-credits-sub">
<?= (int)$detail['balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_monthly', $uiLang)) ?>
· <?= (int)$detail['bonus_balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_bonus', $uiLang)) ?>
</p>
<?php if ($renewalLabel): ?>
<div class="account-row">
<span class="account-row__label"><?= htmlspecialchars(dbnToolsT('renewal_date', $uiLang)) ?></span>
<span class="account-row__value"><?= htmlspecialchars($renewalLabel) ?></span>
</div>
<?php endif; ?>
<?php if (in_array($tier, ['free'], true)): ?>
<a href="/pricing.php" class="account-upgrade-cta">Upgrade plan →</a>
<?php endif; ?>
</section>
<?php endif; ?>
<!-- ── Profile ──────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('account_profile', $uiLang)) ?></p>
<div class="account-row">
<span class="account-row__label">Email</span>
<span class="account-row__value"><?= htmlspecialchars($email) ?></span>
</div>
<div class="account-row">
<span class="account-row__label">Login</span>
<span class="account-row__value">
<?php if ($isSso): ?>
<?= htmlspecialchars(dbnToolsT('login_method_sso', $uiLang)) ?>
<?php else: ?>
<?= htmlspecialchars(dbnToolsT('login_method_email', $uiLang)) ?>
<?php endif; ?>
</span>
</div>
<?php if ($role !== '' && $role !== 'sso'): ?>
<div class="account-row">
<span class="account-row__label">Role</span>
<span class="account-row__value"><span class="account-role-pill"><?= htmlspecialchars($role) ?></span></span>
</div>
<?php endif; ?>
</section>
<!-- ── Team ─────────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('account_team', $uiLang)) ?></p>
<?php if ($isSso): ?>
<p style="color:var(--muted,#667085); font-size:0.88rem;"><?= htmlspecialchars(dbnToolsT('team_single_sso', $uiLang)) ?></p>
<?php elseif (empty($teamMembers)): ?>
<p style="color:var(--muted,#667085); font-size:0.88rem;">—</p>
<?php else: ?>
<table class="account-team-table">
<thead>
<tr>
<th>Email</th>
<th>Role</th>
<th>Added</th>
</tr>
</thead>
<tbody>
<?php foreach ($teamMembers as $member): ?>
<tr>
<td><?= htmlspecialchars($member['email']) ?></td>
<td><span class="account-role-pill"><?= htmlspecialchars($member['role']) ?></span></td>
<td><?= htmlspecialchars(date('d M Y', strtotime((string)$member['created_at']))) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</section>
<?php if ($detail): ?>
<!-- ── Usage ────────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('account_usage', $uiLang)) ?></p>
<div class="account-row">
<span class="account-row__label"><?= htmlspecialchars(dbnToolsT('usage_credits_used', $uiLang)) ?></span>
<span class="account-row__value"><?= $creditsUsed ?> / <?= $monthlyAllowance ?></span>
</div>
<?php if ($monthlyAllowance > 0): ?>
<div class="account-usage-bar-wrap">
<div class="account-usage-bar" style="width:<?= min(100, round($creditsUsed / $monthlyAllowance * 100)) ?>%"></div>
</div>
<?php endif; ?>
<?php if (!empty($detail['storage_quota_bytes']) && (int)$detail['storage_quota_bytes'] > 0):
$storagePct = min(100, round((int)$detail['storage_used_bytes'] / (int)$detail['storage_quota_bytes'] * 100));
$usedMb = round((int)$detail['storage_used_bytes'] / 1048576, 1);
$quotaMb = round((int)$detail['storage_quota_bytes'] / 1048576, 0);
?>
<div class="account-row" style="margin-top:0.75rem;">
<span class="account-row__label"><?= htmlspecialchars(dbnToolsT('usage_storage_used', $uiLang)) ?></span>
<span class="account-row__value"><?= $usedMb ?> / <?= $quotaMb ?> MB</span>
</div>
<div class="account-usage-bar-wrap">
<div class="account-usage-bar" style="width:<?= $storagePct ?>%"></div>
</div>
<?php endif; ?>
<p style="margin-top:1rem; color:var(--muted,#667085); font-size:0.82rem; font-style:italic;">
<?= htmlspecialchars(dbnToolsT('usage_log_coming', $uiLang)) ?>
</p>
<?php if (empty($detail['survey_completed_at'])): ?>
<a href="https://dobetternorge.no/survey.php" class="account-survey-cta">
<div>
<strong><?= htmlspecialchars(dbnToolsT('earn_credits_eyebrow', $uiLang)) ?></strong>
<span><?= htmlspecialchars(dbnToolsT('survey_cta_text', $uiLang)) ?></span>
</div>
<span style="font-size:1.2rem;">→</span>
</a>
<?php endif; ?>
</section>
<?php endif; ?>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
</body>
</html>