import * as plugins from '../plugins.js'; import { apiService } from '../services/index.js'; import { themeStyles } from '../styles/index.js'; const { html, css, cssManager, customElement, property, state, DeesElement } = plugins; @customElement('tsview-mongo-document') export class TsviewMongoDocument extends DeesElement { @property({ type: String }) public accessor databaseName: string = ''; @property({ type: String }) public accessor collectionName: string = ''; @property({ type: String }) public accessor documentId: string = ''; @state() private accessor document: Record | null = null; @state() private accessor loading: boolean = false; @state() private accessor editing: boolean = false; @state() private accessor originalContent: string = ''; @state() private accessor hasChanges: boolean = false; @state() private accessor error: string = ''; public static styles = [ cssManager.defaultStyles, themeStyles, css` :host { display: block; height: 100%; } .document-container { display: flex; flex-direction: column; height: 100%; } .header { padding: 12px; border-bottom: 1px solid #333; display: flex; align-items: center; justify-content: space-between; } .header-title { font-size: 14px; font-weight: 500; } .header-actions { display: flex; gap: 8px; } .action-btn { padding: 6px 12px; background: transparent; border: 1px solid #444; color: #888; border-radius: 4px; cursor: pointer; font-size: 12px; transition: all 0.15s; } .action-btn:hover { border-color: #666; color: #aaa; } .action-btn.primary { background: rgba(255, 255, 255, 0.1); border-color: #404040; color: #e0e0e0; } .action-btn.primary:hover { background: rgba(255, 255, 255, 0.15); } .action-btn.danger { background: rgba(239, 68, 68, 0.2); border-color: #ef4444; color: #f87171; } .action-btn.danger:hover { background: rgba(239, 68, 68, 0.3); } .content { flex: 1; overflow: hidden; display: flex; flex-direction: column; } .content dees-input-code { flex: 1; } .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px; } .empty-state svg { width: 48px; height: 48px; margin-bottom: 12px; opacity: 0.5; } .loading-state { display: flex; align-items: center; justify-content: center; height: 100%; color: #888; } .error-state { padding: 16px; color: #f87171; text-align: center; } `, ]; updated(changedProperties: Map) { if (changedProperties.has('documentId')) { this.editing = false; this.hasChanges = false; if (this.documentId) { this.loadDocument(); } else { this.document = null; this.originalContent = ''; } } } private async loadDocument() { if (!this.documentId || !this.databaseName || !this.collectionName) return; this.loading = true; this.error = ''; try { this.document = await apiService.getDocument( this.databaseName, this.collectionName, this.documentId ); this.originalContent = JSON.stringify(this.document, null, 2); this.hasChanges = false; } catch (err) { console.error('Error loading document:', err); this.error = 'Failed to load document'; } this.loading = false; } private startEditing() { this.editing = true; } private cancelEditing() { this.editing = false; // Reset content to original const codeEditor = this.shadowRoot?.querySelector('dees-input-code') as any; if (codeEditor) { codeEditor.value = this.originalContent; } this.hasChanges = false; } private handleContentChange(e: CustomEvent) { const newValue = e.detail as string; this.hasChanges = newValue !== this.originalContent; } private handleDiscard() { const codeEditor = this.shadowRoot?.querySelector('dees-input-code') as any; if (codeEditor) { codeEditor.value = this.originalContent; } this.hasChanges = false; } private async saveDocument() { try { const codeEditor = this.shadowRoot?.querySelector('dees-input-code') as any; const content = codeEditor?.value || this.originalContent; const updatedDoc = JSON.parse(content); // Remove _id from update (can't update _id) const { _id, ...updateFields } = updatedDoc; await apiService.updateDocument( this.databaseName, this.collectionName, this.documentId, updateFields ); this.editing = false; this.hasChanges = false; await this.loadDocument(); this.dispatchEvent( new CustomEvent('document-updated', { detail: { documentId: this.documentId }, bubbles: true, composed: true, }) ); } catch (err) { console.error('Error saving document:', err); this.error = 'Invalid JSON or save failed'; } } private async deleteDocument() { if (!confirm('Delete this document?')) return; try { await apiService.deleteDocument( this.databaseName, this.collectionName, this.documentId ); this.dispatchEvent( new CustomEvent('document-deleted', { detail: { documentId: this.documentId }, bubbles: true, composed: true, }) ); this.document = null; } catch (err) { console.error('Error deleting document:', err); this.error = 'Delete failed'; } } render() { if (!this.documentId) { return html`

Select a document to view

`; } if (this.loading) { return html`
Loading...
`; } if (this.error && !this.document) { return html`
${this.error}
`; } return html`
Document
${this.editing ? this.hasChanges ? html` ` : html` ` : html` `}
this.handleContentChange(e)} >
`; } }