initial
This commit is contained in:
253
ts_web/elements/tsview-mongo-browser.ts
Normal file
253
ts_web/elements/tsview-mongo-browser.ts
Normal file
@@ -0,0 +1,253 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import { apiService, type ICollectionStats } from '../services/index.js';
|
||||
|
||||
const { html, css, cssManager, customElement, property, state, DeesElement } = plugins;
|
||||
|
||||
type TViewTab = 'documents' | 'indexes' | 'aggregation';
|
||||
|
||||
@customElement('tsview-mongo-browser')
|
||||
export class TsviewMongoBrowser extends DeesElement {
|
||||
@property({ type: String })
|
||||
public accessor databaseName: string = '';
|
||||
|
||||
@property({ type: String })
|
||||
public accessor collectionName: string = '';
|
||||
|
||||
@state()
|
||||
private accessor activeTab: TViewTab = 'documents';
|
||||
|
||||
@state()
|
||||
private accessor selectedDocumentId: string = '';
|
||||
|
||||
@state()
|
||||
private accessor stats: ICollectionStats | null = null;
|
||||
|
||||
public static styles = [
|
||||
cssManager.defaultStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.browser-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.collection-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.collection-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.collection-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 13px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 8px 16px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background: rgba(99, 102, 241, 0.2);
|
||||
color: #818cf8;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 400px;
|
||||
gap: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-panel {
|
||||
overflow: auto;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
await this.loadStats();
|
||||
}
|
||||
|
||||
updated(changedProperties: Map<string, unknown>) {
|
||||
if (changedProperties.has('databaseName') || changedProperties.has('collectionName')) {
|
||||
this.loadStats();
|
||||
this.selectedDocumentId = '';
|
||||
}
|
||||
}
|
||||
|
||||
private async loadStats() {
|
||||
if (!this.databaseName || !this.collectionName) return;
|
||||
|
||||
try {
|
||||
this.stats = await apiService.getCollectionStats(this.databaseName, this.collectionName);
|
||||
} catch (err) {
|
||||
console.error('Error loading stats:', err);
|
||||
this.stats = null;
|
||||
}
|
||||
}
|
||||
|
||||
private setActiveTab(tab: TViewTab) {
|
||||
this.activeTab = tab;
|
||||
}
|
||||
|
||||
private handleDocumentSelected(e: CustomEvent) {
|
||||
this.selectedDocumentId = e.detail.documentId;
|
||||
}
|
||||
|
||||
private formatCount(num: number): string {
|
||||
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
|
||||
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
private formatSize(bytes: number): string {
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
return `${size.toFixed(unitIndex > 0 ? 1 : 0)} ${units[unitIndex]}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="browser-container">
|
||||
<div class="header">
|
||||
<div class="collection-info">
|
||||
<span class="collection-title">${this.collectionName}</span>
|
||||
${this.stats
|
||||
? html`
|
||||
<div class="collection-stats">
|
||||
<span class="stat-item">${this.formatCount(this.stats.count)} docs</span>
|
||||
<span class="stat-item">${this.formatSize(this.stats.size)}</span>
|
||||
<span class="stat-item">${this.stats.indexCount} indexes</span>
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
<button
|
||||
class="tab ${this.activeTab === 'documents' ? 'active' : ''}"
|
||||
@click=${() => this.setActiveTab('documents')}
|
||||
>
|
||||
Documents
|
||||
</button>
|
||||
<button
|
||||
class="tab ${this.activeTab === 'indexes' ? 'active' : ''}"
|
||||
@click=${() => this.setActiveTab('indexes')}
|
||||
>
|
||||
Indexes
|
||||
</button>
|
||||
<button
|
||||
class="tab ${this.activeTab === 'aggregation' ? 'active' : ''}"
|
||||
@click=${() => this.setActiveTab('aggregation')}
|
||||
>
|
||||
Aggregation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="main-panel">
|
||||
${this.activeTab === 'documents'
|
||||
? html`
|
||||
<tsview-mongo-documents
|
||||
.databaseName=${this.databaseName}
|
||||
.collectionName=${this.collectionName}
|
||||
@document-selected=${this.handleDocumentSelected}
|
||||
></tsview-mongo-documents>
|
||||
`
|
||||
: this.activeTab === 'indexes'
|
||||
? html`
|
||||
<tsview-mongo-indexes
|
||||
.databaseName=${this.databaseName}
|
||||
.collectionName=${this.collectionName}
|
||||
></tsview-mongo-indexes>
|
||||
`
|
||||
: html`
|
||||
<div style="padding: 24px; text-align: center; color: #666;">
|
||||
Aggregation pipeline builder coming soon
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
|
||||
<div class="detail-panel">
|
||||
<tsview-mongo-document
|
||||
.databaseName=${this.databaseName}
|
||||
.collectionName=${this.collectionName}
|
||||
.documentId=${this.selectedDocumentId}
|
||||
></tsview-mongo-document>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user