90117fa9de
- 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>
231 lines
10 KiB
PHP
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>
|