Overhaul dashboard: account bar, enhanced tool cards with MCP slugs, MCP quick-start, tool reference section
- Replace manifesto section with compact account overview bar (tier badge, credits breakdown, next refill/billing date, upgrade/manage/top-up CTAs) - Convert tool cards from <a> to keyboard-accessible <div> with footer bar showing Open link, About link (advocate/timeline/korrespond), MCP slug copy button - Add collapsible MCP quick-start section: token prefix fetch, stdio config, remote HTTP config, copy buttons, link to full mcp.php docs - Add 3-column tool reference section for Advocate / Timeline / Korrespond with about/guide/tech links, description, and copyable MCP slug - All new sections fully localised: en / no / uk / pl Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+521
-74
@@ -14,19 +14,228 @@ $tools = dbnToolsLaunchedTools($uiLang);
|
||||
$workbench = dbnToolsWorkbenchMeta($uiLang);
|
||||
$langPath = '/dashboard.php';
|
||||
|
||||
// Tier + balance for SSO users (CaveauAI sessions get no panel)
|
||||
$dashIsSso = dbnToolsIsFreeTier();
|
||||
$dashTier = $dashIsSso ? FreeTier::tier((int)$_SESSION['dbn_tools_sso_uid']) : 'caveau';
|
||||
$dashDetail = $dashIsSso ? FreeTier::balanceDetail((int)$_SESSION['dbn_tools_sso_uid']) : null;
|
||||
$dashUserId = $dashIsSso ? (int)($_SESSION['dbn_tools_sso_uid'] ?? 0) : 0;
|
||||
$dashTier = $dashIsSso ? FreeTier::tier($dashUserId) : 'caveau';
|
||||
$dashDetail = $dashIsSso ? FreeTier::balanceDetail($dashUserId) : null;
|
||||
|
||||
$tierLabels = [
|
||||
'free' => ['Gratis', '#f3f4f6', '#374151'],
|
||||
'free' => ['Free', '#f3f4f6', '#374151'],
|
||||
'plus' => ['Plus', '#ddd6fe', '#5b21b6'],
|
||||
'pro' => ['Pro Familie', '#bfdbfe', '#1e40af'],
|
||||
'caveau' => ['CaveauAI', '#d1fae5', '#065f46'],
|
||||
];
|
||||
$tierLabel = $tierLabels[$dashTier] ?? ['CaveauAI', '#d1fae5', '#065f46'];
|
||||
$tierLabel = $tierLabels[$dashTier] ?? $tierLabels['free'];
|
||||
$showSurveyCta = $dashIsSso && empty($dashDetail['survey_completed_at']);
|
||||
|
||||
// User display name
|
||||
$dashAuthUser = dbnToolsAuthenticatedUser();
|
||||
$dashEmail = '';
|
||||
if ($dashAuthUser !== null) {
|
||||
$e = (string)($dashAuthUser['email'] ?? '');
|
||||
$dashEmail = strstr($e, '@', true) ?: $e;
|
||||
}
|
||||
|
||||
// Next refill / billing date
|
||||
$dashNextBilling = '';
|
||||
$dashNextBillingKey = 'next_refill';
|
||||
if ($dashIsSso && $dashDetail) {
|
||||
if (!empty($dashDetail['subscription_period_end'])) {
|
||||
$ts = strtotime((string)$dashDetail['subscription_period_end']);
|
||||
$dashNextBilling = $ts ? date('j M Y', $ts) : '';
|
||||
$dashNextBillingKey = 'next_billing';
|
||||
} else {
|
||||
$m = (int)date('m'); $y = (int)date('Y');
|
||||
if ($m === 12) { $m = 1; $y++; } else { $m++; }
|
||||
$dashNextBilling = date('j M Y', mktime(0, 0, 0, $m, 1, $y));
|
||||
}
|
||||
}
|
||||
|
||||
// Tool → MCP slug
|
||||
$toolMcpSlugs = [
|
||||
'transcribe' => 'dbn.transcribe_audio',
|
||||
'timeline' => 'dbn.timeline',
|
||||
'redact' => 'dbn.redact',
|
||||
'korrespond' => 'dbn.korrespond',
|
||||
'barnevernet' => 'dbn.barnevernet_analyze',
|
||||
'advocate' => 'dbn.advocate_brief',
|
||||
'deep-research' => 'dbn.deep_research',
|
||||
'discrepancy' => 'dbn.discrepancy_find',
|
||||
'corpus' => 'dbn.list_documents',
|
||||
'citations' => 'dbn.citation_graph',
|
||||
];
|
||||
|
||||
// Tool → About page
|
||||
$toolAboutPages = [
|
||||
'advocate' => '/advocate-about.php',
|
||||
'timeline' => '/timeline-about.php',
|
||||
'korrespond' => '/korrespond-about.php',
|
||||
];
|
||||
|
||||
// Localized strings for new sections
|
||||
$dashL = [
|
||||
'en' => [
|
||||
'acct_header' => 'Account',
|
||||
'signed_in_as' => 'Signed in as',
|
||||
'manage_plan' => 'Manage plan',
|
||||
'upgrade_plan' => 'Upgrade plan',
|
||||
'top_up' => 'Top up credits',
|
||||
'next_refill' => 'Credits refill',
|
||||
'next_billing' => 'Next billing',
|
||||
'monthly_quota' => 'monthly quota',
|
||||
'trial_badge' => 'Trial — %d days left',
|
||||
'about_link' => 'About',
|
||||
'mcp_copy_slug' => 'Copy MCP slug',
|
||||
'mcp_section' => 'Developers & MCP',
|
||||
'mcp_desc' => 'Connect Claude Desktop, Claude Code, Cursor, or any MCP-compatible client to the full tool suite.',
|
||||
'mcp_token_lbl' => 'API token',
|
||||
'mcp_no_token' => 'No active token',
|
||||
'mcp_copy' => 'Copy',
|
||||
'mcp_not_avail' => 'MCP tokens require Plus or Pro — upgrade to connect AI clients.',
|
||||
'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)',
|
||||
'mcp_remote_lbl' => 'Remote HTTP — Cursor, Zed, Windsurf',
|
||||
'mcp_full_docs' => 'Full setup guide & token management →',
|
||||
'tool_ref_title' => 'Tool reference',
|
||||
'tool_ref_sub' => 'Deep-dive docs for the three flagship tools.',
|
||||
'guide_link' => 'Guide',
|
||||
'tech_link' => 'Technical',
|
||||
'open_tool' => 'Open tool',
|
||||
],
|
||||
'no' => [
|
||||
'acct_header' => 'Konto',
|
||||
'signed_in_as' => 'Innlogget som',
|
||||
'manage_plan' => 'Administrer plan',
|
||||
'upgrade_plan' => 'Oppgrader plan',
|
||||
'top_up' => 'Kjøp kreditter',
|
||||
'next_refill' => 'Kreditter fornyes',
|
||||
'next_billing' => 'Neste fakturering',
|
||||
'monthly_quota' => 'månedlig kvote',
|
||||
'trial_badge' => 'Prøveperiode — %d dager igjen',
|
||||
'about_link' => 'Om',
|
||||
'mcp_copy_slug' => 'Kopier MCP-slug',
|
||||
'mcp_section' => 'Utviklere & MCP',
|
||||
'mcp_desc' => 'Koble Claude Desktop, Claude Code, Cursor eller annen MCP-klient til hele verktøysuiten.',
|
||||
'mcp_token_lbl' => 'API-token',
|
||||
'mcp_no_token' => 'Ingen aktiv token',
|
||||
'mcp_copy' => 'Kopier',
|
||||
'mcp_not_avail' => 'MCP-tokens krever Plus eller Pro — oppgrader for å koble til AI-klienter.',
|
||||
'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)',
|
||||
'mcp_remote_lbl' => 'Ekstern HTTP — Cursor, Zed, Windsurf',
|
||||
'mcp_full_docs' => 'Full oppsettsguide & token-administrasjon →',
|
||||
'tool_ref_title' => 'Verktøyreferanse',
|
||||
'tool_ref_sub' => 'Dybdedokumentasjon for de tre flaggskipverktøyene.',
|
||||
'guide_link' => 'Guide',
|
||||
'tech_link' => 'Teknisk',
|
||||
'open_tool' => 'Åpne verktøy',
|
||||
],
|
||||
'uk' => [
|
||||
'acct_header' => 'Обліковий запис',
|
||||
'signed_in_as' => 'Ввійшли як',
|
||||
'manage_plan' => 'Управляти планом',
|
||||
'upgrade_plan' => 'Покращити план',
|
||||
'top_up' => 'Поповнити кредити',
|
||||
'next_refill' => 'Кредити поновлюються',
|
||||
'next_billing' => 'Наступне списання',
|
||||
'monthly_quota' => 'місячна квота',
|
||||
'trial_badge' => 'Пробний — %d дн. залишилось',
|
||||
'about_link' => 'Про',
|
||||
'mcp_copy_slug' => 'Копіювати MCP-ідентифікатор',
|
||||
'mcp_section' => 'Розробники & MCP',
|
||||
'mcp_desc' => 'Підключайте Claude Desktop, Claude Code, Cursor або будь-який MCP-клієнт до повного набору інструментів.',
|
||||
'mcp_token_lbl' => 'API-токен',
|
||||
'mcp_no_token' => 'Немає активного токена',
|
||||
'mcp_copy' => 'Копіювати',
|
||||
'mcp_not_avail' => 'MCP-токени потребують Plus або Pro — оновіться для підключення AI-клієнтів.',
|
||||
'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)',
|
||||
'mcp_remote_lbl' => 'Віддалений HTTP — Cursor, Zed, Windsurf',
|
||||
'mcp_full_docs' => 'Повна документація та управління токенами →',
|
||||
'tool_ref_title' => 'Довідник інструментів',
|
||||
'tool_ref_sub' => 'Детальна документація трьох флагманських інструментів.',
|
||||
'guide_link' => 'Посібник',
|
||||
'tech_link' => 'Технічний',
|
||||
'open_tool' => 'Відкрити',
|
||||
],
|
||||
'pl' => [
|
||||
'acct_header' => 'Konto',
|
||||
'signed_in_as' => 'Zalogowany jako',
|
||||
'manage_plan' => 'Zarządzaj planem',
|
||||
'upgrade_plan' => 'Ulepsz plan',
|
||||
'top_up' => 'Doładuj kredyty',
|
||||
'next_refill' => 'Kredyty odnawiają się',
|
||||
'next_billing' => 'Następne rozliczenie',
|
||||
'monthly_quota' => 'miesięczny limit',
|
||||
'trial_badge' => 'Próba — %d dni pozostało',
|
||||
'about_link' => 'O narzędziu',
|
||||
'mcp_copy_slug' => 'Kopiuj identyfikator MCP',
|
||||
'mcp_section' => 'Deweloperzy & MCP',
|
||||
'mcp_desc' => 'Podłącz Claude Desktop, Claude Code, Cursor lub dowolnego klienta MCP do pełnego zestawu narzędzi.',
|
||||
'mcp_token_lbl' => 'Token API',
|
||||
'mcp_no_token' => 'Brak aktywnego tokenu',
|
||||
'mcp_copy' => 'Kopiuj',
|
||||
'mcp_not_avail' => 'Tokeny MCP wymagają Plus lub Pro — zaktualizuj, aby połączyć klientów AI.',
|
||||
'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)',
|
||||
'mcp_remote_lbl' => 'Zdalny HTTP — Cursor, Zed, Windsurf',
|
||||
'mcp_full_docs' => 'Pełna dokumentacja i zarządzanie tokenami →',
|
||||
'tool_ref_title' => 'Dokumentacja narzędzi',
|
||||
'tool_ref_sub' => 'Szczegółowa dokumentacja trzech flagowych narzędzi.',
|
||||
'guide_link' => 'Poradnik',
|
||||
'tech_link' => 'Techniczny',
|
||||
'open_tool' => 'Otwórz',
|
||||
],
|
||||
];
|
||||
$dl = $dashL[$uiLang] ?? $dashL['en'];
|
||||
|
||||
// Three flagship tools reference data
|
||||
$toolRefCards = [
|
||||
'advocate' => [
|
||||
'icon' => '⚖️',
|
||||
'mcp' => 'dbn.advocate_brief',
|
||||
'about' => '/advocate-about.php',
|
||||
'guide' => '/advocate-guide.php',
|
||||
'tech' => '/advocate-tech.php',
|
||||
'tool' => '/advocate.php',
|
||||
'name' => ['en' => 'Advocate', 'no' => 'Advokat', 'uk' => 'Адвокат', 'pl' => 'Adwokat'],
|
||||
'desc' => [
|
||||
'en' => 'AI-generated partisan brief grounded in Lovdata + ECHR, framed for your party role. 9 role presets, 220 K+ verified legal passages.',
|
||||
'no' => 'AI-generert partisk prosedyre basert på Lovdata + EMD, formulert for din partsrolle. 9 roller, 220 K+ verifiserte rettskilder.',
|
||||
'uk' => 'ШІ-стислий виклад позиції на основі Lovdata + ЄСПЛ. 9 ролей, 220 K+ верифікованих правових пасажів.',
|
||||
'pl' => 'Brief AI oparty na Lovdata + ETPC, z Twojej perspektywy. 9 ról, 220 K+ zweryfikowanych źródeł prawnych.',
|
||||
],
|
||||
],
|
||||
'timeline' => [
|
||||
'icon' => '📅',
|
||||
'mcp' => 'dbn.timeline',
|
||||
'about' => '/timeline-about.php',
|
||||
'guide' => '/timeline-guide.php',
|
||||
'tech' => '/timeline-tech.php',
|
||||
'tool' => '/timeline.php',
|
||||
'name' => ['en' => 'Timeline', 'no' => 'Tidslinje', 'uk' => 'Хронологія', 'pl' => 'Oś czasu'],
|
||||
'desc' => [
|
||||
'en' => 'Extract every key date from case documents into a confidence-scored chronology. Recognises 12+ Norwegian date formats, 5 event types.',
|
||||
'no' => 'Trekk ut alle viktige datoer til en konfidensscorert kronologi. Gjenkjenner 12+ norske datoformater og 5 hendelsestyper.',
|
||||
'uk' => 'Витягуйте ключові дати з документів справи. 12+ форматів, 5 типів подій, оцінка достовірності.',
|
||||
'pl' => 'Wyodrębnij daty z dokumentów sprawy w ocenioną chronologię. 12+ formatów dat, 5 typów zdarzeń.',
|
||||
],
|
||||
],
|
||||
'korrespond' => [
|
||||
'icon' => '✉️',
|
||||
'mcp' => 'dbn.korrespond',
|
||||
'about' => '/korrespond-about.php',
|
||||
'guide' => '/korrespond-guide.php',
|
||||
'tech' => '/korrespond-tech.php',
|
||||
'tool' => '/korrespond.php',
|
||||
'name' => ['en' => 'Korrespond', 'no' => 'Korrespond', 'uk' => 'Кореспонд', 'pl' => 'Korrespond'],
|
||||
'desc' => [
|
||||
'en' => 'Statute-grounded authority letters in Norwegian bokmål + your language, side-by-side. Hard-RAG: every § verified — no hallucinated citations.',
|
||||
'no' => 'Lovsikre myndighetsbrev på norsk bokmål + ditt språk, side ved side. Hard-RAG: alle §§ verifisert — ingen hallusinerte kilder.',
|
||||
'uk' => 'Офіційні листи, підкріплені законами, норвезькою та вашою мовою. Hard-RAG: кожен § перевірений.',
|
||||
'pl' => 'Pisma urzędowe oparte na przepisach, po norwesku i w Twoim języku. Hard-RAG: każdy § zweryfikowany.',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
require_once __DIR__ . '/includes/tool-svgs.php';
|
||||
$langSuffix = $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '';
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="<?= htmlspecialchars($uiLang) ?>">
|
||||
@@ -41,6 +250,19 @@ require_once __DIR__ . '/includes/tool-svgs.php';
|
||||
<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">
|
||||
<link rel="stylesheet" href="assets/css/dbn-tools-redesign.css">
|
||||
<style>
|
||||
/* ── Dashboard enhancements ───────────────────────────── */
|
||||
details.dash-mcp-details summary::-webkit-details-marker { display: none; }
|
||||
details.dash-mcp-details summary::marker { display: none; }
|
||||
.dash-chevron { transition: transform .2s ease; display: inline-block; }
|
||||
details.dash-mcp-details[open] .dash-chevron { transform: rotate(180deg); }
|
||||
/* card footer links always sit above the div onclick */
|
||||
.dash-card-footer a,
|
||||
.dash-card-footer button { position: relative; z-index: 1; }
|
||||
.dash-card-footer { padding-top: 0.65rem; margin-top: auto; border-top: 1px solid rgba(0,0,0,.07); display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
|
||||
/* pill badges on acct bar */
|
||||
.dash-tier-badge { display: inline-flex; align-items: center; font-size: .72rem; font-weight: 700; padding: 2px 10px; border-radius: 999px; text-transform: uppercase; letter-spacing: .06em; border: 1px solid currentColor; flex-shrink: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body data-authenticated="true" class="lt-app">
|
||||
<script>
|
||||
@@ -53,96 +275,245 @@ window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
|
||||
<div class="dbn-context-bar" role="note">
|
||||
<span class="dbn-context-bar__tag"><?= htmlspecialchars(dbnToolsT('context_bar_tag', $uiLang)) ?></span>
|
||||
<nav class="dbn-context-bar__links" aria-label="About">
|
||||
<a href="/why-ours.php<?= $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '' ?>"><?= htmlspecialchars(dbnToolsT('context_bar_why', $uiLang)) ?></a>
|
||||
<a href="/pricing.php<?= $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '' ?>"><?= htmlspecialchars(dbnToolsT('context_bar_pricing', $uiLang)) ?></a>
|
||||
<a href="/mcp-tool.php<?= $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '' ?>"><?= htmlspecialchars(dbnToolsT('context_bar_mcp', $uiLang)) ?></a>
|
||||
<a href="/privacy.php<?= $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : '' ?>"><?= htmlspecialchars(dbnToolsT('context_bar_privacy', $uiLang)) ?></a>
|
||||
<a href="/why-ours.php<?= $langSuffix ?>"><?= htmlspecialchars(dbnToolsT('context_bar_why', $uiLang)) ?></a>
|
||||
<a href="/pricing.php<?= $langSuffix ?>"><?= htmlspecialchars(dbnToolsT('context_bar_pricing', $uiLang)) ?></a>
|
||||
<a href="/mcp-tool.php<?= $langSuffix ?>"><?= htmlspecialchars(dbnToolsT('context_bar_mcp', $uiLang)) ?></a>
|
||||
<a href="/privacy.php<?= $langSuffix ?>"><?= htmlspecialchars(dbnToolsT('context_bar_privacy', $uiLang)) ?></a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<main id="appShell" class="app-shell dashboard-shell">
|
||||
|
||||
<?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;">
|
||||
<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;"><?= htmlspecialchars(dbnToolsT('credits_available', $uiLang)) ?></p>
|
||||
<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']; ?>
|
||||
<?= number_format($eff, 0, ',', ' ') ?>
|
||||
</p>
|
||||
<p style="margin:0; color:#6b7280; font-size:0.85rem;"><?= (int)$dashDetail['balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_monthly', $uiLang)) ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= $uiLang === 'no' ? 'forhåndsbetalte' : 'prepaid' ?> · <a href="/billing.php"><?= htmlspecialchars(dbnToolsT('details_link', $uiLang)) ?></a></p>
|
||||
<?php if ($dashIsSso && $dashDetail):
|
||||
$eff = (int)$dashDetail['balance'] + (int)$dashDetail['bonus_balance'];
|
||||
$isPaid = in_array($dashTier, ['plus', 'pro'], true);
|
||||
?>
|
||||
|
||||
<!-- ── Account overview bar ─────────────────────────────────────── -->
|
||||
<div style="background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:.9rem 1.25rem; margin:0 0 1.25rem; display:flex; align-items:center; justify-content:space-between; gap:.75rem; flex-wrap:wrap;">
|
||||
<div style="display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; min-width:0;">
|
||||
<span class="dash-tier-badge" style="background:<?= $tierLabel[1] ?>; color:<?= $tierLabel[2] ?>;"><?= htmlspecialchars($tierLabel[0]) ?></span>
|
||||
<?php if (!empty($dashDetail['trial_active'])): ?>
|
||||
<span class="dash-tier-badge" style="background:#fef3c7; color:#92400e;"><?= htmlspecialchars(sprintf($dl['trial_badge'], (int)$dashDetail['trial_days_remaining'])) ?></span>
|
||||
<?php endif; ?>
|
||||
<span style="font-size:.95rem; font-weight:700; color:#00205B; white-space:nowrap;"><?= number_format($eff) ?> <?= $uiLang === 'no' ? 'kred.' : 'credits' ?></span>
|
||||
<span style="color:#9ca3af; font-size:.8rem; white-space:nowrap;"><?= (int)$dashDetail['balance'] ?> <?= $uiLang === 'no' ? 'månedlige' : 'monthly' ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= $uiLang === 'no' ? 'forhåndsbetalt' : 'prepaid' ?></span>
|
||||
<?php if ($dashNextBilling): ?>
|
||||
<span style="color:#6b7280; font-size:.8rem; white-space:nowrap;"><?= htmlspecialchars($dl[$dashNextBillingKey]) ?>: <strong style="color:#374151;"><?= htmlspecialchars($dashNextBilling) ?></strong></span>
|
||||
<?php endif; ?>
|
||||
<?php if ($dashEmail): ?>
|
||||
<span style="color:#d1d5db; font-size:.8rem; white-space:nowrap; display:none;" class="dash-email-sep">·</span>
|
||||
<span style="color:#9ca3af; font-size:.8rem; white-space:nowrap;"><?= htmlspecialchars($dl['signed_in_as']) ?>: <strong style="color:#374151;"><?= htmlspecialchars($dashEmail) ?></strong></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if (in_array($dashTier, ['plus','pro'], true)): ?>
|
||||
<div style="display:flex; align-items:center; gap:.5rem; flex-shrink:0; flex-wrap:wrap;">
|
||||
<?php if ($isPaid): ?>
|
||||
<a href="/billing.php" style="color:#6b7280; font-size:.8rem; text-decoration:none; border:1px solid #e5e7eb; padding:5px 12px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['manage_plan']) ?></a>
|
||||
<?php else: ?>
|
||||
<a href="/pricing.php" style="background:#00205B; color:#fff; font-size:.8rem; font-weight:700; text-decoration:none; padding:5px 12px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['upgrade_plan']) ?> →</a>
|
||||
<?php endif; ?>
|
||||
<a href="/pricing.php#topup" style="background:#eff6ff; color:#1d4ed8; border:1px solid #bfdbfe; font-size:.8rem; font-weight:600; text-decoration:none; padding:5px 12px; border-radius:7px; white-space:nowrap;"><?= htmlspecialchars($dl['top_up']) ?> →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Status cards row ─────────────────────────────────────────── -->
|
||||
<section class="dashboard-status-row" style="display:grid; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); gap:1rem; margin:0 0 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:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('credits_available', $uiLang)) ?></p>
|
||||
<p style="margin:.35rem 0 0; font-size:1.8rem; font-weight:700; color:#00205B;"><?= number_format($eff, 0, ',', ' ') ?></p>
|
||||
<p style="margin:0; color:#6b7280; font-size:.85rem;"><?= (int)$dashDetail['balance'] ?> <?= htmlspecialchars(dbnToolsT('credits_monthly', $uiLang)) ?> · <?= (int)$dashDetail['bonus_balance'] ?> <?= $uiLang === 'no' ? 'forhåndsbetalte' : 'prepaid' ?> · <a href="/billing.php"><?= htmlspecialchars(dbnToolsT('details_link', $uiLang)) ?></a></p>
|
||||
</div>
|
||||
<?php if ($isPaid): ?>
|
||||
<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;"><?= htmlspecialchars(dbnToolsT('my_case', $uiLang)) ?></p>
|
||||
<p style="margin:0.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?> →</p>
|
||||
<p style="margin:0; color:#6b7280; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('my_case', $uiLang)) ?></p>
|
||||
<p style="margin:.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?> →</p>
|
||||
<?php
|
||||
$used = (int)$dashDetail['storage_used_bytes'];
|
||||
$quota = (int)$dashDetail['storage_quota_bytes'];
|
||||
$usedMb = $used > 0 ? round($used / 1048576, 1) : 0;
|
||||
$quotaMb = $quota > 0 ? round($quota / 1048576, 0) : 0;
|
||||
?>
|
||||
<p style="margin:0; color:#6b7280; font-size:0.85rem;"><?= $usedMb ?> MB / <?= $quotaMb ?> MB</p>
|
||||
<p style="margin:0; color:#6b7280; font-size:.85rem;"><?= $usedMb ?> MB / <?= $quotaMb ?> MB</p>
|
||||
</a>
|
||||
<?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;">
|
||||
<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;"><?= htmlspecialchars(dbnToolsT('upload_documents', $uiLang)) ?> →</p>
|
||||
<p style="margin:0; opacity:0.85; font-size:0.85rem;"><?= htmlspecialchars(dbnToolsT('upgrade_from_plus', $uiLang)) ?></p>
|
||||
<p style="margin:0; opacity:.85; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('build_your_case', $uiLang)) ?></p>
|
||||
<p style="margin:.35rem 0 0; font-size:1.4rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('upload_documents', $uiLang)) ?> →</p>
|
||||
<p style="margin:0; opacity:.85; font-size:.85rem;"><?= htmlspecialchars(dbnToolsT('upgrade_from_plus', $uiLang)) ?></p>
|
||||
</a>
|
||||
<?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>
|
||||
<p style="margin:0; color:#6b7280; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em;"><?= htmlspecialchars(dbnToolsT('my_corpus', $uiLang)) ?></p>
|
||||
<p id="corpusDocCount" style="margin:.35rem 0 0; font-size:1.4rem; font-weight:700; color:#00205B;">—</p>
|
||||
<p id="corpusUpdated" style="margin:0; color:#6b7280; font-size:.85rem;"><?= htmlspecialchars(dbnToolsT('open_corpus', $uiLang)) ?> →</p>
|
||||
</a>
|
||||
<?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;">
|
||||
<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;"><?= htmlspecialchars(dbnToolsT('survey_btn', $uiLang)) ?> →</p>
|
||||
<p style="margin:0; font-size:0.85rem; opacity:0.85;"><?= htmlspecialchars(dbnToolsT('survey_cta_text', $uiLang)) ?></p>
|
||||
<p style="margin:0; font-size:.85rem; text-transform:uppercase; letter-spacing:.06em; opacity:.85;"><?= htmlspecialchars(dbnToolsT('earn_credits_eyebrow', $uiLang)) ?></p>
|
||||
<p style="margin:.35rem 0 0; font-size:1.2rem; font-weight:700;"><?= htmlspecialchars(dbnToolsT('survey_btn', $uiLang)) ?> →</p>
|
||||
<p style="margin:0; font-size:.85rem; opacity:.85;"><?= htmlspecialchars(dbnToolsT('survey_cta_text', $uiLang)) ?></p>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="manifesto dashboard-manifesto">
|
||||
<div class="manifesto-copy">
|
||||
<p class="manifesto-eyebrow"><?= htmlspecialchars(dbnToolsT('dashboard_eyebrow', $uiLang)) ?></p>
|
||||
<h2 class="manifesto-title"><?= htmlspecialchars(dbnToolsT('manifesto_title', $uiLang)) ?></h2>
|
||||
<p class="manifesto-sub"><?= htmlspecialchars(dbnToolsT('dashboard_sub', $uiLang)) ?></p>
|
||||
</div>
|
||||
<div class="manifesto-stats" aria-label="Headline statistics">
|
||||
<div class="manifesto-stat"><strong>23</strong><span><?= htmlspecialchars(dbnToolsT('stat_echr', $uiLang)) ?></span></div>
|
||||
<div class="manifesto-stat"><strong>64%</strong><span><?= htmlspecialchars(dbnToolsT('stat_loss', $uiLang)) ?></span></div>
|
||||
<div class="manifesto-stat"><strong>1,731</strong><span><?= htmlspecialchars(dbnToolsT('stat_tribunal', $uiLang)) ?></span></div>
|
||||
<div class="manifesto-stat"><strong>20+</strong><span><?= htmlspecialchars(dbnToolsT('stat_pending', $uiLang)) ?></span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="disclaimer" role="note"><?= htmlspecialchars(dbnToolsT('disclaimer', $uiLang)) ?></div>
|
||||
|
||||
<!-- ── Tool cards grid ──────────────────────────────────────────── -->
|
||||
<section class="tool-dashboard-grid" aria-label="Available tools">
|
||||
<a class="dashboard-tool-card dashboard-tool-card--workbench" href="<?= htmlspecialchars($workbench['url']) ?>">
|
||||
|
||||
<?php /* Workbench card */ ?>
|
||||
<div class="dashboard-tool-card dashboard-tool-card--workbench"
|
||||
style="display:flex; flex-direction:column; cursor:pointer;"
|
||||
tabindex="0" role="link"
|
||||
onclick="location.href='<?= htmlspecialchars($workbench['url']) ?>'"
|
||||
onkeydown="if(event.key==='Enter'||event.key===' ')location.href='<?= htmlspecialchars($workbench['url']) ?>'">
|
||||
<span class="dashboard-tool-card__icon"><?= htmlspecialchars($workbench['icon']) ?></span>
|
||||
<span class="dashboard-tool-card__badge"><?= htmlspecialchars($workbench['badge']) ?></span>
|
||||
<h2><?= htmlspecialchars($workbench['label']) ?></h2>
|
||||
<p><?= htmlspecialchars($workbench['description']) ?></p>
|
||||
<strong><?= htmlspecialchars(dbnToolsT('enter_workbench', $uiLang)) ?></strong>
|
||||
<div class="dash-card-footer">
|
||||
<a href="<?= htmlspecialchars($workbench['url']) ?>"
|
||||
onclick="event.stopPropagation();"
|
||||
style="color:#00205B; font-size:.82rem; font-weight:700; text-decoration:none; white-space:nowrap;">
|
||||
<?= htmlspecialchars(dbnToolsT('enter_workbench', $uiLang)) ?> →
|
||||
</a>
|
||||
<?php foreach ($tools as $slug => $item): ?>
|
||||
<a class="dashboard-tool-card" href="<?= htmlspecialchars($item['url']) ?>">
|
||||
<button class="dash-mcp-slug-btn" data-slug="dbn.case_workbench_plan"
|
||||
onclick="event.stopPropagation(); copyDashSlug(this);"
|
||||
title="<?= htmlspecialchars($dl['mcp_copy_slug']) ?>"
|
||||
style="margin-left:auto; font-family:monospace; font-size:.69rem; background:#f1f5f9; border:1px solid #e2e8f0; color:#475569; padding:2px 8px; border-radius:4px; cursor:pointer; white-space:nowrap;">
|
||||
dbn.case_workbench_plan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php foreach ($tools as $slug => $item):
|
||||
$mcpSlug = $toolMcpSlugs[$slug] ?? null;
|
||||
$aboutUrl = isset($toolAboutPages[$slug]) ? htmlspecialchars($toolAboutPages[$slug] . $langSuffix) : null;
|
||||
$cardUrl = htmlspecialchars($item['url']);
|
||||
?>
|
||||
<div class="dashboard-tool-card"
|
||||
style="display:flex; flex-direction:column; cursor:pointer;"
|
||||
tabindex="0" role="link"
|
||||
onclick="location.href='<?= $cardUrl ?>'"
|
||||
onkeydown="if(event.key==='Enter'||event.key===' ')location.href='<?= $cardUrl ?>'">
|
||||
<span class="dashboard-tool-card__icon"><?= htmlspecialchars($item['icon']) ?></span>
|
||||
<span class="dashboard-tool-card__badge"><?= htmlspecialchars($item['badge']) ?></span>
|
||||
<h2><?= htmlspecialchars($item['label']) ?></h2>
|
||||
<p><?= htmlspecialchars($item['description']) ?></p>
|
||||
<strong><?= htmlspecialchars(dbnToolsT('open_tool', $uiLang)) ?></strong>
|
||||
<div class="dash-card-footer">
|
||||
<a href="<?= $cardUrl ?>"
|
||||
onclick="event.stopPropagation();"
|
||||
style="color:#00205B; font-size:.82rem; font-weight:700; text-decoration:none; white-space:nowrap;">
|
||||
<?= htmlspecialchars(dbnToolsT('open_tool', $uiLang)) ?> →
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
<?php if ($aboutUrl): ?>
|
||||
<a href="<?= $aboutUrl ?>"
|
||||
onclick="event.stopPropagation();"
|
||||
style="color:#6b7280; font-size:.78rem; text-decoration:none; white-space:nowrap;">
|
||||
<?= htmlspecialchars($dl['about_link']) ?> →
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ($mcpSlug): ?>
|
||||
<button class="dash-mcp-slug-btn" data-slug="<?= htmlspecialchars($mcpSlug) ?>"
|
||||
onclick="event.stopPropagation(); copyDashSlug(this);"
|
||||
title="<?= htmlspecialchars($dl['mcp_copy_slug']) ?>"
|
||||
style="margin-left:auto; font-family:monospace; font-size:.69rem; background:#f1f5f9; border:1px solid #e2e8f0; color:#475569; padding:2px 8px; border-radius:4px; cursor:pointer; white-space:nowrap;">
|
||||
<?= htmlspecialchars($mcpSlug) ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- Welcome modal — shown once per browser until dismissed with "don't show again" -->
|
||||
<!-- ── MCP quick-start (collapsed) ─────────────────────────────── -->
|
||||
<details class="dash-mcp-details" style="background:#fff; border:1px solid #e5e7eb; border-radius:12px; margin:1.5rem 0; overflow:hidden;">
|
||||
<summary style="display:flex; align-items:center; gap:.75rem; padding:1rem 1.5rem; cursor:pointer; list-style:none; font-weight:600; font-size:.95rem; color:#111827; user-select:none;">
|
||||
<span aria-hidden="true" style="font-size:1.1rem;">⚙️</span>
|
||||
<?= htmlspecialchars($dl['mcp_section']) ?>
|
||||
<span style="color:#6b7280; font-weight:400; font-size:.82rem; margin-left:.25rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">— <?= htmlspecialchars($dl['mcp_desc']) ?></span>
|
||||
<span class="dash-chevron" aria-hidden="true" style="margin-left:auto; color:#9ca3af; font-size:.75rem; flex-shrink:0;">▼</span>
|
||||
</summary>
|
||||
|
||||
<div style="padding:0 1.5rem 1.5rem; border-top:1px solid #f3f4f6;">
|
||||
|
||||
<?php if ($dashIsSso && in_array($dashTier, ['plus','pro'], true)): ?>
|
||||
<!-- Token prefix row -->
|
||||
<div style="display:flex; align-items:center; gap:.75rem; flex-wrap:wrap; margin:.9rem 0; padding:.7rem 1rem; background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px;">
|
||||
<span style="font-size:.82rem; font-weight:600; color:#374151; white-space:nowrap;"><?= htmlspecialchars($dl['mcp_token_lbl']) ?>:</span>
|
||||
<code id="dashMcpTokenPrefix" style="font-size:.82rem; color:#111827; flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"><?= $uiLang === 'no' ? 'Laster…' : 'Loading…' ?></code>
|
||||
<a href="/mcp.php<?= $langSuffix ?>" onclick="event.stopPropagation();" style="font-size:.78rem; color:#00205B; font-weight:600; text-decoration:none; white-space:nowrap; flex-shrink:0;"><?= $uiLang === 'no' ? 'Administrer tokens →' : 'Manage tokens →' ?></a>
|
||||
</div>
|
||||
<?php elseif ($dashIsSso): ?>
|
||||
<!-- Upgrade prompt -->
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; gap:.75rem; flex-wrap:wrap; margin:.9rem 0; padding:.7rem 1rem; background:#fefce8; border:1px solid #fef08a; border-radius:8px;">
|
||||
<span style="font-size:.82rem; color:#713f12;"><?= htmlspecialchars($dl['mcp_not_avail']) ?></span>
|
||||
<a href="/pricing.php" style="font-size:.8rem; background:#00205B; color:#fff; font-weight:600; text-decoration:none; padding:4px 12px; border-radius:6px; white-space:nowrap; flex-shrink:0;"><?= htmlspecialchars($dl['upgrade_plan']) ?> →</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- stdio config -->
|
||||
<p style="margin:1.1rem 0 .35rem; font-size:.82rem; font-weight:600; color:#374151;"><?= htmlspecialchars($dl['mcp_stdio_lbl']) ?></p>
|
||||
<div style="position:relative;">
|
||||
<pre id="dashStdioBlock" style="background:#101828; color:#f9fafb; padding:.85rem 3.5rem .85rem 1rem; border-radius:8px; font-size:.76rem; overflow-x:auto; margin:0; line-height:1.75; white-space:pre;">claude mcp add dobetternorge -- npx -y @bluenotelogic/mcp dobetternorge-mcp --stdio
|
||||
|
||||
# Set token (add to ~/.bashrc or ~/.zshrc to persist):
|
||||
export DBN_MCP_TOKEN=<span id="dashStdioToken" style="color:#86efac;">dbn_user_mcp_…</span></pre>
|
||||
<button onclick="copyDashBlock('dashStdioBlock', this)" style="position:absolute; top:.5rem; right:.5rem; background:rgba(249,250,251,.12); border:1px solid rgba(249,250,251,.2); color:#f9fafb; font-size:.72rem; padding:3px 8px; border-radius:5px; cursor:pointer;"><?= htmlspecialchars($dl['mcp_copy']) ?></button>
|
||||
</div>
|
||||
|
||||
<!-- Remote HTTP -->
|
||||
<p style="margin:1.1rem 0 .35rem; font-size:.82rem; font-weight:600; color:#374151;"><?= htmlspecialchars($dl['mcp_remote_lbl']) ?></p>
|
||||
<div style="position:relative;">
|
||||
<pre style="background:#101828; color:#f9fafb; padding:.85rem 3.5rem .85rem 1rem; border-radius:8px; font-size:.76rem; overflow-x:auto; margin:0; line-height:1.75; white-space:pre;">URL: https://mcp.dobetternorge.no/mcp
|
||||
Authorization: Bearer <span id="dashRemoteToken" style="color:#86efac;">dbn_user_mcp_…</span></pre>
|
||||
<button onclick="copyDashRemote(this)" style="position:absolute; top:.5rem; right:.5rem; background:rgba(249,250,251,.12); border:1px solid rgba(249,250,251,.2); color:#f9fafb; font-size:.72rem; padding:3px 8px; border-radius:5px; cursor:pointer;"><?= htmlspecialchars($dl['mcp_copy']) ?></button>
|
||||
</div>
|
||||
|
||||
<p style="margin:1rem 0 0; font-size:.85rem;">
|
||||
<a href="/mcp.php<?= $langSuffix ?>" style="color:#00205B; font-weight:600;"><?= htmlspecialchars($dl['mcp_full_docs']) ?></a>
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- ── Tool reference cards ─────────────────────────────────────── -->
|
||||
<section style="margin:0 0 2rem;" aria-label="Tool reference">
|
||||
<div style="display:flex; align-items:baseline; gap:.75rem; margin-bottom:1rem; flex-wrap:wrap;">
|
||||
<h2 style="margin:0; font-family:'Crimson Pro',serif; font-size:1.55rem; color:#111827; line-height:1.1;"><?= htmlspecialchars($dl['tool_ref_title']) ?></h2>
|
||||
<p style="margin:0; color:#6b7280; font-size:.85rem;"><?= htmlspecialchars($dl['tool_ref_sub']) ?></p>
|
||||
</div>
|
||||
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(270px, 1fr)); gap:1rem;">
|
||||
<?php foreach ($toolRefCards as $refSlug => $ref):
|
||||
$refName = $ref['name'][$uiLang] ?? $ref['name']['en'];
|
||||
$refDesc = $ref['desc'][$uiLang] ?? $ref['desc']['en'];
|
||||
?>
|
||||
<div style="background:#fff; border:1px solid #e5e7eb; border-radius:10px; padding:1.1rem 1.25rem; display:flex; flex-direction:column;">
|
||||
<div style="display:flex; align-items:center; gap:.6rem; margin-bottom:.6rem;">
|
||||
<span style="font-size:1.4rem; line-height:1;" aria-hidden="true"><?= $ref['icon'] ?></span>
|
||||
<strong style="font-size:1rem; color:#111827;"><?= htmlspecialchars($refName) ?></strong>
|
||||
<code data-copy-slug="<?= htmlspecialchars($ref['mcp']) ?>"
|
||||
title="<?= htmlspecialchars($dl['mcp_copy_slug']) ?>"
|
||||
style="margin-left:auto; font-size:.68rem; background:#f1f5f9; border:1px solid #e2e8f0; color:#64748b; padding:1px 7px; border-radius:4px; cursor:pointer; white-space:nowrap; flex-shrink:0;">
|
||||
<?= htmlspecialchars($ref['mcp']) ?>
|
||||
</code>
|
||||
</div>
|
||||
<p style="margin:0 0 .8rem; font-size:.84rem; color:#4b5563; line-height:1.55; flex:1;"><?= htmlspecialchars($refDesc) ?></p>
|
||||
<div style="display:flex; align-items:center; gap:.4rem; flex-wrap:wrap; font-size:.78rem; border-top:1px solid #f3f4f6; padding-top:.6rem;">
|
||||
<a href="<?= htmlspecialchars($ref['about'] . $langSuffix) ?>" style="color:#00205B; text-decoration:none; font-weight:600;"><?= htmlspecialchars($dl['about_link']) ?></a>
|
||||
<span style="color:#d1d5db;">·</span>
|
||||
<a href="<?= htmlspecialchars($ref['guide'] . $langSuffix) ?>" style="color:#374151; text-decoration:none;"><?= htmlspecialchars($dl['guide_link']) ?></a>
|
||||
<span style="color:#d1d5db;">·</span>
|
||||
<a href="<?= htmlspecialchars($ref['tech'] . $langSuffix) ?>" style="color:#374151; text-decoration:none;"><?= htmlspecialchars($dl['tech_link']) ?></a>
|
||||
<a href="<?= htmlspecialchars($ref['tool']) ?>" style="margin-left:auto; color:#00205B; font-weight:600; text-decoration:none;"><?= htmlspecialchars($dl['open_tool']) ?> →</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Welcome modal ────────────────────────────────────────────── -->
|
||||
<div id="welcomeModal" class="wlc-backdrop" role="dialog" aria-modal="true" aria-labelledby="wlcTitle" hidden>
|
||||
<div class="wlc-card">
|
||||
<div class="wlc-header">
|
||||
@@ -168,7 +539,7 @@ $welcomeTips = [
|
||||
'corpus' => 'Browse the 220 K+ indexed legal passages behind every AI answer',
|
||||
'citations' => 'Trace how cases cite each other to find supporting precedents',
|
||||
];
|
||||
$toolIcons = [
|
||||
$welcomeIcons = [
|
||||
'transcribe' => '🎙',
|
||||
'timeline' => '📅',
|
||||
'redact' => '🔒',
|
||||
@@ -182,7 +553,7 @@ $toolIcons = [
|
||||
];
|
||||
foreach ($tools as $slug => $item):
|
||||
$tip = $welcomeTips[$slug] ?? $item['description'];
|
||||
$icon = $toolIcons[$slug] ?? '🛠';
|
||||
$icon = $welcomeIcons[$slug] ?? '🛠';
|
||||
?>
|
||||
<a class="wlc-tool-item" href="<?= htmlspecialchars($item['url']) ?>">
|
||||
<span class="wlc-tool-icon" aria-hidden="true"><?= $icon ?></span>
|
||||
@@ -200,10 +571,12 @@ foreach ($tools as $slug => $item):
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
<script src="assets/js/tools.js" defer></script>
|
||||
<script>
|
||||
/* ── Welcome modal ──────────────────────────────────────────────────── */
|
||||
(function () {
|
||||
var STORAGE_KEY = 'dbn-welcome-v1-seen';
|
||||
var modal = document.getElementById('welcomeModal');
|
||||
@@ -213,34 +586,24 @@ foreach ($tools as $slug => $item):
|
||||
function closeModal(saveFlag) {
|
||||
modal.hidden = true;
|
||||
document.body.style.overflow = '';
|
||||
if (saveFlag) {
|
||||
try { localStorage.setItem(STORAGE_KEY, '1'); } catch (e) {}
|
||||
}
|
||||
if (saveFlag) { try { localStorage.setItem(STORAGE_KEY, '1'); } catch (e) {} }
|
||||
}
|
||||
|
||||
if (modal && !localStorage.getItem(STORAGE_KEY)) {
|
||||
modal.hidden = false;
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
if (btnStart) {
|
||||
btnStart.addEventListener('click', function () {
|
||||
closeModal(chkDontShow && chkDontShow.checked);
|
||||
});
|
||||
btnStart.addEventListener('click', function () { closeModal(chkDontShow && chkDontShow.checked); });
|
||||
}
|
||||
|
||||
if (modal) {
|
||||
modal.addEventListener('click', function (e) {
|
||||
if (e.target === modal) closeModal(false);
|
||||
});
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && !modal.hidden) closeModal(false);
|
||||
});
|
||||
modal.addEventListener('click', function (e) { if (e.target === modal) closeModal(false); });
|
||||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !modal.hidden) closeModal(false); });
|
||||
}
|
||||
}());
|
||||
</script>
|
||||
|
||||
/* ── Corpus summary ─────────────────────────────────────────────────── */
|
||||
<?php if ($dashIsSso): ?>
|
||||
<script>
|
||||
(function () {
|
||||
var card = document.getElementById('corpusSummaryCard');
|
||||
var countEl = document.getElementById('corpusDocCount');
|
||||
@@ -251,8 +614,7 @@ foreach ($tools as $slug => $item):
|
||||
.then(function (data) {
|
||||
if (!data) return;
|
||||
countEl.textContent = data.doc_count !== undefined
|
||||
? data.doc_count.toLocaleString() + ' docs'
|
||||
: '—';
|
||||
? 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' });
|
||||
@@ -260,7 +622,92 @@ foreach ($tools as $slug => $item):
|
||||
})
|
||||
.catch(function () {});
|
||||
}());
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
/* ── MCP section ────────────────────────────────────────────────────── */
|
||||
(function () {
|
||||
/* chevron toggle */
|
||||
var det = document.querySelector('details.dash-mcp-details');
|
||||
if (det) {
|
||||
det.addEventListener('toggle', function () {
|
||||
var chev = det.querySelector('.dash-chevron');
|
||||
if (chev) chev.style.transform = det.open ? 'rotate(180deg)' : '';
|
||||
});
|
||||
}
|
||||
|
||||
<?php if ($dashIsSso && in_array($dashTier, ['plus','pro'], true)): ?>
|
||||
/* load token prefix */
|
||||
fetch('/api/mcp-tokens.php', { credentials: 'same-origin' })
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (data) {
|
||||
if (!data || !data.ok) return;
|
||||
var active = (data.tokens || []).filter(function (t) { return t.is_active; });
|
||||
var prefixEl = document.getElementById('dashMcpTokenPrefix');
|
||||
if (prefixEl) {
|
||||
prefixEl.textContent = active.length > 0
|
||||
? active[0].token_prefix + '…'
|
||||
: <?= json_encode($dl['mcp_no_token'], JSON_UNESCAPED_UNICODE) ?>;
|
||||
}
|
||||
if (active.length > 0) {
|
||||
var p = active[0].token_prefix + '…';
|
||||
var s = document.getElementById('dashStdioToken');
|
||||
var r = document.getElementById('dashRemoteToken');
|
||||
if (s) s.textContent = p;
|
||||
if (r) r.textContent = p;
|
||||
}
|
||||
})
|
||||
.catch(function () {});
|
||||
<?php endif; ?>
|
||||
|
||||
/* copy a pre block */
|
||||
window.copyDashBlock = function (id, btn) {
|
||||
var el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
navigator.clipboard.writeText(el.innerText || el.textContent).then(function () {
|
||||
var orig = btn.textContent; btn.textContent = '✓';
|
||||
setTimeout(function () { btn.textContent = orig; }, 1500);
|
||||
}).catch(function () {});
|
||||
};
|
||||
|
||||
/* copy remote HTTP config */
|
||||
window.copyDashRemote = function (btn) {
|
||||
var tok = document.getElementById('dashRemoteToken');
|
||||
var txt = 'URL: https://mcp.dobetternorge.no/mcp\nAuthorization: Bearer ' + (tok ? tok.textContent : 'dbn_user_mcp_…');
|
||||
navigator.clipboard.writeText(txt).then(function () {
|
||||
var orig = btn.textContent; btn.textContent = '✓';
|
||||
setTimeout(function () { btn.textContent = orig; }, 1500);
|
||||
}).catch(function () {});
|
||||
};
|
||||
|
||||
/* copy MCP slug from tool card button */
|
||||
window.copyDashSlug = function (btn) {
|
||||
var slug = btn.getAttribute('data-slug');
|
||||
if (!slug) return;
|
||||
navigator.clipboard.writeText(slug).then(function () {
|
||||
var orig = btn.textContent;
|
||||
btn.textContent = '✓ copied';
|
||||
btn.style.cssText += '; color:#065f46 !important; background:#d1fae5 !important; border-color:#6ee7b7 !important;';
|
||||
setTimeout(function () {
|
||||
btn.textContent = orig;
|
||||
btn.style.color = '';
|
||||
btn.style.background = '';
|
||||
btn.style.borderColor = '';
|
||||
}, 1500);
|
||||
}).catch(function () {});
|
||||
};
|
||||
|
||||
/* copy MCP slug from tool reference cards */
|
||||
document.querySelectorAll('[data-copy-slug]').forEach(function (el) {
|
||||
el.addEventListener('click', function () {
|
||||
var slug = el.getAttribute('data-copy-slug');
|
||||
if (!slug) return;
|
||||
navigator.clipboard.writeText(slug).then(function () {
|
||||
var orig = el.textContent; el.textContent = '✓';
|
||||
setTimeout(function () { el.textContent = orig; }, 1200);
|
||||
}).catch(function () {});
|
||||
});
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user