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>
This commit is contained in:
+230
@@ -0,0 +1,230 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* GET /api/corpus-summary.php
|
||||||
|
* Returns: { doc_count: int, last_updated: string|null }
|
||||||
|
*
|
||||||
|
* Auth: SSO session only. Ensures dashboard tenant is provisioned.
|
||||||
|
*/
|
||||||
|
require_once __DIR__ . '/../includes/bootstrap.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Cache-Control: no-store');
|
||||||
|
|
||||||
|
if (!dbnToolsIsAuthenticated() || !dbnToolsIsFreeTier()) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['error' => 'auth_required']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$tenant = dbnToolsEnsureDashboardTenant();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(503);
|
||||||
|
echo json_encode(['error' => 'tenant_unavailable']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = dbnToolsDb();
|
||||||
|
$stmt = $db->prepare(
|
||||||
|
"SELECT COUNT(*) AS doc_count, MAX(created_at) AS last_updated
|
||||||
|
FROM client_documents
|
||||||
|
WHERE client_id = ? AND status = 'ready'"
|
||||||
|
);
|
||||||
|
$stmt->execute([(int)$tenant['client_id']]);
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'doc_count' => (int)($row['doc_count'] ?? 0),
|
||||||
|
'last_updated' => $row['last_updated'] ?? null,
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => 'db_error']);
|
||||||
|
}
|
||||||
@@ -8814,3 +8814,362 @@ body.lt-landing {
|
|||||||
color: rgba(22,19,15,0.80);
|
color: rgba(22,19,15,0.80);
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════
|
||||||
|
UNIFIED SITE NAVBAR — .dbn-nav
|
||||||
|
═══════════════════════════════════════════════════════════════ */
|
||||||
|
.dbn-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
background: var(--dbn-blue, #00205b);
|
||||||
|
height: 52px;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
border-bottom: 2px solid var(--dbn-red, #ba0c2f);
|
||||||
|
font-family: 'IBM Plex Sans', sans-serif;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Brand */
|
||||||
|
.dbn-nav__brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.45rem;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.dbn-nav__brandmark { font-size: 1.15rem; }
|
||||||
|
.dbn-nav__brandname { font-size: 0.9rem; }
|
||||||
|
|
||||||
|
/* Centre links */
|
||||||
|
.dbn-nav__links {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 0;
|
||||||
|
margin-left: 1.75rem;
|
||||||
|
}
|
||||||
|
.dbn-nav__link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
color: rgba(255, 255, 255, 0.80);
|
||||||
|
padding: 0 1rem;
|
||||||
|
height: 52px;
|
||||||
|
text-decoration: none;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: color 0.12s, background 0.12s;
|
||||||
|
}
|
||||||
|
.dbn-nav__link:hover,
|
||||||
|
.dbn-nav__link.is-active {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.10);
|
||||||
|
}
|
||||||
|
.dbn-nav__caret { font-size: 0.65rem; opacity: 0.7; }
|
||||||
|
|
||||||
|
/* Dropdown */
|
||||||
|
.dbn-nav__dropdown { position: relative; display: flex; align-items: stretch; }
|
||||||
|
.dbn-nav__panel {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 2px);
|
||||||
|
left: 0;
|
||||||
|
min-width: 240px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid var(--line, #d8dde7);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16), 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
padding: 0.4rem 0;
|
||||||
|
z-index: 1100;
|
||||||
|
animation: navPanelIn 0.12s ease;
|
||||||
|
}
|
||||||
|
@keyframes navPanelIn {
|
||||||
|
from { opacity: 0; transform: translateY(-4px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
.dbn-nav__dropdown.is-open .dbn-nav__panel { display: block; }
|
||||||
|
.dbn-nav__panel-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2.5rem 1fr;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.55rem 1rem;
|
||||||
|
color: var(--dbn-ink, #16130f);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
gap: 0 0;
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
.dbn-nav__panel-item:hover { background: var(--soft-teal, #e7f5f2); }
|
||||||
|
.dbn-nav__panel-badge {
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--dbn-blue, #00205b);
|
||||||
|
background: rgba(0, 32, 91, 0.08);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
.dbn-nav__panel-label {
|
||||||
|
grid-column: 2;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--dbn-ink, #16130f);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
.dbn-nav__panel-sub {
|
||||||
|
grid-column: 2;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--muted, #667085);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Right side */
|
||||||
|
.dbn-nav__right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Language switcher */
|
||||||
|
.dbn-nav__langs {
|
||||||
|
display: flex;
|
||||||
|
gap: 1px;
|
||||||
|
}
|
||||||
|
.dbn-nav__lang {
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 3px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
transition: color 0.12s, background 0.12s;
|
||||||
|
}
|
||||||
|
.dbn-nav__lang:hover { color: #fff; background: rgba(255,255,255,0.12); }
|
||||||
|
.dbn-nav__lang.is-active { color: #fff; background: rgba(255,255,255,0.18); }
|
||||||
|
|
||||||
|
/* Auth right */
|
||||||
|
.dbn-nav__account-link {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
gap: 1px;
|
||||||
|
}
|
||||||
|
.dbn-nav__username {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
max-width: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.dbn-nav__account-badge {
|
||||||
|
color: rgba(255, 255, 255, 0.58);
|
||||||
|
font-size: 0.68rem;
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
}
|
||||||
|
.dbn-nav__account-link:hover .dbn-nav__username { color: #fff; }
|
||||||
|
.dbn-nav__account-link:hover .dbn-nav__account-badge { color: rgba(255,255,255,0.82); }
|
||||||
|
|
||||||
|
.dbn-nav__logout,
|
||||||
|
.dbn-nav__login {
|
||||||
|
color: rgba(255, 255, 255, 0.80);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.3rem 0.65rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: color 0.12s, background 0.12s;
|
||||||
|
}
|
||||||
|
.dbn-nav__logout:hover { color: #fff; background: rgba(255,255,255,0.12); }
|
||||||
|
.dbn-nav__login {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.30);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.dbn-nav__login:hover { color: #fff; background: rgba(255,255,255,0.12); border-color: rgba(255,255,255,0.50); }
|
||||||
|
|
||||||
|
/* Push down any direct sibling so nav doesn't overlap */
|
||||||
|
.dbn-nav + .app-shell,
|
||||||
|
.dbn-nav + main,
|
||||||
|
.dbn-nav + .dash-shell { margin-top: 0; }
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════
|
||||||
|
ACCOUNT PAGE — .account-*
|
||||||
|
═══════════════════════════════════════════════════════════════ */
|
||||||
|
.account-shell {
|
||||||
|
max-width: 780px;
|
||||||
|
margin: 2.5rem auto 4rem;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
}
|
||||||
|
.account-hero {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.account-hero h1 {
|
||||||
|
font-family: 'Crimson Pro', serif;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--dbn-blue, #00205b);
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
}
|
||||||
|
.account-hero p {
|
||||||
|
color: var(--muted, #667085);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.account-section {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid var(--line, #d8dde7);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
.account-section__title {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--muted, #667085);
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
padding-bottom: 0.6rem;
|
||||||
|
border-bottom: 1px solid var(--line, #d8dde7);
|
||||||
|
}
|
||||||
|
.account-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.55rem 0;
|
||||||
|
border-bottom: 1px solid var(--line, #d8dde7);
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
.account-row:last-child { border-bottom: none; }
|
||||||
|
.account-row__label { color: var(--muted, #667085); flex-shrink: 0; }
|
||||||
|
.account-row__value { font-weight: 600; color: var(--dbn-ink, #16130f); text-align: right; }
|
||||||
|
.account-row__value a { color: var(--dbn-blue, #00205b); text-decoration: none; }
|
||||||
|
.account-row__value a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
.account-tier-pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-credits-big {
|
||||||
|
font-family: 'Crimson Pro', serif;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--dbn-blue, #00205b);
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0.5rem 0 0.25rem;
|
||||||
|
}
|
||||||
|
.account-credits-sub {
|
||||||
|
font-size: 0.83rem;
|
||||||
|
color: var(--muted, #667085);
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-upgrade-cta {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
background: var(--dbn-blue, #00205b);
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.55rem 1.1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
.account-upgrade-cta:hover { background: #001845; }
|
||||||
|
|
||||||
|
.account-team-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.account-team-table th {
|
||||||
|
text-align: left;
|
||||||
|
color: var(--muted, #667085);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
padding: 0.4rem 0;
|
||||||
|
border-bottom: 1px solid var(--line, #d8dde7);
|
||||||
|
}
|
||||||
|
.account-team-table td {
|
||||||
|
padding: 0.6rem 0;
|
||||||
|
border-bottom: 1px solid var(--line, #d8dde7);
|
||||||
|
color: var(--dbn-ink, #16130f);
|
||||||
|
}
|
||||||
|
.account-team-table tr:last-child td { border-bottom: none; }
|
||||||
|
.account-role-pill {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background: rgba(0,32,91,0.08);
|
||||||
|
color: var(--dbn-blue, #00205b);
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-usage-bar-wrap {
|
||||||
|
height: 8px;
|
||||||
|
background: var(--line, #d8dde7);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
}
|
||||||
|
.account-usage-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--dbn-blue, #00205b);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: width 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-survey-cta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
background: #fef3c7;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.9rem 1.1rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #92400e;
|
||||||
|
}
|
||||||
|
.account-survey-cta strong { display: block; font-size: 0.9rem; }
|
||||||
|
.account-survey-cta span { font-size: 0.8rem; opacity: 0.85; }
|
||||||
|
.account-survey-cta:hover { background: #fde68a; }
|
||||||
|
|
||||||
|
|||||||
+42
-37
@@ -47,68 +47,50 @@ window.DBN_TOOLS_AUTHENTICATED = true;
|
|||||||
window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
|
window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<?php include __DIR__ . '/includes/nav.php'; ?>
|
||||||
|
|
||||||
<main id="appShell" class="app-shell dashboard-shell">
|
<main id="appShell" class="app-shell dashboard-shell">
|
||||||
<header class="topbar">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow"><?= htmlspecialchars(dbnToolsT('brand_line', $uiLang)) ?></p>
|
|
||||||
<h1><?= htmlspecialchars(dbnToolsT('dashboard_title', $uiLang)) ?></h1>
|
|
||||||
<div class="case-no">
|
|
||||||
<span class="pulse"></span>
|
|
||||||
<span>family-legal</span>
|
|
||||||
<span class="case-sep">.</span>
|
|
||||||
<span><?= htmlspecialchars(dbnToolsT('retention', $uiLang)) ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="topbar-actions">
|
|
||||||
<nav class="shell-lang-switcher" aria-label="Language">
|
|
||||||
<?php foreach (dbnToolsSupportedLanguages() as $langCode): ?>
|
|
||||||
<a href="<?= htmlspecialchars($langPath . '?lang=' . $langCode) ?>" class="<?= $langCode === $uiLang ? 'is-active' : '' ?>"><?= htmlspecialchars(dbnToolsLanguageLabel($langCode)) ?></a>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</nav>
|
|
||||||
<span id="healthPill" class="status-pill"><?= htmlspecialchars(dbnToolsT('session_active', $uiLang)) ?></span>
|
|
||||||
<?php if ($dashIsSso): ?>
|
|
||||||
<a href="/billing.php" class="tier-pill" style="background: <?= htmlspecialchars($tierLabel[1]) ?>; color: <?= htmlspecialchars($tierLabel[2]) ?>; padding: 4px 12px; border-radius: 999px; font-size: 0.82rem; font-weight: 600; text-decoration: none;">
|
|
||||||
<?= htmlspecialchars($tierLabel[0]) ?>
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<button id="healthButton" class="secondary-button" type="button"><?= htmlspecialchars(dbnToolsT('health', $uiLang)) ?></button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<?php if ($dashIsSso && $dashDetail): ?>
|
<?php if ($dashIsSso && $dashDetail): ?>
|
||||||
<section class="dashboard-status-row" style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap:1rem; margin: 1.5rem 0;">
|
<section class="dashboard-status-row" style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap:1rem; margin: 1.5rem 0;">
|
||||||
<div class="status-card" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem;">
|
<div class="status-card" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem;">
|
||||||
<p style="margin:0; color:#6b7280; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em;">Tilgjengelige kreditter</p>
|
<p style="margin:0; color:#6b7280; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em;"><?= htmlspecialchars(dbnToolsT('credits_available', $uiLang)) ?></p>
|
||||||
<p style="margin:0.35rem 0 0; font-size:1.8rem; font-weight:700; color:#00205B;">
|
<p style="margin:0.35rem 0 0; font-size:1.8rem; font-weight:700; color:#00205B;">
|
||||||
<?php $eff = (int)$dashDetail['balance'] + (int)$dashDetail['bonus_balance']; ?>
|
<?php $eff = (int)$dashDetail['balance'] + (int)$dashDetail['bonus_balance']; ?>
|
||||||
<?= number_format($eff, 0, ',', ' ') ?>
|
<?= number_format($eff, 0, ',', ' ') ?>
|
||||||
</p>
|
</p>
|
||||||
<p style="margin:0; color:#6b7280; font-size:0.85rem;"><?= (int)$dashDetail['balance'] ?> månedlige · <?= (int)$dashDetail['bonus_balance'] ?> bonus · <a href="/billing.php">Detaljer</a></p>
|
<p style="margin:0; color:#6b7280; font-size:0.85rem;"><?= (int)$dashDetail['balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_monthly', $uiLang)) ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_bonus', $uiLang)) ?> · <a href="/billing.php"><?= htmlspecialchars(dbnToolsT('details_link', $uiLang)) ?></a></p>
|
||||||
</div>
|
</div>
|
||||||
<?php if (in_array($dashTier, ['plus','pro'], true)): ?>
|
<?php if (in_array($dashTier, ['plus','pro'], true)): ?>
|
||||||
<a class="status-card" href="/min-sak.php" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none; color:inherit;">
|
<a class="status-card" href="/min-sak.php" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none; color:inherit;">
|
||||||
<p style="margin:0; color:#6b7280; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em;">Min sak</p>
|
<p style="margin:0; color:#6b7280; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em;"><?= htmlspecialchars(dbnToolsT('my_case', $uiLang)) ?></p>
|
||||||
<p style="margin:0.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;">Bygg din egen sak →</p>
|
<p style="margin:0.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?> →</p>
|
||||||
<?php
|
<?php
|
||||||
$used = (int)$dashDetail['storage_used_bytes'];
|
$used = (int)$dashDetail['storage_used_bytes'];
|
||||||
$quota = (int)$dashDetail['storage_quota_bytes'];
|
$quota = (int)$dashDetail['storage_quota_bytes'];
|
||||||
$usedMb = $used > 0 ? round($used / 1048576, 1) : 0;
|
$usedMb = $used > 0 ? round($used / 1048576, 1) : 0;
|
||||||
$quotaMb = $quota > 0 ? round($quota / 1048576, 0) : 0;
|
$quotaMb = $quota > 0 ? round($quota / 1048576, 0) : 0;
|
||||||
?>
|
?>
|
||||||
<p style="margin:0; color:#6b7280; font-size:0.85rem;"><?= $usedMb ?> MB / <?= $quotaMb ?> MB brukt</p>
|
<p style="margin:0; color:#6b7280; font-size:0.85rem;"><?= $usedMb ?> MB / <?= $quotaMb ?> MB</p>
|
||||||
</a>
|
</a>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a class="status-card" href="/pricing.php" style="background:linear-gradient(135deg,#00205B,#003478); color:#fff; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none;">
|
<a class="status-card" href="/pricing.php" style="background:linear-gradient(135deg,#00205B,#003478); color:#fff; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none;">
|
||||||
<p style="margin:0; opacity:0.85; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em;">Bygg din egen sak</p>
|
<p style="margin:0; opacity:0.85; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?></p>
|
||||||
<p style="margin:0.35rem 0 0; font-size:1.4rem; font-weight:700;">Last opp dokumenter →</p>
|
<p style="margin:0.35rem 0 0; font-size:1.4rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('upload_documents', $uiLang)) ?> →</p>
|
||||||
<p style="margin:0; opacity:0.85; font-size:0.85rem;">Tilgjengelig fra Plus NOK 129/mnd</p>
|
<p style="margin:0; opacity:0.85; font-size:0.85rem;"><?= htmlspecialchars(dbnToolsT('upgrade_from_plus', $uiLang)) ?></p>
|
||||||
</a>
|
</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<!-- Corpus summary widget — populated via fetch('/api/corpus-summary.php') -->
|
||||||
|
<a id="corpusSummaryCard" class="status-card" href="/dashboard/" style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none; color:inherit; display:block;">
|
||||||
|
<p style="margin:0; color:#6b7280; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em;"><?= htmlspecialchars(dbnToolsT('my_corpus', $uiLang)) ?></p>
|
||||||
|
<p id="corpusDocCount" style="margin:0.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;">—</p>
|
||||||
|
<p id="corpusUpdated" style="margin:0; color:#6b7280; font-size:0.85rem;"><?= htmlspecialchars(dbnToolsT('open_corpus', $uiLang)) ?> →</p>
|
||||||
|
</a>
|
||||||
<?php if ($showSurveyCta): ?>
|
<?php if ($showSurveyCta): ?>
|
||||||
<a class="status-card" href="https://dobetternorge.no/survey.php" style="background:#fef3c7; color:#92400e; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none;">
|
<a class="status-card" href="https://dobetternorge.no/survey.php" style="background:#fef3c7; color:#92400e; border-radius:10px; padding:1.1rem 1.25rem; text-decoration:none;">
|
||||||
<p style="margin:0; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em; opacity:0.85;">Tjen 25 ekstra kreditter</p>
|
<p style="margin:0; font-size:0.85rem; text-transform:uppercase; letter-spacing:0.06em; opacity:0.85;"><?= htmlspecialchars(dbnToolsT('earn_credits_eyebrow', $uiLang)) ?></p>
|
||||||
<p style="margin:0.35rem 0 0; font-size:1.2rem; font-weight:700;">Ta vår 5-spørsmåls undersøkelse →</p>
|
<p style="margin:0.35rem 0 0; font-size:1.2rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('survey_btn', $uiLang)) ?> →</p>
|
||||||
<p style="margin:0; font-size:0.85rem; opacity:0.85;">Ingen salgspitch — bare research</p>
|
<p style="margin:0; font-size:0.85rem; opacity:0.85;"><?= htmlspecialchars(dbnToolsT('survey_cta_text', $uiLang)) ?></p>
|
||||||
</a>
|
</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</section>
|
</section>
|
||||||
@@ -246,5 +228,28 @@ foreach ($tools as $slug => $item):
|
|||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
</script>
|
</script>
|
||||||
|
<?php if ($dashIsSso): ?>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var card = document.getElementById('corpusSummaryCard');
|
||||||
|
var countEl = document.getElementById('corpusDocCount');
|
||||||
|
var updEl = document.getElementById('corpusUpdated');
|
||||||
|
if (!card || !countEl) return;
|
||||||
|
fetch('/api/corpus-summary.php')
|
||||||
|
.then(function (r) { return r.ok ? r.json() : null; })
|
||||||
|
.then(function (data) {
|
||||||
|
if (!data) return;
|
||||||
|
countEl.textContent = data.doc_count !== undefined
|
||||||
|
? data.doc_count.toLocaleString() + ' docs'
|
||||||
|
: '—';
|
||||||
|
if (data.last_updated && updEl) {
|
||||||
|
var d = new Date(data.last_updated);
|
||||||
|
updEl.textContent = d.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric' });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function () {});
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -225,6 +225,40 @@ function dbnToolsTranslations(): array
|
|||||||
'pricing_strip_title' => 'Start free. Upgrade when you need your own case.',
|
'pricing_strip_title' => 'Start free. Upgrade when you need your own case.',
|
||||||
'pricing_strip_sub' => '30 free credits every month. Paid plans unlock private case storage — upload your documents and every tool references them.',
|
'pricing_strip_sub' => '30 free credits every month. Paid plans unlock private case storage — upload your documents and every tool references them.',
|
||||||
'pricing_strip_cta' => 'See all plans & pricing →',
|
'pricing_strip_cta' => 'See all plans & pricing →',
|
||||||
|
// ── Nav + account + dashboard i18n (added 2026-05-23) ──────────
|
||||||
|
'nav_tools' => 'Tools',
|
||||||
|
'nav_dashboard' => 'Dashboard',
|
||||||
|
'nav_account' => 'Account',
|
||||||
|
'nav_login' => 'Log in',
|
||||||
|
'nav_logout' => 'Log out',
|
||||||
|
'credits_available' => 'Available credits',
|
||||||
|
'credits_monthly' => 'monthly',
|
||||||
|
'credits_bonus' => 'bonus',
|
||||||
|
'details_link' => 'Details',
|
||||||
|
'my_case' => 'My case',
|
||||||
|
'build_your_case' => 'Build your own case',
|
||||||
|
'upload_documents' => 'Upload documents',
|
||||||
|
'upgrade_from_plus' => 'Available from Plus NOK 129/month',
|
||||||
|
'my_corpus' => 'My corpus',
|
||||||
|
'open_corpus' => 'Open corpus',
|
||||||
|
'account_title' => 'Account',
|
||||||
|
'account_credits' => 'Credits & plan',
|
||||||
|
'account_profile' => 'Profile',
|
||||||
|
'account_team' => 'Team',
|
||||||
|
'account_usage' => 'Usage',
|
||||||
|
'renewal_date' => 'Next renewal',
|
||||||
|
'trial_active_label' => 'Trial active',
|
||||||
|
'trial_days_left' => 'days left',
|
||||||
|
'earn_credits_eyebrow'=> 'Earn 25 extra credits',
|
||||||
|
'survey_cta_text' => 'Answer 5 short questions — no sales pitch, just research.',
|
||||||
|
'survey_btn' => 'Take the survey',
|
||||||
|
'login_method_sso' => 'Google SSO',
|
||||||
|
'login_method_email' => 'Email & password',
|
||||||
|
'team_single_sso' => 'Single-user account (SSO)',
|
||||||
|
'usage_credits_used' => 'Credits used this month',
|
||||||
|
'usage_storage_used' => 'Storage used',
|
||||||
|
'usage_log_coming' => 'Detailed activity log coming soon',
|
||||||
|
'account_not_auth' => 'You must be logged in to view your account.',
|
||||||
],
|
],
|
||||||
'no' => [
|
'no' => [
|
||||||
'meta_title' => 'Do Better Norge - juridiske AI-verktøy',
|
'meta_title' => 'Do Better Norge - juridiske AI-verktøy',
|
||||||
@@ -383,6 +417,40 @@ function dbnToolsTranslations(): array
|
|||||||
'pricing_strip_title' => 'Start gratis. Oppgrader når du trenger din egen sak.',
|
'pricing_strip_title' => 'Start gratis. Oppgrader når du trenger din egen sak.',
|
||||||
'pricing_strip_sub' => '30 gratis kreditter hver måned. Betalte planer låser opp privat sakslager — last opp dokumentene og alle verktøy refererer til dem.',
|
'pricing_strip_sub' => '30 gratis kreditter hver måned. Betalte planer låser opp privat sakslager — last opp dokumentene og alle verktøy refererer til dem.',
|
||||||
'pricing_strip_cta' => 'Se alle planer og priser →',
|
'pricing_strip_cta' => 'Se alle planer og priser →',
|
||||||
|
// ── Nav + account + dashboard i18n ──────────────────────────────
|
||||||
|
'nav_tools' => 'Verktøy',
|
||||||
|
'nav_dashboard' => 'Oversikt',
|
||||||
|
'nav_account' => 'Konto',
|
||||||
|
'nav_login' => 'Logg inn',
|
||||||
|
'nav_logout' => 'Logg ut',
|
||||||
|
'credits_available' => 'Tilgjengelige kreditter',
|
||||||
|
'credits_monthly' => 'månedlige',
|
||||||
|
'credits_bonus' => 'bonus',
|
||||||
|
'details_link' => 'Detaljer',
|
||||||
|
'my_case' => 'Min sak',
|
||||||
|
'build_your_case' => 'Bygg din egen sak',
|
||||||
|
'upload_documents' => 'Last opp dokumenter',
|
||||||
|
'upgrade_from_plus' => 'Tilgjengelig fra Plus 129 kr/mnd',
|
||||||
|
'my_corpus' => 'Min korpus',
|
||||||
|
'open_corpus' => 'Åpne korpus',
|
||||||
|
'account_title' => 'Konto',
|
||||||
|
'account_credits' => 'Kreditter og plan',
|
||||||
|
'account_profile' => 'Profil',
|
||||||
|
'account_team' => 'Team',
|
||||||
|
'account_usage' => 'Bruk',
|
||||||
|
'renewal_date' => 'Neste fornyelse',
|
||||||
|
'trial_active_label' => 'Prøveperiode aktiv',
|
||||||
|
'trial_days_left' => 'dager igjen',
|
||||||
|
'earn_credits_eyebrow'=> 'Tjen 25 ekstra kreditter',
|
||||||
|
'survey_cta_text' => 'Svar på 5 korte spørsmål — ingen salgspitch, bare research.',
|
||||||
|
'survey_btn' => 'Ta undersøkelsen',
|
||||||
|
'login_method_sso' => 'Google SSO',
|
||||||
|
'login_method_email' => 'E-post og passord',
|
||||||
|
'team_single_sso' => 'Enkeltbrukerkonto (SSO)',
|
||||||
|
'usage_credits_used' => 'Kreditter brukt denne måneden',
|
||||||
|
'usage_storage_used' => 'Lagring brukt',
|
||||||
|
'usage_log_coming' => 'Detaljert aktivitetslogg kommer snart',
|
||||||
|
'account_not_auth' => 'Du må være innlogget for å se kontoen din.',
|
||||||
],
|
],
|
||||||
'uk' => [
|
'uk' => [
|
||||||
'meta_title' => 'Do Better Norge - юридичні AI інструменти',
|
'meta_title' => 'Do Better Norge - юридичні AI інструменти',
|
||||||
@@ -541,6 +609,40 @@ function dbnToolsTranslations(): array
|
|||||||
'pricing_strip_title' => 'Починайте безкоштовно. Оновлюйтеся, коли потрібна власна справа.',
|
'pricing_strip_title' => 'Починайте безкоштовно. Оновлюйтеся, коли потрібна власна справа.',
|
||||||
'pricing_strip_sub' => '30 безкоштовних кредитів щомісяця. Платні плани відкривають приватне сховище справи — завантажте документи, і кожен інструмент посилається на них.',
|
'pricing_strip_sub' => '30 безкоштовних кредитів щомісяця. Платні плани відкривають приватне сховище справи — завантажте документи, і кожен інструмент посилається на них.',
|
||||||
'pricing_strip_cta' => 'Переглянути всі плани та ціни →',
|
'pricing_strip_cta' => 'Переглянути всі плани та ціни →',
|
||||||
|
// ── Nav + account + dashboard i18n ──────────────────────────────
|
||||||
|
'nav_tools' => 'Інструменти',
|
||||||
|
'nav_dashboard' => 'Огляд',
|
||||||
|
'nav_account' => 'Обліковий запис',
|
||||||
|
'nav_login' => 'Увійти',
|
||||||
|
'nav_logout' => 'Вийти',
|
||||||
|
'credits_available' => 'Доступні кредити',
|
||||||
|
'credits_monthly' => 'щомісячні',
|
||||||
|
'credits_bonus' => 'бонусні',
|
||||||
|
'details_link' => 'Деталі',
|
||||||
|
'my_case' => 'Моя справа',
|
||||||
|
'build_your_case' => 'Побудуйте власну справу',
|
||||||
|
'upload_documents' => 'Завантажити документи',
|
||||||
|
'upgrade_from_plus' => 'Доступно з Plus NOK 129/місяць',
|
||||||
|
'my_corpus' => 'Мій корпус',
|
||||||
|
'open_corpus' => 'Відкрити корпус',
|
||||||
|
'account_title' => 'Обліковий запис',
|
||||||
|
'account_credits' => 'Кредити та план',
|
||||||
|
'account_profile' => 'Профіль',
|
||||||
|
'account_team' => 'Команда',
|
||||||
|
'account_usage' => 'Використання',
|
||||||
|
'renewal_date' => 'Наступне оновлення',
|
||||||
|
'trial_active_label' => 'Пробний період активний',
|
||||||
|
'trial_days_left' => 'днів залишилось',
|
||||||
|
'earn_credits_eyebrow'=> 'Заробіть 25 додаткових кредитів',
|
||||||
|
'survey_cta_text' => 'Дайте відповідь на 5 коротких запитань — без реклами, лише дослідження.',
|
||||||
|
'survey_btn' => 'Пройти опитування',
|
||||||
|
'login_method_sso' => 'Google SSO',
|
||||||
|
'login_method_email' => 'Email та пароль',
|
||||||
|
'team_single_sso' => 'Одноосібний обліковий запис (SSO)',
|
||||||
|
'usage_credits_used' => 'Кредити використано цього місяця',
|
||||||
|
'usage_storage_used' => 'Використано сховища',
|
||||||
|
'usage_log_coming' => 'Детальний журнал активності незабаром',
|
||||||
|
'account_not_auth' => 'Увійдіть, щоб переглянути свій обліковий запис.',
|
||||||
],
|
],
|
||||||
'pl' => [
|
'pl' => [
|
||||||
'meta_title' => 'Do Better Norge - prawne narzędzia AI',
|
'meta_title' => 'Do Better Norge - prawne narzędzia AI',
|
||||||
@@ -699,6 +801,40 @@ function dbnToolsTranslations(): array
|
|||||||
'pricing_strip_title' => 'Zacznij bezpłatnie. Rozszerz, gdy potrzebujesz własnej sprawy.',
|
'pricing_strip_title' => 'Zacznij bezpłatnie. Rozszerz, gdy potrzebujesz własnej sprawy.',
|
||||||
'pricing_strip_sub' => '30 bezpłatnych kredytów miesięcznie. Płatne plany odblokują prywatne przechowywanie sprawy — prześlij dokumenty, a każde narzędzie do nich się odwoła.',
|
'pricing_strip_sub' => '30 bezpłatnych kredytów miesięcznie. Płatne plany odblokują prywatne przechowywanie sprawy — prześlij dokumenty, a każde narzędzie do nich się odwoła.',
|
||||||
'pricing_strip_cta' => 'Zobacz wszystkie plany i cennik →',
|
'pricing_strip_cta' => 'Zobacz wszystkie plany i cennik →',
|
||||||
|
// ── Nav + account + dashboard i18n ──────────────────────────────
|
||||||
|
'nav_tools' => 'Narzędzia',
|
||||||
|
'nav_dashboard' => 'Przegląd',
|
||||||
|
'nav_account' => 'Konto',
|
||||||
|
'nav_login' => 'Zaloguj',
|
||||||
|
'nav_logout' => 'Wyloguj',
|
||||||
|
'credits_available' => 'Dostępne kredyty',
|
||||||
|
'credits_monthly' => 'miesięczne',
|
||||||
|
'credits_bonus' => 'bonusowe',
|
||||||
|
'details_link' => 'Szczegóły',
|
||||||
|
'my_case' => 'Moja sprawa',
|
||||||
|
'build_your_case' => 'Zbuduj własną sprawę',
|
||||||
|
'upload_documents' => 'Prześlij dokumenty',
|
||||||
|
'upgrade_from_plus' => 'Dostępne od Plus NOK 129/miesiąc',
|
||||||
|
'my_corpus' => 'Mój korpus',
|
||||||
|
'open_corpus' => 'Otwórz korpus',
|
||||||
|
'account_title' => 'Konto',
|
||||||
|
'account_credits' => 'Kredyty i plan',
|
||||||
|
'account_profile' => 'Profil',
|
||||||
|
'account_team' => 'Zespół',
|
||||||
|
'account_usage' => 'Użycie',
|
||||||
|
'renewal_date' => 'Następne odnowienie',
|
||||||
|
'trial_active_label' => 'Okres próbny aktywny',
|
||||||
|
'trial_days_left' => 'dni pozostało',
|
||||||
|
'earn_credits_eyebrow'=> 'Zdobądź 25 dodatkowych kredytów',
|
||||||
|
'survey_cta_text' => 'Odpowiedz na 5 krótkich pytań — bez reklam, tylko badania.',
|
||||||
|
'survey_btn' => 'Wypełnij ankietę',
|
||||||
|
'login_method_sso' => 'Google SSO',
|
||||||
|
'login_method_email' => 'Email i hasło',
|
||||||
|
'team_single_sso' => 'Konto jednoosobowe (SSO)',
|
||||||
|
'usage_credits_used' => 'Kredyty użyte w tym miesiącu',
|
||||||
|
'usage_storage_used' => 'Użyte miejsce',
|
||||||
|
'usage_log_coming' => 'Szczegółowy dziennik aktywności wkrótce',
|
||||||
|
'account_not_auth' => 'Musisz być zalogowany, aby zobaczyć swoje konto.',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-32
@@ -31,6 +31,7 @@ if ($layoutAuthUser !== null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$layoutReturnUrl = urlencode($_SERVER['REQUEST_URI'] ?? '/');
|
$layoutReturnUrl = urlencode($_SERVER['REQUEST_URI'] ?? '/');
|
||||||
|
// $layoutAuthUser / $layoutUserDisplay kept for backwards compat (now also used by nav.php internally)
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="<?= htmlspecialchars($uiLang) ?>">
|
<html lang="<?= htmlspecialchars($uiLang) ?>">
|
||||||
@@ -58,6 +59,8 @@ window.DBN_FREE_TIER_BALANCE = <?= $layoutFreeTierBalance ?>;
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php include __DIR__ . '/nav.php'; ?>
|
||||||
|
|
||||||
<?php if ($layoutIsGuest): ?>
|
<?php if ($layoutIsGuest): ?>
|
||||||
<div id="guestBanner" class="guest-banner" role="alert" aria-live="polite">
|
<div id="guestBanner" class="guest-banner" role="alert" aria-live="polite">
|
||||||
<span>Du er ikke innlogget — verktøyene krever konto for å fungere.</span>
|
<span>Du er ikke innlogget — verktøyene krever konto for å fungere.</span>
|
||||||
@@ -67,38 +70,6 @@ window.DBN_FREE_TIER_BALANCE = <?= $layoutFreeTierBalance ?>;
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<main id="appShell" class="app-shell">
|
<main id="appShell" class="app-shell">
|
||||||
<header class="topbar">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow"><?= htmlspecialchars(dbnToolsT('brand_line', $uiLang)) ?></p>
|
|
||||||
<h1><?= htmlspecialchars(dbnToolsT('suite_title', $uiLang)) ?> <span class="title-mark">.</span> <?= htmlspecialchars(dbnToolsT('workspace_title', $uiLang)) ?></h1>
|
|
||||||
<div class="case-no">
|
|
||||||
<span class="pulse"></span>
|
|
||||||
<span>family-legal</span>
|
|
||||||
<span class="case-sep">.</span>
|
|
||||||
<span><?= htmlspecialchars(dbnToolsT('retention', $uiLang)) ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="topbar-actions">
|
|
||||||
<nav class="shell-lang-switcher" aria-label="Language">
|
|
||||||
<?php foreach (dbnToolsSupportedLanguages() as $langCode): ?>
|
|
||||||
<a href="<?= htmlspecialchars($langPath . '?lang=' . $langCode) ?>" class="<?= $langCode === $uiLang ? 'is-active' : '' ?>"><?= htmlspecialchars(dbnToolsLanguageLabel($langCode)) ?></a>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</nav>
|
|
||||||
<?php if ($layoutIsGuest): ?>
|
|
||||||
<a href="/?return=<?= $layoutReturnUrl ?>" class="secondary-button" style="text-decoration:none;">Logg inn</a>
|
|
||||||
<?php else: ?>
|
|
||||||
<a href="/dashboard/" class="secondary-button" style="text-decoration:none;">📚 Min korpus</a>
|
|
||||||
<span id="healthPill" class="status-pill"><?= htmlspecialchars(dbnToolsT('session_active', $uiLang)) ?></span>
|
|
||||||
<button id="healthButton" class="secondary-button" type="button"><?= htmlspecialchars(dbnToolsT('health', $uiLang)) ?></button>
|
|
||||||
<span class="topbar-user">
|
|
||||||
<?php if ($layoutUserDisplay !== ''): ?>
|
|
||||||
<span class="topbar-user__name" title="<?= htmlspecialchars($layoutAuthUser['email'] ?? '') ?>"><?= htmlspecialchars($layoutUserDisplay) ?></span>
|
|
||||||
<?php endif; ?>
|
|
||||||
<a href="/api/logout.php" class="topbar-user__logout">Logg ut</a>
|
|
||||||
</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section class="manifesto" role="banner">
|
<section class="manifesto" role="banner">
|
||||||
<div class="manifesto-copy">
|
<div class="manifesto-copy">
|
||||||
|
|||||||
@@ -73,25 +73,8 @@ window.DBN_DASHBOARD = {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<?php include __DIR__ . '/nav.php'; ?>
|
||||||
<div class="dash-shell">
|
<div class="dash-shell">
|
||||||
<header class="dash-topbar" role="banner">
|
|
||||||
<a class="dash-brand" href="/dashboard/">
|
|
||||||
<span class="dash-brand__mark">⚖</span>
|
|
||||||
<span class="dash-brand__text">
|
|
||||||
<strong>Min korpus</strong>
|
|
||||||
<small>Do Better Norge</small>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<nav class="dash-topbar__tools" aria-label="Tools">
|
|
||||||
<a href="/dashboard.php" class="dash-topbar__link">← Tilbake til verktøy</a>
|
|
||||||
</nav>
|
|
||||||
<div class="dash-topbar__user">
|
|
||||||
<?php if ($dashUserDisplay !== ''): ?>
|
|
||||||
<span class="dash-topbar__username" title="<?= htmlspecialchars($dashAuthUser['email'] ?? '') ?>"><?= htmlspecialchars($dashUserDisplay) ?></span>
|
|
||||||
<?php endif; ?>
|
|
||||||
<a href="/api/logout.php" class="dash-topbar__logout">Logg ut</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="dash-layout">
|
<div class="dash-layout">
|
||||||
<nav class="dash-sidebar" aria-label="Dashboard sections">
|
<nav class="dash-sidebar" aria-label="Dashboard sections">
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Unified site navbar — included by layout.php, layout_dashboard.php, and standalone pages.
|
||||||
|
* Assumes bootstrap.php (and i18n.php) has already been required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$_navGuest = !dbnToolsIsAuthenticated();
|
||||||
|
$_navAuth = $_navGuest ? null : dbnToolsAuthenticatedUser();
|
||||||
|
$_navEmail = (string)($_navAuth['email'] ?? '');
|
||||||
|
$_navUser = $_navEmail !== '' ? (strstr($_navEmail, '@', true) ?: $_navEmail) : '';
|
||||||
|
$_navLang = dbnToolsCurrentLanguage();
|
||||||
|
$_navTools = dbnToolsLaunchedTools($_navLang);
|
||||||
|
$_navPath = strtok((string)($_SERVER['REQUEST_URI'] ?? '/'), '?') ?: '/';
|
||||||
|
$_navOnDash = str_starts_with($_navPath, '/dashboard');
|
||||||
|
$_navReturnUrl = urlencode($_navPath);
|
||||||
|
?>
|
||||||
|
<nav class="dbn-nav" role="navigation" aria-label="<?= htmlspecialchars(dbnToolsT('suite_title', $_navLang)) ?>">
|
||||||
|
<a class="dbn-nav__brand" href="/dashboard.php">
|
||||||
|
<span class="dbn-nav__brandmark" aria-hidden="true">⚖</span>
|
||||||
|
<span class="dbn-nav__brandname">Do Better Norge</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="dbn-nav__links" role="menubar">
|
||||||
|
<div class="dbn-nav__dropdown" data-nav-dropdown>
|
||||||
|
<button class="dbn-nav__link dbn-nav__dropper"
|
||||||
|
type="button"
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="navToolsPanel">
|
||||||
|
<?= htmlspecialchars(dbnToolsT('nav_tools', $_navLang)) ?>
|
||||||
|
<span class="dbn-nav__caret" aria-hidden="true">▾</span>
|
||||||
|
</button>
|
||||||
|
<div class="dbn-nav__panel" id="navToolsPanel" role="menu" aria-label="<?= htmlspecialchars(dbnToolsT('nav_tools', $_navLang)) ?>">
|
||||||
|
<?php foreach ($_navTools as $slug => $item): ?>
|
||||||
|
<a class="dbn-nav__panel-item" href="<?= htmlspecialchars($item['url']) ?>" role="menuitem">
|
||||||
|
<span class="dbn-nav__panel-badge" aria-hidden="true"><?= htmlspecialchars($item['icon']) ?></span>
|
||||||
|
<span class="dbn-nav__panel-label"><?= htmlspecialchars($item['label']) ?></span>
|
||||||
|
<span class="dbn-nav__panel-sub"><?= htmlspecialchars($item['sub']) ?></span>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class="dbn-nav__link<?= $_navOnDash ? ' is-active' : '' ?>"
|
||||||
|
href="/dashboard.php"
|
||||||
|
role="menuitem"
|
||||||
|
<?= $_navOnDash ? 'aria-current="page"' : '' ?>>
|
||||||
|
<?= htmlspecialchars(dbnToolsT('nav_dashboard', $_navLang)) ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dbn-nav__right">
|
||||||
|
<nav class="dbn-nav__langs" aria-label="Language">
|
||||||
|
<?php foreach (dbnToolsSupportedLanguages() as $lc): ?>
|
||||||
|
<a href="<?= htmlspecialchars($_navPath . '?lang=' . $lc) ?>"
|
||||||
|
class="dbn-nav__lang<?= $lc === $_navLang ? ' is-active' : '' ?>"
|
||||||
|
hreflang="<?= htmlspecialchars($lc) ?>"
|
||||||
|
aria-label="<?= htmlspecialchars(dbnToolsLanguageName($lc)) ?>"
|
||||||
|
title="<?= htmlspecialchars(dbnToolsLanguageName($lc)) ?>"><?= htmlspecialchars(strtoupper($lc)) ?></a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<?php if ($_navGuest): ?>
|
||||||
|
<a href="/?return=<?= $_navReturnUrl ?>" class="dbn-nav__login">
|
||||||
|
<?= htmlspecialchars(dbnToolsT('nav_login', $_navLang)) ?>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a class="dbn-nav__account-link" href="/account.php" title="<?= htmlspecialchars($_navEmail) ?>">
|
||||||
|
<?php if ($_navUser !== ''): ?>
|
||||||
|
<span class="dbn-nav__username"><?= htmlspecialchars($_navUser) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<span class="dbn-nav__account-badge"><?= htmlspecialchars(dbnToolsT('nav_account', $_navLang)) ?></span>
|
||||||
|
</a>
|
||||||
|
<a href="/api/logout.php" class="dbn-nav__logout">
|
||||||
|
<?= htmlspecialchars(dbnToolsT('nav_logout', $_navLang)) ?>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var dropdowns = document.querySelectorAll('[data-nav-dropdown]');
|
||||||
|
dropdowns.forEach(function (dd) {
|
||||||
|
var btn = dd.querySelector('.dbn-nav__dropper');
|
||||||
|
var panel = dd.querySelector('.dbn-nav__panel');
|
||||||
|
if (!btn || !panel) return;
|
||||||
|
|
||||||
|
function open() { dd.classList.add('is-open'); btn.setAttribute('aria-expanded', 'true'); }
|
||||||
|
function close() { dd.classList.remove('is-open'); btn.setAttribute('aria-expanded', 'false'); }
|
||||||
|
function toggle() { dd.classList.contains('is-open') ? close() : open(); }
|
||||||
|
|
||||||
|
btn.addEventListener('click', function (e) { e.stopPropagation(); toggle(); });
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
if (!dd.contains(e.target)) close();
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Escape') close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user