diff --git a/src/api/infrastructure.ts b/src/api/infrastructure.ts index 5c83400..c399387 100644 --- a/src/api/infrastructure.ts +++ b/src/api/infrastructure.ts @@ -174,3 +174,20 @@ export const fetchTableRelations = async (schemaId: string): Promise => { + const res = await fetch(`${API_BASE}/rpc/sync_all_schemas`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + if (!res.ok) throw new Error(`Sync error: ${res.status}`); + return res.json(); +}; diff --git a/src/styles/main.css b/src/styles/main.css index d0cbb36..d8062bf 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -582,3 +582,70 @@ svg { max-height: calc(100vh - 150px); overflow-y: auto; } + +/* Sync button styles */ +.btn-sync { + width: 100%; + padding: 10px 12px; + background: var(--primary); + color: white; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 13px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + transition: background 0.2s, opacity 0.2s; +} + +.btn-sync:hover:not(:disabled) { + background: var(--primary-dark, #5a6be8); +} + +.btn-sync:disabled { + opacity: 0.7; + cursor: not-allowed; +} + +.sync-icon { + font-size: 16px; + display: inline-block; +} + +.sync-icon.spinning { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.sync-status { + margin-top: 10px; + font-size: 12px; +} + +.sync-result { + padding: 6px 8px; + border-radius: 4px; + margin-bottom: 4px; +} + +.sync-result.success { + background: rgba(76, 175, 80, 0.15); + color: #4CAF50; +} + +.sync-result.pending { + background: rgba(255, 152, 0, 0.15); + color: #FF9800; +} + +.sync-result.error { + background: rgba(244, 67, 54, 0.15); + color: #F44336; +} diff --git a/src/views/TablesGraph.ts b/src/views/TablesGraph.ts index baa2da6..7077788 100644 --- a/src/views/TablesGraph.ts +++ b/src/views/TablesGraph.ts @@ -1,6 +1,6 @@ import type { DBSchema, TableSchema, TableRelation } from '../api/schemas.ts'; import { CATEGORY_COLORS, DB_SCHEMAS } from '../api/schemas.ts'; -import { fetchColumns, fetchTables, fetchTableRelations, type Column } from '../api/infrastructure.ts'; +import { fetchColumns, fetchTables, fetchTableRelations, syncSchemas, type Column, type SyncResult } from '../api/infrastructure.ts'; type D3Module = typeof import('d3'); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -42,7 +42,7 @@ export class TablesGraph { this.container = container; this.serverName = serverName; this.schema = schema; - this.showCategories = new Set(['core', 'api', 'graph', 'tree', 'library', 'data', 'system', 'directus', 'comm', 'repo', 'user', 'action', 'auth', 'agent', 'context', 'creds']); + this.showCategories = new Set(['core', 'api', 'graph', 'tree', 'library', 'data', 'system', 'directus', 'comm', 'repo', 'user', 'action', 'auth', 'agent', 'context', 'creds', 'storage']); } async mount(): Promise { @@ -254,7 +254,7 @@ export class TablesGraph { const sidebar = this.container.querySelector('#graph-sidebar'); if (!sidebar) return; - const categories: TableSchema['category'][] = ['core', 'api', 'graph', 'tree', 'library', 'data', 'directus', 'system', 'comm', 'repo', 'user', 'action', 'auth', 'agent', 'context', 'creds']; + const categories: TableSchema['category'][] = ['core', 'api', 'graph', 'tree', 'library', 'data', 'directus', 'system', 'comm', 'repo', 'user', 'action', 'auth', 'agent', 'context', 'creds', 'storage']; sidebar.innerHTML = ` + +
+
Sincronizacion
+ +
+
`; this.bindSidebarEvents(sidebar as HTMLElement); @@ -379,6 +387,37 @@ export class TablesGraph { } }; } + + // Sync button + const syncBtn = sidebar.querySelector('#btn-sync'); + const syncStatus = sidebar.querySelector('#sync-status'); + if (syncBtn && syncStatus) { + syncBtn.onclick = async () => { + syncBtn.disabled = true; + syncBtn.innerHTML = ' Sincronizando...'; + syncStatus.innerHTML = ''; + + try { + const results = await syncSchemas(); + syncStatus.innerHTML = results.map(r => + `
+ ${r.schema_id}: ${r.status === 'ok' ? `${r.columns_synced} cols` : r.status} +
` + ).join(''); + + // Reload current schema if it was synced + const currentSynced = results.find(r => r.schema_id === this.serverName && r.status === 'ok'); + if (currentSynced) { + setTimeout(() => this.mount(), 1000); + } + } catch (e) { + syncStatus.innerHTML = `
Error: ${e}
`; + } finally { + syncBtn.disabled = false; + syncBtn.innerHTML = ' Sincronizar Schemas'; + } + }; + } } private renderLegend(): void {