feat(web): add database overview panel, collection overview and resizable panels; show/hide system databases; use code editor with change-tracking in document view; add getDatabaseStats API and typings; enable overwrite for S3 uploads

This commit is contained in:
2026-01-25 17:34:52 +00:00
parent 2ca5f52da3
commit a26e7a5a20
17 changed files with 718 additions and 143 deletions

View File

@@ -45,6 +45,9 @@ export class TsviewApp extends DeesElement {
@state()
private accessor newDatabaseName: string = '';
@state()
private accessor showSystemDatabases: boolean = false;
@state()
private accessor showS3CreateDialog: boolean = false;
@@ -57,6 +60,12 @@ export class TsviewApp extends DeesElement {
@state()
private accessor s3CreateDialogName: string = '';
@state()
private accessor sidebarWidth: number = 240;
@state()
private accessor isResizingSidebar: boolean = false;
public static styles = [
cssManager.defaultStyles,
themeStyles,
@@ -130,10 +139,22 @@ export class TsviewApp extends DeesElement {
.app-main {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-columns: var(--sidebar-width, 240px) 4px 1fr;
overflow: hidden;
}
.resize-divider {
width: 4px;
background: transparent;
cursor: col-resize;
transition: background 0.2s;
}
.resize-divider:hover,
.resize-divider.active {
background: rgba(255, 255, 255, 0.2);
}
.sidebar {
background: #1e1e1e;
border-right: 1px solid #333;
@@ -389,6 +410,15 @@ export class TsviewApp extends DeesElement {
`,
];
private readonly SYSTEM_DATABASES = ['admin', 'config', 'local'];
private get visibleDatabases() {
if (this.showSystemDatabases) {
return this.databases;
}
return this.databases.filter(db => !this.SYSTEM_DATABASES.includes(db.name));
}
async connectedCallback() {
super.connectedCallback();
await this.loadData();
@@ -423,8 +453,15 @@ export class TsviewApp extends DeesElement {
}
private selectDatabase(db: string) {
this.selectedDatabase = db;
this.selectedCollection = '';
if (this.selectedDatabase === db) {
// Collapse - clicking the same database again
// Keep the collection selection intact
this.selectedDatabase = '';
} else {
// Switch to different database - clear collection
this.selectedDatabase = db;
this.selectedCollection = '';
}
}
private selectCollection(collection: string) {
@@ -635,6 +672,38 @@ export class TsviewApp extends DeesElement {
]);
}
private handleSidebarContextMenu(event: MouseEvent) {
event.preventDefault();
DeesContextmenu.openContextMenuWithOptions(event, [
{
name: this.showSystemDatabases ? 'Hide System Databases' : 'Show System Databases',
iconName: this.showSystemDatabases ? 'lucide:eyeOff' : 'lucide:eye',
action: async () => {
this.showSystemDatabases = !this.showSystemDatabases;
},
},
]);
}
private startSidebarResize = (e: MouseEvent) => {
e.preventDefault();
this.isResizingSidebar = true;
document.addEventListener('mousemove', this.handleSidebarResize);
document.addEventListener('mouseup', this.endSidebarResize);
};
private handleSidebarResize = (e: MouseEvent) => {
if (!this.isResizingSidebar) return;
const newWidth = Math.min(Math.max(e.clientX, 150), 500);
this.sidebarWidth = newWidth;
};
private endSidebarResize = () => {
this.isResizingSidebar = false;
document.removeEventListener('mousemove', this.handleSidebarResize);
document.removeEventListener('mouseup', this.endSidebarResize);
};
render() {
return html`
<div class="app-container">
@@ -669,8 +738,12 @@ export class TsviewApp extends DeesElement {
</nav>
</header>
<main class="app-main">
<main class="app-main" style="--sidebar-width: ${this.sidebarWidth}px">
${this.renderSidebar()}
<div
class="resize-divider ${this.isResizingSidebar ? 'active' : ''}"
@mousedown=${this.startSidebarResize}
></div>
${this.renderContent()}
</main>
</div>
@@ -849,7 +922,7 @@ export class TsviewApp extends DeesElement {
if (this.viewMode === 'mongo') {
return html`
<aside class="sidebar">
<aside class="sidebar" @contextmenu=${(e: MouseEvent) => this.handleSidebarContextMenu(e)}>
<div class="sidebar-header">Databases & Collections</div>
<button class="create-btn" @click=${() => this.showCreateDatabaseDialog = true}>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -859,9 +932,9 @@ export class TsviewApp extends DeesElement {
New Database
</button>
<div class="sidebar-list">
${this.databases.length === 0
${this.visibleDatabases.length === 0
? html`<div class="sidebar-item" style="color: #666; cursor: default;">No databases found</div>`
: this.databases.map((db) => this.renderDatabaseGroup(db))}
: this.visibleDatabases.map((db) => this.renderDatabaseGroup(db))}
</div>
</aside>
`;
@@ -954,6 +1027,17 @@ export class TsviewApp extends DeesElement {
`;
}
// Show database overview when __overview__ is selected
if (this.selectedCollection === '__overview__') {
return html`
<div class="content-area">
<tsview-mongo-db-overview
.databaseName=${this.selectedDatabase}
></tsview-mongo-db-overview>
</div>
`;
}
return html`
<div class="content-area">
<tsview-mongo-browser