feat(dashboard): add corpus dashboard at /dashboard/
Full private corpus dashboard for tools.dobetternorge.no users — each SSO
account gets an auto-provisioned CaveauAI tenant (clients row, corpus) on
first visit. Includes upload (file/paste/URL), RAG chat with SSE streaming
and citation chips, document CRUD, FalkorDB graph relations tab, and
improved save-from-tool flow with tag/preview support.
- dashboard/{index,documents,document,upload,chat,settings}.php
- api/dashboard/{corpus-init,documents,upload,ingest-status,chat-stream,
save-from-tool,graph}.php
- includes/{CorpusProvision,layout_dashboard,layout_dashboard_footer}.php
- assets/css/dashboard.css assets/js/corpus-save.js (routing upgrade)
- includes/{bootstrap,layout}.php extended for dashboard provisioning
Migration 141 (clients.dbn_sso_uid + import_method enum) applied on chloe.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Dashboard chrome (minimal). Used by /dashboard/* pages.
|
||||
*
|
||||
* Page contract:
|
||||
* $dashboardPage string — slug for active-state ('index'|'documents'|'document'|'upload'|'chat'|'settings')
|
||||
* $dashboardTitle string — H1 for the content area
|
||||
* $dashboardLead string? — optional sub-title sentence
|
||||
* $extraScripts string[]?— optional extra script srcs (defer-loaded)
|
||||
*
|
||||
* Lazy-provisions the tenant on first hit; exposes ids to JS as window.DBN_DASHBOARD.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/bootstrap.php';
|
||||
|
||||
if (!dbnToolsIsAuthenticated()) {
|
||||
$return = urlencode($_SERVER['REQUEST_URI'] ?? '/dashboard/');
|
||||
header('Location: /?return=' . $return);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$dashboardTenant = dbnToolsEnsureDashboardTenant();
|
||||
} catch (DbnToolsHttpException $e) {
|
||||
http_response_code($e->status);
|
||||
echo '<!doctype html><meta charset="utf-8"><title>Dashboard unavailable</title>'
|
||||
. '<p style="font-family:sans-serif;max-width:540px;margin:4rem auto;">'
|
||||
. htmlspecialchars($e->getMessage())
|
||||
. ' <a href="/dashboard/">Try again</a></p>';
|
||||
exit;
|
||||
}
|
||||
|
||||
$uiLang = dbnToolsCurrentLanguage();
|
||||
$dashboardPage = $dashboardPage ?? 'index';
|
||||
$dashboardTitle = $dashboardTitle ?? 'Dashboard';
|
||||
$dashboardLead = $dashboardLead ?? '';
|
||||
$langPath = strtok((string)($_SERVER['REQUEST_URI'] ?? '/dashboard/'), '?') ?: '/dashboard/';
|
||||
|
||||
$dashboardNav = [
|
||||
'index' => ['url' => '/dashboard/', 'label' => 'Oversikt', 'sub' => 'Overview'],
|
||||
'documents' => ['url' => '/dashboard/documents.php', 'label' => 'Dokumenter', 'sub' => 'Documents'],
|
||||
'upload' => ['url' => '/dashboard/upload.php', 'label' => 'Last opp', 'sub' => 'Upload'],
|
||||
'chat' => ['url' => '/dashboard/chat.php', 'label' => 'Spør', 'sub' => 'Ask'],
|
||||
'settings' => ['url' => '/dashboard/settings.php', 'label' => 'Innstillinger', 'sub' => 'Settings'],
|
||||
];
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="<?= htmlspecialchars($uiLang) ?>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?= htmlspecialchars($dashboardTitle) ?> · Min korpus · Do Better Norge</title>
|
||||
<link rel="stylesheet" href="/assets/css/tools.css">
|
||||
<link rel="stylesheet" href="/assets/css/dashboard.css">
|
||||
</head>
|
||||
<body data-authenticated="true" data-dashboard-page="<?= htmlspecialchars($dashboardPage) ?>">
|
||||
<script>
|
||||
window.DBN_TOOLS_AUTHENTICATED = true;
|
||||
window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
|
||||
window.DBN_DASHBOARD = {
|
||||
clientId: <?= (int)$dashboardTenant['client_id'] ?>,
|
||||
clientUserId: <?= (int)$dashboardTenant['client_user_id'] ?>,
|
||||
corpusId: <?= (int)$dashboardTenant['corpus_id'] ?>,
|
||||
apiBase: '/api/dashboard'
|
||||
};
|
||||
</script>
|
||||
|
||||
<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>
|
||||
</header>
|
||||
|
||||
<div class="dash-layout">
|
||||
<nav class="dash-sidebar" aria-label="Dashboard sections">
|
||||
<?php foreach ($dashboardNav as $slug => $item): ?>
|
||||
<a href="<?= htmlspecialchars($item['url']) ?>"
|
||||
class="dash-sidebar__item<?= $slug === $dashboardPage ? ' is-active' : '' ?>"
|
||||
<?= $slug === $dashboardPage ? 'aria-current="page"' : '' ?>>
|
||||
<strong><?= htmlspecialchars($item['label']) ?></strong>
|
||||
<small><?= htmlspecialchars($item['sub']) ?></small>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<main class="dash-main" id="dashMain">
|
||||
<header class="dash-main__head">
|
||||
<h1><?= htmlspecialchars($dashboardTitle) ?></h1>
|
||||
<?php if ($dashboardLead !== ''): ?>
|
||||
<p class="dash-main__lead"><?= htmlspecialchars($dashboardLead) ?></p>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<div class="dash-main__body">
|
||||
Reference in New Issue
Block a user