/**
 * Студия БД — Vue 3 runtime без сборки (FR-083 UX, 1.0.4).
 */
(function () {
  'use strict';
  if (typeof Vue === 'undefined') return;
  var h = Vue.h;
  var mountEl = document.getElementById('app-db-studio');
  if (!mountEl) return;

  var API = '/api/v1/admin';

    function api(method, path, body) {
    var url = (window.location.origin || '') + path;
    var opts = {
      method: method,
      credentials: 'same-origin',
      headers: { Accept: 'application/json', 'Content-Type': 'application/json' }
    };
    if (body) opts.body = JSON.stringify(body);
    var fetchFn = window.cascad360Fetch || fetch;
    return fetchFn(url, opts).then(function (r) { return r.json(); });
  }

  function qs(params) {
    var parts = [];
    Object.keys(params).forEach(function (k) {
      if (params[k] !== undefined && params[k] !== null && params[k] !== '') {
        parts.push(encodeURIComponent(k) + '=' + encodeURIComponent(params[k]));
      }
    });
    return parts.length ? '?' + parts.join('&') : '';
  }

  function parseInitialContext() {
    var sp = new URLSearchParams(window.location.search);
    return {
      datasource_id: sp.get('datasource') || sp.get('datasource_id') || 'core',
      database: sp.get('database') || ''
    };
  }

  function classifySql(sql) {
    var u = (sql || '').trim().toUpperCase();
    if (!u) return 'read';
    if (u.indexOf('DROP DATABASE') >= 0 || u.indexOf('DROP SCHEMA') >= 0) return 'blocked';
    var ddl = ['CREATE', 'ALTER', 'DROP', 'TRUNCATE'];
    for (var i = 0; i < ddl.length; i++) {
      if (u.indexOf(ddl[i]) === 0 || u.indexOf(' ' + ddl[i] + ' ') >= 0) return 'ddl';
    }
    var dml = ['INSERT', 'UPDATE', 'DELETE'];
    for (var j = 0; j < dml.length; j++) {
      if (u.indexOf(dml[j]) === 0 || u.indexOf(' ' + dml[j] + ' ') >= 0) return 'dml';
    }
    return 'read';
  }

  function loadSqlHistory() {
    try {
      var raw = localStorage.getItem('db_studio_sql_history');
      return raw ? JSON.parse(raw) : [];
    } catch (e) {
      return [];
    }
  }

  function saveSqlHistory(entry) {
    try {
      var list = loadSqlHistory().filter(function (x) { return x !== entry; });
      list.unshift(entry);
      if (list.length > 20) list = list.slice(0, 20);
      localStorage.setItem('db_studio_sql_history', JSON.stringify(list));
      return list;
    } catch (e) {
      return [];
    }
  }

  Vue.createApp({
    data: function () {
      var init = parseInitialContext();
      return {
        datasources: [],
        databases: [],
        tables: [],
        context: null,
        datasourceId: init.datasource_id,
        database: init.database,
        tableSearch: '',
        activeTable: '',
        activeTab: 'data',
        rows: [],
        columns: [],
        structure: null,
        total: 0,
        page: 1,
        pageSize: 50,
        sortCol: '',
        sortOrder: 'asc',
        loading: false,
        error: '',
        sqlText: '',
        sqlResult: null,
        sqlWriteMode: false,
        sqlSchemaMode: false,
        sqlHistory: loadSqlHistory(),
        sidebarCollapsed: false,
        modalOpen: false,
        modalMode: 'edit',
        modalData: {},
        pkCol: 'id',
        editPk: null
      };
    },
    computed: {
      filteredTables: function () {
        var s = (this.tableSearch || '').toLowerCase();
        if (!s) return this.tables;
        return this.tables.filter(function (t) {
          return t.name.toLowerCase().indexOf(s) >= 0;
        });
      },
      readonly: function () {
        return this.context && this.context.readonly;
      },
      dataReadBlocked: function () {
        return this.context && this.context.blocks_data_read;
      },
      contextBadgeLabel: function () {
        if (!this.context) return '';
        if (this.context.blocks_data_read) return 'Системная СУБД (read-only)';
        if (this.context.is_platform) return 'БД ядра';
        return 'Пользовательская';
      },
      contextBadgeClass: function () {
        if (!this.context) return 'badge bg-secondary';
        if (this.context.blocks_data_read) return 'db-studio-badge-system';
        if (this.context.is_platform) return 'badge bg-primary';
        return 'badge bg-success';
      },
      totalPages: function () {
        return Math.max(1, Math.ceil(this.total / this.pageSize));
      },
      ctxQuery: function () {
        return qs({ datasource_id: this.datasourceId, database: this.database });
      }
    },
    mounted: function () {
      var self = this;
      var initialDb = self.database;
      self.loadDatasources();
      setTimeout(function () {
        self.onDatasourceChange({ preserveDatabase: !!initialDb, preselectedDatabase: initialDb });
      }, 200);
    },
    methods: {
      loadDatasources: function () {
        var self = this;
        api('GET', API + '/datasources').then(function (d) {
          if (d.success && d.datasources) self.datasources = d.datasources;
        });
      },
      onDatasourceChange: function (options) {
        var self = this;
        options = options || {};
        var preselected = options.preselectedDatabase || (options.preserveDatabase ? self.database : '');
        if (!options.preserveDatabase) {
          self.database = '';
        }
        self.databases = [];
        self.context = null;
        api('GET', API + '/databases/datasource/' + encodeURIComponent(self.datasourceId) + '/list?include_platform=true').then(function (d) {
          if (d.success) {
            self.databases = (d.databases || []).map(function (x) {
              return typeof x === 'string' ? { name: x, is_system: false } : x;
            });
            if (preselected) {
              var found = self.databases.some(function (db) { return db.name === preselected; });
              if (found) {
                self.database = preselected;
                self.onDatabaseChange();
              } else if (options.preserveDatabase) {
                self.database = preselected;
                self.onDatabaseChange();
              }
            }
          }
        });
      },
      onDatabaseChange: function () {
        var self = this;
        if (!self.database) return;
        self.loading = true;
        self.error = '';
        self.activeTable = '';
        self.rows = [];
        self.columns = [];
        self.total = 0;
        self.structure = null;
        api('GET', API + '/db/context' + qs({ datasource_id: self.datasourceId, database: self.database })).then(function (d) {
          self.loading = false;
          if (!d.success) {
            self.error = d.error || 'Ошибка контекста';
            return;
          }
          self.context = d.context;
          self.loadTables();
        });
      },
      loadTables: function () {
        var self = this;
        api('GET', API + '/db/tables' + qs({
          datasource_id: self.datasourceId,
          database: self.database,
          search: self.tableSearch
        })).then(function (d) {
          if (d.success) self.tables = d.tables || [];
        });
      },
      selectTable: function (name) {
        this.activeTable = name;
        this.page = 1;
        this.rows = [];
        this.columns = [];
        this.total = 0;
        this.error = '';
        this.structure = null;
        if (this.activeTab === 'data') {
          this.loadStructure();
          this.loadRows();
        }
        if (this.activeTab === 'structure') this.loadStructure();
      },
      loadRows: function () {
        var self = this;
        if (!self.activeTable) return;
        self.loading = true;
        api('GET', API + '/db/table/' + encodeURIComponent(self.activeTable) + '/rows' + qs({
          datasource_id: self.datasourceId,
          database: self.database,
          page: self.page,
          page_size: self.pageSize,
          sort: self.sortCol,
          order: self.sortOrder
        })).then(function (d) {
          self.loading = false;
          if (!d.success) {
            self.rows = [];
            self.total = 0;
            if (self.structure && self.structure.columns && self.structure.columns.length) {
              self.columns = self.structure.columns.map(function (c) { return c.name; });
            } else {
              self.columns = [];
            }
            self.error = d.error || 'Ошибка загрузки';
            return;
          }
          self.error = '';
          self.rows = d.rows || [];
          self.total = d.total || 0;
          self.sortCol = d.sort || self.sortCol;
          self.sortOrder = d.order || self.sortOrder;
          if (self.rows.length) {
            self.columns = Object.keys(self.rows[0]);
          } else if (self.structure && self.structure.columns && self.structure.columns.length) {
            self.columns = self.structure.columns.map(function (c) { return c.name; });
          }
        });
      },
      loadStructure: function () {
        var self = this;
        if (!self.activeTable) return Promise.resolve();
        return api('GET', API + '/db/table/' + encodeURIComponent(self.activeTable) + '/structure' + self.ctxQuery).then(function (d) {
          if (d.success) {
            self.structure = d.structure;
            self.pkCol = (d.structure.primary_key && d.structure.primary_key[0]) || self.pkCol || 'id';
            if (!self.rows.length && self.activeTab === 'data' && d.structure.columns) {
              self.columns = d.structure.columns.map(function (c) { return c.name; });
            }
          }
          return d;
        });
      },
      getModalFields: function () {
        if (this.modalMode === 'edit') {
          return Object.keys(this.modalData);
        }
        if (!this.structure || !this.structure.columns) return [];
        return this.structure.columns.map(function (c) { return c.name; });
      },
      getColumnMeta: function (name) {
        if (!this.structure || !this.structure.columns) return null;
        for (var i = 0; i < this.structure.columns.length; i++) {
          if (this.structure.columns[i].name === name) return this.structure.columns[i];
        }
        return null;
      },
      initCreateModalData: function () {
        var data = {};
        if (!this.structure || !this.structure.columns) return data;
        this.structure.columns.forEach(function (c) {
          data[c.name] = '';
        });
        return data;
      },
      sortBy: function (col) {
        if (this.sortCol === col) {
          this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
        } else {
          this.sortCol = col;
          this.sortOrder = 'asc';
        }
        this.loadRows();
      },
      openEdit: function (row) {
        if (this.readonly) return;
        this.modalMode = 'edit';
        this.modalData = Object.assign({}, row);
        this.editPk = row[this.pkCol];
        this.modalOpen = true;
      },
      openCreate: function () {
        if (this.readonly) return;
        var self = this;
        self.modalMode = 'create';
        self.modalData = {};
        self.editPk = null;
        self.modalOpen = true;
        self.loadStructure().then(function () {
          if (self.modalMode !== 'create' || !self.modalOpen) return;
          self.modalData = self.initCreateModalData();
        });
      },
      saveModal: function () {
        var self = this;
        var path = API + '/db/table/' + encodeURIComponent(self.activeTable) + '/rows';
        if (self.modalMode === 'create') {
          var payload = {};
          Object.keys(self.modalData).forEach(function (k) {
            var v = self.modalData[k];
            if (v !== '' && v != null) payload[k] = v;
          });
          api('POST', path + self.ctxQuery, { data: payload }).then(function (d) {
            if (d.success) { self.modalOpen = false; self.loadRows(); }
            else alert(d.error || 'Ошибка');
          });
        } else {
          api('PATCH', path + '/' + encodeURIComponent(self.editPk) + self.ctxQuery, { data: self.modalData }).then(function (d) {
            if (d.success) { self.modalOpen = false; self.loadRows(); }
            else alert(d.error || 'Ошибка');
          });
        }
      },
      deleteRow: function (row) {
        var self = this;
        if (self.readonly) return;
        var reason = window.prompt('Причина удаления (необязательно):', '');
        if (reason === null) return;
        if (!confirm('Удалить строку?' + (reason ? ' Причина: ' + reason : ''))) return;
        var pk = row[self.pkCol];
        api('DELETE', API + '/db/table/' + encodeURIComponent(self.activeTable) + '/rows/' + encodeURIComponent(pk) + self.ctxQuery).then(function (d) {
          if (d.success) self.loadRows();
          else alert(d.error || 'Ошибка');
        });
      },
      runSql: function () {
        var self = this;
        var kind = classifySql(self.sqlText);
        if (kind === 'blocked') {
          alert('DROP DATABASE/SCHEMA запрещено');
          return;
        }
        var body = { sql: self.sqlText };
        if (kind === 'ddl') {
          if (!self.sqlSchemaMode) {
            alert('DDL требует режима db_schema');
            return;
          }
          if (!confirm('Выполнить DDL-запрос? Это изменит схему базы данных.')) return;
          body.allow_schema = true;
        } else if (kind === 'dml') {
          if (!self.sqlWriteMode) {
            alert('DML требует режима db_sql_execute');
            return;
          }
          if (!confirm('Выполнить изменяющий SQL-запрос?')) return;
          body.allow_write = true;
        }
        api('POST', API + '/db/sql' + self.ctxQuery, body).then(function (d) {
          self.sqlResult = d;
          if (self.sqlText && self.sqlText.trim()) {
            self.sqlHistory = saveSqlHistory(self.sqlText.trim());
          }
        });
      },
      applyHistory: function (sql) {
        this.sqlText = sql;
      },
      switchTab: function (tab) {
        this.activeTab = tab;
        if (tab === 'data' && this.activeTable) {
          this.loadStructure();
          this.loadRows();
        }
        if (tab === 'structure' && this.activeTable) this.loadStructure();
      }
    },
    render: function () {
      var self = this;
      var ctxBadge = self.context
        ? h('span', { class: self.contextBadgeClass }, self.contextBadgeLabel)
        : null;

      function dbOptionLabel(db) {
        if (db.system_reason === 'platform') return db.name + ' [ядро]';
        if (db.blocks_data_read || db.system_reason === 'catalog' || db.system_reason === 'config' || db.system_reason === 'engine_rule') {
          return db.name + ' [sys]';
        }
        if (db.is_system && db.system_reason) return db.name + ' [' + db.system_reason + ']';
        return db.name;
      }

      var sidebar = h('div', {
        class: 'db-studio-sidebar' + (self.sidebarCollapsed ? ' collapsed' : ''),
        'aria-label': 'Список таблиц'
      }, [
        h('button', {
          type: 'button',
          class: 'btn btn-sm btn-outline-secondary mb-2 w-100',
          'aria-label': self.sidebarCollapsed ? 'Развернуть панель таблиц' : 'Свернуть панель таблиц',
          onClick: function () { self.sidebarCollapsed = !self.sidebarCollapsed; }
        }, self.sidebarCollapsed ? '▶ Таблицы' : '◀ Свернуть'),
        self.sidebarCollapsed ? null : h('input', {
          class: 'form-control form-control-sm mb-2',
          placeholder: 'Поиск таблиц...',
          'aria-label': 'Поиск таблиц',
          value: self.tableSearch,
          onInput: function (e) { self.tableSearch = e.target.value; }
        }),
        self.sidebarCollapsed ? null : self.filteredTables.map(function (t) {
          return h('div', {
            class: 'db-studio-object-item' + (self.activeTable === t.name ? ' active' : ''),
            role: 'button',
            tabindex: 0,
            'aria-label': 'Таблица ' + t.name,
            onClick: function () { self.selectTable(t.name); }
          }, [t.name, t.type === 'view' ? ' (view)' : '']);
        })
      ]);

      var dataPanel = self.dataReadBlocked
        ? h('div', { class: 'db-studio-readonly mb-2' }, [
            h('strong', {}, self.activeTable || ''),
            h('span', {}, ' — просмотр данных недоступен (системный каталог СУБД). Доступны вкладки «Структура» и SQL read-only.')
          ])
        : self.readonly
          ? h('div', { class: 'db-studio-readonly mb-2' }, [
              h('strong', {}, self.activeTable || ''),
              h('span', {}, self.context && self.context.is_platform
                ? ' — БД ядра: просмотр данных доступен, изменение через CRUD недоступно.'
                : ' — режим только чтения (недостаточно прав db_edit).')
            ])
          : h('div', { class: 'mb-2' }, [
              h('button', { class: 'btn btn-sm btn-primary', onClick: self.openCreate }, 'Добавить строку')
            ]);

      var grid = self.activeTable && self.activeTab === 'data'
        ? h('div', { class: 'db-studio-grid-wrap db-studio-grid' }, [
            dataPanel,
            self.columns.length
              ? h('table', { class: 'table table-sm table-striped' }, [
                  h('thead', {}, [
                    h('tr', {}, self.columns.map(function (c) {
                      return h('th', { onClick: function () { self.sortBy(c); } }, [
                        c,
                        self.sortCol === c ? (self.sortOrder === 'asc' ? ' ▲' : ' ▼') : ''
                      ]);
                    }).concat(self.readonly ? [] : [h('th', {}, 'Действия')]))
                  ]),
                  h('tbody', {}, self.rows.length
                    ? self.rows.map(function (row) {
                        return h('tr', {}, self.columns.map(function (c) {
                          return h('td', {}, String(row[c] != null ? row[c] : ''));
                        }).concat(self.readonly ? [] : [
                          h('td', {}, [
                            h('button', { class: 'btn btn-link btn-sm p-0 me-2', onClick: function () { self.openEdit(row); } }, 'Изм.'),
                            h('button', { class: 'btn btn-link btn-sm p-0 text-danger', onClick: function () { self.deleteRow(row); } }, 'Удал.')
                          ])
                        ]));
                      })
                    : [h('tr', {}, [h('td', { colspan: self.columns.length + (self.readonly ? 0 : 1), class: 'text-muted' }, 'Нет данных')])]
                  )
                ])
              : h('p', { class: 'text-muted mb-0' }, self.loading ? 'Загрузка…' : 'Нет колонок для отображения'),
            h('div', { class: 'd-flex gap-2 align-items-center mt-2' }, [
              h('button', { class: 'btn btn-sm btn-outline-secondary', disabled: self.page <= 1, onClick: function () { self.page--; self.loadRows(); } }, 'Назад'),
              h('span', {}, 'Стр. ' + self.page + ' / ' + self.totalPages + ' (' + self.total + ')'),
              h('button', { class: 'btn btn-sm btn-outline-secondary', disabled: self.page >= self.totalPages, onClick: function () { self.page++; self.loadRows(); } }, 'Вперёд'),
              h('select', {
                class: 'form-select form-select-sm',
                style: 'width:auto',
                value: String(self.pageSize),
                onChange: function (e) { self.pageSize = parseInt(e.target.value, 10); self.page = 1; self.loadRows(); }
              }, [25, 50, 100].map(function (n) { return h('option', { value: String(n) }, String(n)); }))
            ])
          ])
        : h('p', { class: 'text-muted' }, 'Выберите таблицу');

      var structPanel = self.activeTab === 'structure' && self.structure
        ? h('div', {}, [
            h('p', {}, 'PK: ' + (self.structure.primary_key || []).join(', ')),
            h('table', { class: 'table table-sm' }, [
              h('thead', {}, h('tr', {}, ['Колонка', 'Тип', 'Null', 'PK'].map(function (x) { return h('th', {}, x); }))),
              h('tbody', {}, (self.structure.columns || []).map(function (c) {
                return h('tr', {}, [
                  h('td', {}, c.name),
                  h('td', {}, c.data_type),
                  h('td', {}, c.nullable ? 'YES' : 'NO'),
                  h('td', {}, c.is_primary_key ? '✓' : '')
                ]);
              }))
            ])
          ])
        : null;

      var sqlPanel = self.activeTab === 'sql'
        ? h('div', {}, [
            h('div', { class: 'd-flex gap-2 mb-2 flex-wrap align-items-center' }, [
              h('span', {
                class: 'badge ' + (self.readonly ? 'bg-secondary' : 'bg-success'),
                'aria-label': 'Режим доступа к данным'
              }, self.readonly ? 'read-only' : 'read/write'),
              h('span', {
                class: 'badge ' + (self.sqlWriteMode ? 'bg-warning text-dark' : 'bg-light text-dark border'),
                'aria-label': 'Режим DML'
              }, self.sqlWriteMode ? 'DML mode' : 'DML off'),
              h('span', {
                class: 'badge ' + (self.sqlSchemaMode ? 'bg-danger' : 'bg-light text-dark border'),
                'aria-label': 'Режим DDL'
              }, self.sqlSchemaMode ? 'DDL mode' : 'DDL off')
            ]),
            h('div', { class: 'form-check mb-2' }, [
              h('input', { type: 'checkbox', class: 'form-check-input', id: 'sqlWrite', checked: self.sqlWriteMode, onChange: function (e) { self.sqlWriteMode = e.target.checked; } }),
              h('label', { class: 'form-check-label', for: 'sqlWrite' }, 'Write mode (db_sql_execute / DML)')
            ]),
            h('div', { class: 'form-check mb-2' }, [
              h('input', { type: 'checkbox', class: 'form-check-input', id: 'sqlSchema', checked: self.sqlSchemaMode, onChange: function (e) { self.sqlSchemaMode = e.target.checked; } }),
              h('label', { class: 'form-check-label', for: 'sqlSchema' }, 'Schema mode (db_schema / DDL)')
            ]),
            self.sqlHistory.length ? h('div', { class: 'mb-2' }, [
              h('label', { class: 'form-label small' }, 'История SQL'),
              h('select', {
                class: 'form-select form-select-sm',
                'aria-label': 'История SQL запросов',
                onChange: function (e) {
                  if (e.target.value) self.applyHistory(e.target.value);
                }
              }, [h('option', { value: '' }, '— выбрать —')].concat(
                self.sqlHistory.map(function (item, idx) {
                  return h('option', { value: item }, '#' + (idx + 1) + ': ' + item.substring(0, 60));
                })
              ))
            ]) : null,
            h('textarea', {
              class: 'form-control mb-2',
              rows: 6,
              'aria-label': 'SQL запрос',
              value: self.sqlText,
              onInput: function (e) { self.sqlText = e.target.value; }
            }),
            h('button', {
              class: 'btn btn-primary btn-sm',
              'aria-label': 'Выполнить SQL',
              onClick: self.runSql
            }, 'Выполнить'),
            self.sqlResult ? h('pre', { class: 'mt-2 small bg-light p-2', 'aria-live': 'polite' }, JSON.stringify(self.sqlResult, null, 2)) : null
          ])
        : null;

      var modal = self.modalOpen
        ? h('div', { class: 'modal d-block', style: 'background:rgba(0,0,0,0.4)' }, [
            h('div', { class: 'modal-dialog' }, [
              h('div', { class: 'modal-content' }, [
                h('div', { class: 'modal-header' }, h('h5', {}, self.modalMode === 'create' ? 'Новая строка' : 'Редактирование')),
                h('div', { class: 'modal-body' }, self.getModalFields().length
                  ? self.getModalFields().map(function (key) {
                    var meta = self.getColumnMeta(key);
                    var label = key;
                    if (meta) {
                      label = key + ' (' + meta.data_type + (meta.nullable ? '' : ', NOT NULL') + ')';
                    }
                    return h('div', { class: 'mb-2' }, [
                      h('label', { class: 'form-label' }, label),
                      h('input', {
                        class: 'form-control',
                        value: self.modalData[key] != null ? String(self.modalData[key]) : '',
                        onInput: function (e) { self.modalData[key] = e.target.value; }
                      })
                    ]);
                  })
                  : [h('p', { class: 'text-muted mb-0' }, 'Загрузка структуры таблицы…')]),
                h('div', { class: 'modal-footer' }, [
                  h('button', { class: 'btn btn-secondary', onClick: function () { self.modalOpen = false; } }, 'Отмена'),
                  h('button', { class: 'btn btn-primary', onClick: self.saveModal }, 'Сохранить')
                ])
              ])
            ])
          ])
        : null;

      return h('div', { class: 'db-studio-app' }, [
        h('div', { class: 'db-studio-context' }, [
          h('strong', {}, 'Студия БД'),
          h('select', {
            class: 'form-select form-select-sm',
            style: 'width:140px',
            value: self.datasourceId,
            onChange: function (e) { self.datasourceId = e.target.value; self.onDatasourceChange(); }
          }, self.datasources.map(function (ds) { return h('option', { value: ds.id }, ds.id); })),
          h('select', {
            class: 'form-select form-select-sm',
            style: 'width:180px',
            value: self.database,
            onChange: function (e) { self.database = e.target.value; self.onDatabaseChange(); }
          }, [h('option', { value: '' }, '— база —')].concat(self.databases.map(function (db) {
            return h('option', { value: db.name }, dbOptionLabel(db));
          }))),
          ctxBadge,
          h('button', { class: 'btn btn-sm btn-outline-primary', onClick: function () { self.onDatabaseChange(); self.loadTables(); } }, 'Обновить')
        ]),
        self.error ? h('div', { class: 'alert alert-danger' }, self.error) : null,
        self.loading ? h('div', { class: 'text-center py-3' }, h('div', { class: 'spinner-border text-primary' })) : null,
        !self.database
          ? h('div', { class: 'text-center text-muted py-5' }, 'Выберите datasource и базу данных')
          : h('div', { class: 'db-studio-layout' }, [
              sidebar,
              h('div', { class: 'db-studio-main' }, [
                h('ul', { class: 'nav nav-tabs mb-3' }, [
                  ['data', 'Данные'], ['structure', 'Структура'], ['sql', 'SQL']
                ].map(function (pair) {
                  return h('li', { class: 'nav-item' }, [
                    h('button', {
                      class: 'nav-link' + (self.activeTab === pair[0] ? ' active' : ''),
                      onClick: function () { self.switchTab(pair[0]); }
                    }, pair[1])
                  ]);
                })),
                self.activeTab === 'data' ? grid : null,
                self.activeTab === 'structure' ? (structPanel || h('p', { class: 'text-muted' }, 'Выберите таблицу')) : null,
                sqlPanel
              ])
            ]),
        modal
      ]);
    }
  }).mount(mountEl);

  var init = parseInitialContext();
  if (init.datasource_id) {
    setTimeout(function () {
      var app = mountEl.__vue_app__;
    }, 0);
  }
})();
