Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6d3fc06a3 | |||
| e67ee4ffdb | |||
| 2baf975847 | |||
| c1672ebc8e | |||
| bbbd291065 | |||
| 0a3c1efdd4 | |||
| 0c15e524d7 | |||
| b0b5b1c30d | |||
| 30b4c85c5a | |||
| 910f9cadfe | |||
| 6de37ebd16 | |||
| de57c4e87e | |||
| b85cf66490 | |||
| 8e638ea9a6 | |||
| b4849ec495 | |||
| 09c12d52ac | |||
| db6a2ddd7e | |||
| 12ef9463ab | |||
| a557ad177e | |||
| 78e838f2f0 | |||
| c1f216c7c7 | |||
| b75ff99e4c | |||
| 780dd8ade9 | |||
| e1c10b7653 | |||
| be9505f8fe | |||
| d6bcd4f94f | |||
| 7d2196f4c3 | |||
| 0539174317 | |||
| b4b52e12d5 | |||
| f2e0b1cfa2 | |||
| 8020e2a263 | |||
| 6112d9b1b0 | |||
| 4a1fbcbd31 | |||
| a02a3230f1 | |||
| 0218bb4990 | |||
| 3769c03565 | |||
| d96cb10476 | |||
| b6b6123434 | |||
| b40877fcc1 | |||
| af5ae29b73 | |||
| 082fceebbe | |||
| f1dab80a06 | |||
| cbf2fac2cf | |||
| 4564bd7180 | |||
| d12ad7b882 | |||
| f8e9f07a00 | |||
| 4ac3891e07 | |||
| a2d55d3fdd | |||
| d015a24300 | |||
| be38acbede | |||
| 34d0cb4dc7 | |||
| 18d0558b19 | |||
| d4469f3a2d | |||
| 43b760b4bf | |||
| 3d47932c09 | |||
| 7196d6e1bf | |||
| ec06a7d861 | |||
| ef23f0d18e | |||
| e9abc5f07f | |||
| 6f9d6ff849 | |||
| eab18d3c11 | |||
| 3a509a6a97 | |||
| 615c6f4e24 | |||
| 96660b4539 | |||
| 1be284974b | |||
| 790b6478dc | |||
| 106344b33e | |||
| 85a2a4b873 | |||
| 4ab694de0c | |||
| 5e193c1725 | |||
| 2b055c028c | |||
| b341749e45 | |||
| 6ae381b1fd | |||
| 352b6cbe04 | |||
| e5e6d2701e | |||
| 9ad1924488 | |||
| 2aadbfc64a | |||
| 1c5d652f93 | |||
| b2355a3b2d | |||
| 4ee6d089d5 | |||
| 43a826e2e5 | |||
| 4ce9faf39b | |||
| d94a67d0af | |||
| e14f59256d |
@@ -43,7 +43,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
|
||||
ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
|
||||
@@ -8,6 +8,20 @@ Builds:
|
||||
- linux - application for linux
|
||||
- win - application for Windows
|
||||
|
||||
## 6.7.3
|
||||
- FIXED: Fixed problem in analyser core - in PostgreSQL, after dropping table, dropped table still appeared in structure
|
||||
- FIXED: PostgreSQL numeric columns do not align right #1254
|
||||
- ADDED: Custom thousands separator #1213
|
||||
|
||||
## 6.7.2
|
||||
- CHANGED: Settings modal redesign - now is settings opened in tab instead of modal, similarily as in VSCode
|
||||
- FIXED: Fixed search in table shortcuts #1273
|
||||
- CHANGED: Improved foreign key editor UX
|
||||
- FIXED: Fixed incremental DB structure refresh for PostgreSQL, optimalized slow loading primary keys in PostgreSQL
|
||||
- CHANGED: You could now choose, how to refresh structure, added ability to disconnect or reconnect
|
||||
- ADDED: Better processing of table backups, generate table restore script #1274
|
||||
- CHANGED: Improved storage of settings, especially for Team Premium edition
|
||||
|
||||
## 6.7.1
|
||||
- ADDED: LANGUAGE environment variable for the web version. #1266
|
||||
- ADDED: New localizations (Italian, Portugese (Brazil), Japanese)
|
||||
|
||||
@@ -10,6 +10,7 @@ module.exports = defineConfig({
|
||||
// baseUrl: 'http://localhost:3000',
|
||||
// trashAssetsBeforeRuns: false,
|
||||
chromeWebSecurity: false,
|
||||
reporter: process.env.CI ? 'mocha-reporter-gha' : 'spec',
|
||||
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
|
||||
@@ -113,6 +113,18 @@ describe('Add connection', () => {
|
||||
cy.contains('performance_schema');
|
||||
});
|
||||
|
||||
it('Plugin tab', () => {
|
||||
cy.testid('WidgetIconPanel_menu').click();
|
||||
cy.contains('Tools').click();
|
||||
cy.contains('Manage plugins').click();
|
||||
cy.contains('dbgate-plugin-theme-total-white').click();
|
||||
// text from plugin markdown
|
||||
cy.contains('Total white theme');
|
||||
// wait for load logos
|
||||
cy.wait(2000);
|
||||
cy.themeshot('view-plugin-tab');
|
||||
});
|
||||
|
||||
it('export connections', () => {
|
||||
cy.testid('WidgetIconPanel_menu').click();
|
||||
cy.contains('Tools').click();
|
||||
|
||||
@@ -310,17 +310,6 @@ describe('Data browser data', () => {
|
||||
cy.themeshot('search-in-connections');
|
||||
});
|
||||
|
||||
it('Plugin tab', () => {
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Manage plugins').click();
|
||||
cy.contains('dbgate-plugin-theme-total-white').click();
|
||||
// text from plugin markdown
|
||||
cy.contains('Total white theme');
|
||||
// wait for load logos
|
||||
cy.wait(2000);
|
||||
cy.themeshot('view-plugin-tab');
|
||||
});
|
||||
|
||||
it('Edit mongo data JSON', () => {
|
||||
// TODO FIX: Missing button+ctx menu Revert all changes, missing button+ctx menu add document
|
||||
// TODO: Dark theme - not visible changed and deleted document
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('Charts', () => {
|
||||
cy.themeshot('new-object-window');
|
||||
});
|
||||
|
||||
it('Database chat - charts', () => {
|
||||
it.skip('Database chat - charts', () => {
|
||||
cy.contains('MySql-connection').click();
|
||||
cy.contains('MyChinook').click();
|
||||
cy.testid('TabsPanel_buttonNewObject').click();
|
||||
@@ -125,7 +125,7 @@ describe('Charts', () => {
|
||||
cy.themeshot('database-chat-chart');
|
||||
});
|
||||
|
||||
it('Database chat', () => {
|
||||
it.skip('Database chat', () => {
|
||||
cy.contains('MySql-connection').click();
|
||||
cy.contains('MyChinook').click();
|
||||
cy.testid('TabsPanel_buttonNewObject').click();
|
||||
@@ -146,7 +146,7 @@ describe('Charts', () => {
|
||||
// cy.themeshot('database-chat');
|
||||
});
|
||||
|
||||
it('Explain query error', () => {
|
||||
it.skip('Explain query error', () => {
|
||||
cy.contains('MySql-connection').click();
|
||||
cy.contains('MyChinook').click();
|
||||
cy.testid('TabsPanel_buttonNewObject').click();
|
||||
@@ -163,45 +163,109 @@ describe('Charts', () => {
|
||||
cy.contains('MySql-connection').click();
|
||||
cy.contains('MyChinook').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Settings').click();
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Deutsch');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Einstellungen').click();
|
||||
cy.contains('Lokalisierung');
|
||||
cy.contains('Sprache');
|
||||
cy.themeshot('switch-language-de');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Français');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Paramètres').click();
|
||||
cy.contains('Localisation');
|
||||
cy.contains('Langue');
|
||||
cy.themeshot('switch-language-fr');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Español');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Configuración').click();
|
||||
cy.contains('Localización');
|
||||
cy.contains('Idioma');
|
||||
cy.themeshot('switch-language-es');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('Čeština');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Nastavení').click();
|
||||
cy.contains('Lokalizace');
|
||||
cy.contains('Jazyk');
|
||||
cy.themeshot('switch-language-cs');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('中文');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('设置').click();
|
||||
cy.contains('本地化');
|
||||
cy.contains('语言');
|
||||
cy.themeshot('switch-language-zh');
|
||||
|
||||
cy.testid('SettingsModal_languageSelect').select('English');
|
||||
cy.testid('ConfirmModal_okButton').click();
|
||||
cy.testid('WidgetIconPanel_settings');
|
||||
});
|
||||
|
||||
it('Settings', () => {
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.themeshot('app-settings-general');
|
||||
|
||||
cy.contains('Behaviour').click();
|
||||
cy.themeshot('app-settings-behaviour');
|
||||
cy.get('[data-testid=BehaviourSettings_useTabPreviewMode]').uncheck();
|
||||
|
||||
// SQL Editor
|
||||
cy.contains('SQL Editor').click();
|
||||
cy.get('[data-testid=SQLEditorSettings_sqlCommandsCase]').select('lowerCase');
|
||||
|
||||
cy.contains('MySql-connection').click();
|
||||
cy.contains('charts_sample').click();
|
||||
cy.contains('employees').click();
|
||||
cy.contains('MyChinook').click();
|
||||
cy.contains('Customer').rightclick();
|
||||
cy.contains('SQL template').click();
|
||||
cy.contains('CREATE TABLE').click();
|
||||
cy.contains('create table');
|
||||
|
||||
// Default Actions
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Default Actions').click();
|
||||
cy.get('[data-testid=DefaultActionsSettings_useLastUsedAction]').uncheck();
|
||||
|
||||
|
||||
// Themes
|
||||
cy.contains('Themes').click();
|
||||
cy.themeshot('app-settings-themes');
|
||||
cy.contains('Dark').click();
|
||||
cy.get('body').find('.theme-dark').should('exist');
|
||||
cy.contains('Light').click();
|
||||
cy.get('body').find('.theme-light').should('exist');
|
||||
|
||||
// General
|
||||
cy.contains(/^General$/).click();
|
||||
cy.contains('charts_sample');
|
||||
cy.get('[data-testid=GeneralSettings_lockedDatabaseMode]').check();
|
||||
cy.contains('Connections').click();
|
||||
cy.contains('charts_sample').should('not.exist');
|
||||
|
||||
// Datagrid
|
||||
cy.contains('Data grid').click();
|
||||
cy.get('[data-testid=DataGridSettings_showHintColumns]').uncheck();
|
||||
cy.wait(500);
|
||||
cy.contains('Album').click();
|
||||
cy.contains('AC/DC').should('not.exist');
|
||||
|
||||
cy.testid('WidgetIconPanel_settings').click();
|
||||
cy.contains('Keyboard shortcuts').click();
|
||||
cy.themeshot('app-settings-keyboard-shortcuts');
|
||||
cy.contains('Chart').click();
|
||||
cy.testid('CommandModal_keyboardButton').click();
|
||||
cy.realPress(['Control', 'g']);
|
||||
cy.realPress('Enter');
|
||||
cy.contains('OK').click();
|
||||
cy.contains('Ctrl+G');
|
||||
|
||||
|
||||
cy.contains('AI').click();
|
||||
cy.themeshot('app-settings-ai');
|
||||
cy.get('[data-testid=AISettings_addProviderButton]').click();
|
||||
cy.contains('Provider 1');
|
||||
cy.get('[data-testid=AiProviderCard_removeButton]').click();
|
||||
cy.contains('Are you sure you want to remove Provider 1 provider?');
|
||||
cy.contains('OK').click();
|
||||
cy.contains('Provider 1').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -103,13 +103,70 @@ describe('Transactions', () => {
|
||||
|
||||
describe('Backup table', () => {
|
||||
multiTest({ skipMongo: true }, (connectionName, databaseName, engine, options = {}) => {
|
||||
const implicitTransactions = options.implicitTransactions ?? false;
|
||||
|
||||
cy.contains(connectionName).click();
|
||||
if (databaseName) cy.contains(databaseName).click();
|
||||
cy.contains('customers').rightclick();
|
||||
cy.contains('addresses').rightclick();
|
||||
cy.contains('Create table backup').click();
|
||||
cy.testid('ConfirmSqlModal_okButton').click();
|
||||
cy.contains('_customers').click();
|
||||
cy.contains('Rows: 8').should('be.visible');
|
||||
cy.testid('app-object-group-items-table-backups').contains('addresses').click();
|
||||
cy.contains('Rows: 12').should('be.visible');
|
||||
cy.testid('app-object-group-items-tables').contains('addresses').click();
|
||||
|
||||
cy.contains('Ridgewood').click();
|
||||
cy.testid('TableDataTab_deleteSelectedRows').click();
|
||||
cy.contains('Rosewood').click();
|
||||
cy.testid('TableDataTab_deleteSelectedRows').click();
|
||||
|
||||
cy.contains('Vermont').click();
|
||||
cy.get('body').realType('Wermont{enter}');
|
||||
|
||||
cy.testid('TableDataTab_insertNewRow').click();
|
||||
cy.get('body').realType('Modranska{enter}');
|
||||
cy.realPress(['ArrowLeft']);
|
||||
cy.realPress(['ArrowLeft']);
|
||||
cy.get('body').realType('13{enter}');
|
||||
cy.realPress(['ArrowRight']);
|
||||
cy.get('body').realType('1{enter}');
|
||||
cy.realPress(['ArrowRight']);
|
||||
cy.realPress(['ArrowRight']);
|
||||
cy.realPress(['ArrowRight']);
|
||||
cy.get('body').realType('Prague{enter}');
|
||||
cy.realPress(['ArrowRight']);
|
||||
cy.get('body').realType('CZ{enter}');
|
||||
cy.realPress(['ArrowRight']);
|
||||
cy.get('body').realType('10000{enter}');
|
||||
cy.realPress(['ArrowRight']);
|
||||
cy.get('body').realType('111222333{enter}');
|
||||
|
||||
cy.testid('TableDataTab_save').click();
|
||||
cy.testid('ConfirmSqlModal_okButton').click();
|
||||
cy.contains('Rows: 11').should('be.visible'); // wait for save
|
||||
|
||||
cy.testid('app-object-group-items-table-backups').contains('addresses').rightclick();
|
||||
cy.contains('restore script').click();
|
||||
cy.contains('UPDATE'); // wait for query
|
||||
cy.testid('QueryTab_executeButton').click();
|
||||
cy.contains('Query execution finished');
|
||||
|
||||
if (implicitTransactions) {
|
||||
cy.testid('QueryTab_commitTransactionButton').click();
|
||||
cy.contains('Commit Transaction finished');
|
||||
}
|
||||
|
||||
cy.realPress('F1');
|
||||
cy.realType('Close all');
|
||||
cy.realPress('Enter');
|
||||
// cy.testid('CloseTabModal_buttonConfirm').click();
|
||||
cy.wait(1000);
|
||||
|
||||
cy.testid('app-object-group-items-tables').contains('addresses').click();
|
||||
|
||||
// check whether data was successfully restored
|
||||
cy.contains('Rows: 12').should('be.visible');
|
||||
cy.contains('Ridgewood');
|
||||
cy.contains('Vermont');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -146,13 +203,15 @@ describe('Import CSV', () => {
|
||||
cy.contains('Import').click();
|
||||
|
||||
cy.get('input[type=file]').selectFile('cypress/fixtures/customers-20.csv', { force: true });
|
||||
cy.contains('customers-20');
|
||||
cy.testid('ImportExportConfigurator_tableMappingSection').contains('customers-20');
|
||||
cy.testid('ImportExportTab_preview_content').contains('50ddd99fAdF48B3').should('be.visible');
|
||||
|
||||
cy.testid('ImportExportTab_executeButton').click();
|
||||
cy.contains('20 rows written').should('be.visible');
|
||||
cy.testid('ImportExportConfigurator_tableMappingSection').contains('20 rows written').should('be.visible');
|
||||
|
||||
cy.testid('SqlObjectList_refreshButton').click();
|
||||
cy.testid('DatabasStatusMenu_refreshFull').click();
|
||||
// cy.contains('Refresh DB structure (incremental)').click();
|
||||
cy.testid('SqlObjectList_container').contains('customers-20').click();
|
||||
cy.contains('Rows: 20').should('be.visible');
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
"cypress-real-events": "^1.13.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"kill-port": "^2.0.1",
|
||||
"mocha-reporter-gha": "^1.1.1",
|
||||
"start-server-and-test": "^2.0.8"
|
||||
},
|
||||
"scripts": {
|
||||
"cy:open": "cypress open --config experimentalInteractiveRunEvents=true",
|
||||
|
||||
"cy:run:add-connection": "cypress run --spec cypress/e2e/add-connection.cy.js",
|
||||
"cy:run:portal": "cypress run --spec cypress/e2e/portal.cy.js",
|
||||
"cy:run:oauth": "cypress run --spec cypress/e2e/oauth.cy.js",
|
||||
@@ -23,7 +23,6 @@
|
||||
"cy:run:multi-sql": "cypress run --spec cypress/e2e/multi-sql.cy.js",
|
||||
"cy:run:cloud": "cypress run --spec cypress/e2e/cloud.cy.js",
|
||||
"cy:run:charts": "cypress run --spec cypress/e2e/charts.cy.js",
|
||||
|
||||
"start:add-connection": "node clearTestingData && cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests",
|
||||
"start:portal": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/portal/.env node e2e-tests/init/portal.js && env-cmd -f e2e-tests/env/portal/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
|
||||
"start:oauth": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/oauth/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
|
||||
@@ -32,7 +31,6 @@
|
||||
"start:multi-sql": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/multi-sql/.env node e2e-tests/init/multi-sql.js && env-cmd -f e2e-tests/env/multi-sql/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
|
||||
"start:cloud": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/cloud/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
|
||||
"start:charts": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/charts/.env node e2e-tests/init/charts.js && env-cmd -f e2e-tests/env/charts/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
|
||||
|
||||
"test:add-connection": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection",
|
||||
"test:portal": "start-server-and-test start:portal http://localhost:3000 cy:run:portal",
|
||||
"test:oauth": "start-server-and-test start:oauth http://localhost:3000 cy:run:oauth",
|
||||
@@ -41,7 +39,6 @@
|
||||
"test:multi-sql": "start-server-and-test start:multi-sql http://localhost:3000 cy:run:multi-sql",
|
||||
"test:cloud": "start-server-and-test start:cloud http://localhost:3000 cy:run:cloud",
|
||||
"test:charts": "start-server-and-test start:charts http://localhost:3000 cy:run:charts",
|
||||
|
||||
"test": "yarn test:add-connection && yarn test:portal && yarn test:oauth && yarn test:browse-data && yarn test:team && yarn test:multi-sql && yarn test:cloud && yarn test:charts",
|
||||
"test:ci": "yarn test"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,34 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@actions/core@^1.10.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.11.1.tgz#ae683aac5112438021588030efb53b1adb86f172"
|
||||
integrity sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==
|
||||
dependencies:
|
||||
"@actions/exec" "^1.1.1"
|
||||
"@actions/http-client" "^2.0.1"
|
||||
|
||||
"@actions/exec@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@actions/exec/-/exec-1.1.1.tgz#2e43f28c54022537172819a7cf886c844221a611"
|
||||
integrity sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==
|
||||
dependencies:
|
||||
"@actions/io" "^1.0.1"
|
||||
|
||||
"@actions/http-client@^2.0.1":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674"
|
||||
integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==
|
||||
dependencies:
|
||||
tunnel "^0.0.6"
|
||||
undici "^5.25.4"
|
||||
|
||||
"@actions/io@^1.0.1":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.1.3.tgz#4cdb6254da7962b07473ff5c335f3da485d94d71"
|
||||
integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==
|
||||
|
||||
"@colors/colors@1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||
@@ -39,6 +67,11 @@
|
||||
debug "^3.1.0"
|
||||
lodash.once "^4.1.1"
|
||||
|
||||
"@fastify/busboy@^2.0.0":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
|
||||
integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
|
||||
|
||||
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
||||
@@ -947,6 +980,13 @@ minimist@^1.2.8:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
mocha-reporter-gha@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/mocha-reporter-gha/-/mocha-reporter-gha-1.1.1.tgz#e1248abd0769f55b57b36ccd7db2b0b6573d5adf"
|
||||
integrity sha512-CFbcgM56V4yWlbF91XuwrE6a5X/IqjVXTPefO7m8cY8Es8G1UhJ2KKOrk16AcSemRzVWXp2Fdy3bWJ7j45snWw==
|
||||
dependencies:
|
||||
"@actions/core" "^1.10.1"
|
||||
|
||||
ms@^2.1.1, ms@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
@@ -1292,6 +1332,11 @@ tunnel-agent@^0.6.0:
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
tunnel@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
|
||||
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
|
||||
|
||||
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
version "0.14.5"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
@@ -1307,6 +1352,13 @@ undici-types@~6.20.0:
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
|
||||
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
|
||||
|
||||
undici@^5.25.4:
|
||||
version "5.29.0"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3"
|
||||
integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==
|
||||
dependencies:
|
||||
"@fastify/busboy" "^2.0.0"
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
|
||||
|
||||
@@ -33,7 +33,9 @@ describe('Schema tests', () => {
|
||||
expect(schemas2.find(x => x.schemaName == 'myschema')).toBeTruthy();
|
||||
expect(schemas2.length).toEqual(count + 1);
|
||||
expect(schemas2.find(x => x.isDefault).schemaName).toEqual(engine.defaultSchemaName);
|
||||
expect(structure2).toBeNull();
|
||||
if (!engine.skipIncrementalAnalysis) {
|
||||
expect(structure2).toBeNull();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -51,7 +53,9 @@ describe('Schema tests', () => {
|
||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||
const schemas2 = await driver.listSchemas(conn);
|
||||
expect(schemas2.find(x => x.schemaName == 'myschema')).toBeFalsy();
|
||||
expect(structure2).toBeNull();
|
||||
if (!engine.skipIncrementalAnalysis) {
|
||||
expect(structure2).toBeNull();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ describe('Table analyse', () => {
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table add - incremental analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||
@@ -112,7 +112,7 @@ describe('Table analyse', () => {
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table remove - incremental analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine)));
|
||||
@@ -130,7 +130,7 @@ describe('Table analyse', () => {
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table change - incremental analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine)));
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "6.7.2-alpha.1",
|
||||
"version": "6.7.3",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
@@ -75,6 +75,7 @@
|
||||
"translations:remove-unused": "node common/translations-cli/index.js remove-unused",
|
||||
"translations:check": "node common/translations-cli/index.js check",
|
||||
"translations:sort": "node common/translations-cli/index.js sort",
|
||||
"translations:translate": "node common/translations-cli/translate.js",
|
||||
"errors": "node common/assign-dbgm-codes.mjs ."
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -289,16 +289,11 @@ module.exports = {
|
||||
const res = await lock.acquire('settings', async () => {
|
||||
const currentValue = await this.loadSettings();
|
||||
try {
|
||||
let updated = currentValue;
|
||||
let updated = {
|
||||
...currentValue,
|
||||
...values,
|
||||
};
|
||||
if (process.env.STORAGE_DATABASE) {
|
||||
updated = {
|
||||
...currentValue,
|
||||
..._.mapValues(values, v => {
|
||||
if (v === true) return 'true';
|
||||
if (v === false) return 'false';
|
||||
return v;
|
||||
}),
|
||||
};
|
||||
await storage.writeConfig({
|
||||
group: 'settings',
|
||||
config: updated,
|
||||
|
||||
@@ -360,6 +360,12 @@ module.exports = {
|
||||
"columnName": "value",
|
||||
"dataType": "varchar(1000)",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"pureName": "config",
|
||||
"columnName": "valueType",
|
||||
"dataType": "varchar(50)",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
|
||||
@@ -21,6 +21,7 @@ export function getFilterValueExpression(value, dataType?) {
|
||||
if (value === false) return 'FALSE';
|
||||
if (value.$oid) return `ObjectId("${value.$oid}")`;
|
||||
if (value.$bigint) return value.$bigint;
|
||||
if (value.$decimal) return value.$decimal;
|
||||
if (value.type == 'Buffer' && Array.isArray(value.data)) {
|
||||
return '0x' + arrayToHexString(value.data);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ function isLike(value, test) {
|
||||
function extractRawValue(value) {
|
||||
if (value?.$bigint) return value.$bigint;
|
||||
if (value?.$oid) return value.$oid;
|
||||
if (value?.$decimal) return value.$decimal;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,11 @@ export class DatabaseAnalyser<TClient = any> {
|
||||
|
||||
const res = {};
|
||||
for (const field of STRUCTURE_FIELDS) {
|
||||
const isAll = this.modifications.some(x => x.action == 'all' && x.objectTypeField == field);
|
||||
if (isAll) {
|
||||
res[field] = newlyAnalysed[field] || [];
|
||||
continue;
|
||||
}
|
||||
const removedIds = this.modifications
|
||||
.filter(x => x.action == 'remove' && x.objectTypeField == field)
|
||||
.map(x => x.objectId);
|
||||
|
||||
@@ -87,6 +87,7 @@ export class SqlDumper implements AlterProcessor {
|
||||
this.putByteArrayValue(bytes);
|
||||
}
|
||||
else if (value?.$bigint) this.putRaw(value?.$bigint);
|
||||
else if (value?.$decimal) this.putRaw(value?.$decimal);
|
||||
else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value));
|
||||
else this.put('^null');
|
||||
}
|
||||
|
||||
@@ -45,14 +45,15 @@ export function hexStringToArray(inputString) {
|
||||
|
||||
export function base64ToHex(base64String) {
|
||||
const binaryString = atob(base64String);
|
||||
const hexString = Array.from(binaryString, c =>
|
||||
c.charCodeAt(0).toString(16).padStart(2, '0')
|
||||
).join('');
|
||||
const hexString = Array.from(binaryString, c => c.charCodeAt(0).toString(16).padStart(2, '0')).join('');
|
||||
return '0x' + hexString.toUpperCase();
|
||||
};
|
||||
}
|
||||
|
||||
export function hexToBase64(hexString) {
|
||||
const binaryString = hexString.match(/.{1,2}/g).map(byte => String.fromCharCode(parseInt(byte, 16))).join('');
|
||||
const binaryString = hexString
|
||||
.match(/.{1,2}/g)
|
||||
.map(byte => String.fromCharCode(parseInt(byte, 16)))
|
||||
.join('');
|
||||
return btoa(binaryString);
|
||||
}
|
||||
|
||||
@@ -68,9 +69,9 @@ export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) {
|
||||
if (mHex) {
|
||||
return {
|
||||
$binary: {
|
||||
base64: hexToBase64(value.substring(2))
|
||||
}
|
||||
}
|
||||
base64: hexToBase64(value.substring(2)),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +201,26 @@ function stringifyJsonToGrid(value): ReturnType<typeof stringifyCellValue> {
|
||||
return { value: '(JSON)', gridStyle: 'nullCellStyle' };
|
||||
}
|
||||
|
||||
function formatNumberCustomSeparator(value, thousandsSeparator) {
|
||||
const [intPart, decPart] = value.split('.');
|
||||
const intPartWithSeparator = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
|
||||
return decPart ? `${intPartWithSeparator}.${decPart}` : intPartWithSeparator;
|
||||
}
|
||||
|
||||
function formatCellNumber(value, gridFormattingOptions?: { thousandsSeparator?: string }) {
|
||||
const separator = gridFormattingOptions?.thousandsSeparator;
|
||||
if (_isNumber(value)) {
|
||||
if (separator === 'none' || (value < 1000 && value > -1000)) return value.toString();
|
||||
if (separator === 'system') return value.toLocaleString();
|
||||
}
|
||||
// fallback for system locale
|
||||
if (separator === 'space' || separator === 'system') return formatNumberCustomSeparator(value.toString(), ' ');
|
||||
if (separator === 'narrowspace') return formatNumberCustomSeparator(value.toString(), '\u202F');
|
||||
if (separator === 'comma') return formatNumberCustomSeparator(value.toString(), ',');
|
||||
if (separator === 'dot') return formatNumberCustomSeparator(value.toString(), '.');
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
export function stringifyCellValue(
|
||||
value,
|
||||
intent:
|
||||
@@ -210,7 +231,7 @@ export function stringifyCellValue(
|
||||
| 'exportIntent'
|
||||
| 'clipboardIntent',
|
||||
editorTypes?: DataEditorTypesBehaviour,
|
||||
gridFormattingOptions?: { useThousandsSeparator?: boolean },
|
||||
gridFormattingOptions?: { thousandsSeparator?: string },
|
||||
jsonParsedValue?: any
|
||||
): {
|
||||
value: string;
|
||||
@@ -251,12 +272,19 @@ export function stringifyCellValue(
|
||||
};
|
||||
}
|
||||
|
||||
if (value?.$decimal) {
|
||||
return {
|
||||
value: formatCellNumber(value.$decimal, gridFormattingOptions),
|
||||
gridStyle: 'valueCellStyle',
|
||||
};
|
||||
}
|
||||
|
||||
if (editorTypes?.parseHexAsBuffer) {
|
||||
// if (value?.type == 'Buffer' && _isArray(value.data)) {
|
||||
// return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' };
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
if (editorTypes?.parseObjectIdAsDollar) {
|
||||
if (value?.$oid) {
|
||||
switch (intent) {
|
||||
@@ -270,13 +298,13 @@ export function stringifyCellValue(
|
||||
}
|
||||
if (value?.$bigint) {
|
||||
return {
|
||||
value: value.$bigint,
|
||||
value: formatCellNumber(value.$bigint, gridFormattingOptions),
|
||||
gridStyle: 'valueCellStyle',
|
||||
};
|
||||
}
|
||||
if (typeof value === 'bigint') {
|
||||
return {
|
||||
value: value.toString(),
|
||||
value: formatCellNumber(value.toString(), gridFormattingOptions),
|
||||
gridStyle: 'valueCellStyle',
|
||||
};
|
||||
}
|
||||
@@ -351,13 +379,8 @@ export function stringifyCellValue(
|
||||
if (_isNumber(value)) {
|
||||
switch (intent) {
|
||||
case 'gridCellIntent':
|
||||
return {
|
||||
value:
|
||||
gridFormattingOptions?.useThousandsSeparator && (value >= 10000 || value <= -10000)
|
||||
? value.toLocaleString()
|
||||
: value.toString(),
|
||||
gridStyle: 'valueCellStyle',
|
||||
};
|
||||
const separator = gridFormattingOptions?.thousandsSeparator;
|
||||
return { value: formatCellNumber(value, gridFormattingOptions), gridStyle: 'valueCellStyle' };
|
||||
default:
|
||||
return { value: value.toString() };
|
||||
}
|
||||
@@ -449,6 +472,9 @@ export function shouldOpenMultilineDialog(value) {
|
||||
if (value?.$bigint) {
|
||||
return false;
|
||||
}
|
||||
if (value?.$decimal) {
|
||||
return false;
|
||||
}
|
||||
if (_isPlainObject(value) || _isArray(value)) {
|
||||
return true;
|
||||
}
|
||||
@@ -699,6 +725,9 @@ export function deserializeJsTypesFromJsonParse(obj) {
|
||||
if (value?.$bigint) {
|
||||
return BigInt(value.$bigint);
|
||||
}
|
||||
if (value?.$decimal) {
|
||||
return value.$decimal;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -713,6 +742,9 @@ export function deserializeJsTypesReviver(key, value) {
|
||||
if (value?.$bigint) {
|
||||
return BigInt(value.$bigint);
|
||||
}
|
||||
if (value?.$decimal) {
|
||||
return value.$decimal;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
@@ -238,6 +238,7 @@ export interface EngineDriver<TClient = any, TDataBase = any> extends FilterBeha
|
||||
supportsDatabaseRestore?: boolean;
|
||||
supportsServerSummary?: boolean;
|
||||
supportsDatabaseProfiler?: boolean;
|
||||
supportsIncrementalAnalysis?: boolean;
|
||||
requiresDefaultSortCriteria?: boolean;
|
||||
profilerFormatterFunction?: string;
|
||||
profilerTimestampFunction?: string;
|
||||
|
||||
@@ -53,14 +53,15 @@
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let data;
|
||||
|
||||
const handleRename = () => {
|
||||
showModal(InputTextModal, {
|
||||
value: data.fileName,
|
||||
label: 'New file name',
|
||||
header: 'Rename file',
|
||||
label: _t('appFile.newFileName', { defaultMessage: 'New file name' }),
|
||||
header: _t('appFile.renameFile', { defaultMessage: 'Rename file' }),
|
||||
onConfirm: newFile => {
|
||||
apiCall('apps/rename-file', {
|
||||
file: data.fileName,
|
||||
@@ -74,7 +75,7 @@
|
||||
|
||||
const handleDelete = () => {
|
||||
showModal(ConfirmModal, {
|
||||
message: `Really delete file ${data.fileName}?`,
|
||||
message: _t('appFile.deleteFileConfirm', { defaultMessage: 'Really delete file {fileName}?', values: { fileName: data.fileName } }),
|
||||
onConfirm: () => {
|
||||
apiCall('apps/delete-file', {
|
||||
file: data.fileName,
|
||||
@@ -101,10 +102,10 @@
|
||||
|
||||
function createMenu() {
|
||||
return [
|
||||
{ text: 'Delete', onClick: handleDelete },
|
||||
{ text: 'Rename', onClick: handleRename },
|
||||
data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile },
|
||||
data.fileType.endsWith('.json') && { text: 'Open JSON', onClick: handleOpenJsonFile },
|
||||
{ text: _t('common.delete', { defaultMessage: 'Delete' }), onClick: handleDelete },
|
||||
{ text: _t('common.rename', { defaultMessage: 'Rename' }), onClick: handleRename },
|
||||
data.fileType.endsWith('.sql') && { text: _t('common.openSql', { defaultMessage: 'Open SQL' }), onClick: handleOpenSqlFile },
|
||||
data.fileType.endsWith('.json') && { text: _t('common.openJson', { defaultMessage: 'Open JSON' }), onClick: handleOpenJsonFile },
|
||||
|
||||
// data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile },
|
||||
];
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { useConnectionList } from '../utility/metadataLoaders';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -34,8 +35,8 @@
|
||||
|
||||
showModal(InputTextModal, {
|
||||
value: name,
|
||||
label: 'New application name',
|
||||
header: 'Rename application',
|
||||
label: _t('appFolder.newApplicationName', { defaultMessage: 'New application name' }),
|
||||
header: _t('appFolder.renameApplication', { defaultMessage: 'Rename application' }),
|
||||
onConfirm: async newFolder => {
|
||||
await apiCall('apps/rename-folder', {
|
||||
folder: data.name,
|
||||
@@ -60,16 +61,16 @@
|
||||
|
||||
function createMenu() {
|
||||
return [
|
||||
{ text: 'Delete', onClick: handleDelete },
|
||||
{ text: 'Rename', onClick: handleRename },
|
||||
{ text: _t('common.delete', { defaultMessage: 'Delete' }), onClick: handleDelete },
|
||||
{ text: _t('common.rename', { defaultMessage: 'Rename' }), onClick: handleRename },
|
||||
|
||||
$currentDatabase && [
|
||||
!isOnCurrentDb($currentDatabase, $connections) && {
|
||||
text: 'Enable on current database',
|
||||
text: _t('appFolder.enableOnCurrentDatabase', { defaultMessage: 'Enable on current database' }),
|
||||
onClick: () => setOnCurrentDb(true),
|
||||
},
|
||||
isOnCurrentDb($currentDatabase, $connections) && {
|
||||
text: 'Disable on current database',
|
||||
text: _t('appFolder.disableOnCurrentDatabase', { defaultMessage: 'Disable on current database' }),
|
||||
onClick: () => setOnCurrentDb(false),
|
||||
},
|
||||
],
|
||||
@@ -90,7 +91,7 @@
|
||||
title={data.name}
|
||||
icon={'img app'}
|
||||
statusIcon={isOnCurrentDb($currentDatabase, $connections) ? 'icon check' : null}
|
||||
statusTitle={`Application ${data.name} is used for database ${$currentDatabase?.name}`}
|
||||
statusTitle={_t('appFolder.applicationUsedForDatabase', { defaultMessage: 'Application {application} is used for database {database}', values: { application: data.name, database: $currentDatabase?.name } })}
|
||||
isBold={data.name == $currentApplication}
|
||||
on:click={() => ($currentApplication = data.name)}
|
||||
menu={createMenu}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div on:drop={handleDrop}>
|
||||
<div on:drop={handleDrop} data-testid={`app-object-group-items-${_.kebabCase(group)}`}>
|
||||
{#each items as item}
|
||||
<AppObjectListItem
|
||||
isHidden={!item.isMatched}
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
import { apiCall } from '../utility/api';
|
||||
import { openImportExportTab } from '../utility/importExportTools';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let data;
|
||||
$: isZipped = data.folderName?.endsWith('.zip');
|
||||
@@ -89,8 +90,8 @@
|
||||
const handleRename = () => {
|
||||
showModal(InputTextModal, {
|
||||
value: data.fileName,
|
||||
label: 'New file name',
|
||||
header: 'Rename file',
|
||||
label: _t('archiveFile.newFileName', { defaultMessage: 'New file name' }),
|
||||
header: _t('archiveFile.renameFile', { defaultMessage: 'Rename file' }),
|
||||
onConfirm: newFile => {
|
||||
apiCall('archive/rename-file', {
|
||||
file: data.fileName,
|
||||
@@ -104,7 +105,7 @@
|
||||
|
||||
const handleDelete = () => {
|
||||
showModal(ConfirmModal, {
|
||||
message: `Really delete file ${data.fileName}?`,
|
||||
message: _t('archiveFile.deleteFileConfirm', { defaultMessage: 'Really delete file {fileName}?', values: { fileName: data.fileName } }),
|
||||
onConfirm: () => {
|
||||
apiCall('archive/delete-file', {
|
||||
file: data.fileName,
|
||||
@@ -147,10 +148,10 @@
|
||||
}
|
||||
|
||||
return [
|
||||
data.fileType == 'jsonl' && { text: 'Open', onClick: handleOpenArchive },
|
||||
data.fileType == 'jsonl' && { text: 'Open in text editor', onClick: handleOpenJsonLinesText },
|
||||
!isZipped && { text: 'Delete', onClick: handleDelete },
|
||||
!isZipped && { text: 'Rename', onClick: handleRename },
|
||||
data.fileType == 'jsonl' && { text: _t('common.open', { defaultMessage: 'Open' }), onClick: handleOpenArchive },
|
||||
data.fileType == 'jsonl' && { text: _t('common.openInTextEditor', { defaultMessage: 'Open in text editor' }), onClick: handleOpenJsonLinesText },
|
||||
!isZipped && { text: _t('common.delete', { defaultMessage: 'Delete' }), onClick: handleDelete },
|
||||
!isZipped && { text: _t('common.rename', { defaultMessage: 'Rename' }), onClick: handleRename },
|
||||
data.fileType == 'jsonl' &&
|
||||
createQuickExportMenu(
|
||||
fmt => async () => {
|
||||
@@ -185,19 +186,19 @@
|
||||
},
|
||||
}
|
||||
),
|
||||
data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile },
|
||||
data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile },
|
||||
data.fileType.endsWith('.sql') && { text: _t('common.openSql', { defaultMessage: 'Open SQL' }), onClick: handleOpenSqlFile },
|
||||
data.fileType.endsWith('.yaml') && { text: _t('common.openYaml', { defaultMessage: 'Open YAML' }), onClick: handleOpenYamlFile },
|
||||
!isZipped &&
|
||||
isProApp() &&
|
||||
data.fileType == 'jsonl' && {
|
||||
text: 'Open in profiler',
|
||||
text: _t('common.openInProfiler', { defaultMessage: 'Open in profiler' }),
|
||||
submenu: getExtensions()
|
||||
.drivers.filter(eng => eng.profilerFormatterFunction)
|
||||
.map(eng => ({
|
||||
text: eng.title,
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: 'Profiler',
|
||||
title: _t('common.profiler', { defaultMessage: 'Profiler' }),
|
||||
icon: 'img profiler',
|
||||
tabComponent: 'ProfilerTab',
|
||||
props: {
|
||||
|
||||
@@ -21,14 +21,15 @@
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import { extractShellConnection } from '../impexp/createImpExpScript';
|
||||
import { saveFileToDisk } from '../utility/exportFileTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let data;
|
||||
|
||||
const handleDelete = () => {
|
||||
showModal(ConfirmModal, {
|
||||
message: data.name.endsWith('.link')
|
||||
? `Really delete link to folder ${data.name}? Folder content remains untouched.`
|
||||
: `Really delete folder ${data.name}?`,
|
||||
? _t('archiveFolder.deleteLinkConfirm', { defaultMessage: 'Really delete link to folder {folderName}? Folder content remains untouched.', values: { folderName: data.name } })
|
||||
: _t('archiveFolder.deleteFolderConfirm', { defaultMessage: 'Really delete folder {folderName}?', values: { folderName: data.name } }),
|
||||
onConfirm: () => {
|
||||
apiCall('archive/delete-folder', { folder: data.name });
|
||||
},
|
||||
@@ -42,8 +43,8 @@
|
||||
|
||||
showModal(InputTextModal, {
|
||||
value: name,
|
||||
label: 'New folder name',
|
||||
header: 'Rename folder',
|
||||
label: _t('archiveFolder.newFolderName', { defaultMessage: 'New folder name' }),
|
||||
header: _t('archiveFolder.renameFolder', { defaultMessage: 'Rename folder' }),
|
||||
onConfirm: async newFolder => {
|
||||
await apiCall('archive/rename-folder', {
|
||||
folder: data.name,
|
||||
@@ -95,7 +96,7 @@ await dbgateApi.deployDb(${JSON.stringify(
|
||||
const handleCompareWithCurrentDb = () => {
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Compare',
|
||||
title: _t('common.compare', { defaultMessage: 'Compare' }),
|
||||
icon: 'img compare',
|
||||
tabComponent: 'CompareModelTab',
|
||||
props: {
|
||||
@@ -153,7 +154,7 @@ await dbgateApi.deployDb(${JSON.stringify(
|
||||
});
|
||||
},
|
||||
{
|
||||
formatLabel: 'ZIP files',
|
||||
formatLabel: _t('common.zipFiles', { defaultMessage: 'ZIP files' }),
|
||||
formatExtension: 'zip',
|
||||
defaultFileName: data.name?.endsWith('.zip') ? data.name : data.name + '.zip',
|
||||
}
|
||||
@@ -162,28 +163,28 @@ await dbgateApi.deployDb(${JSON.stringify(
|
||||
|
||||
function createMenu() {
|
||||
return [
|
||||
data.name != 'default' && { text: 'Delete', onClick: handleDelete },
|
||||
data.name != 'default' && { text: 'Rename', onClick: handleRename },
|
||||
isProApp() && { text: 'Data deployer', onClick: handleOpenDataDeployTab },
|
||||
data.name != 'default' && { text: _t('common.delete', { defaultMessage: 'Delete' }), onClick: handleDelete },
|
||||
data.name != 'default' && { text: _t('common.rename', { defaultMessage: 'Rename' }), onClick: handleRename },
|
||||
isProApp() && { text: _t('common.dataDeployer', { defaultMessage: 'Data deployer' }), onClick: handleOpenDataDeployTab },
|
||||
$currentDatabase && [
|
||||
{ text: 'Generate deploy DB SQL', onClick: handleGenerateDeploySql },
|
||||
hasPermission(`run-shell-script`) && { text: 'Shell: Deploy DB', onClick: handleGenerateDeployScript },
|
||||
{ text: _t('archiveFolder.generateDeployDbSql', { defaultMessage: 'Generate deploy DB SQL' }), onClick: handleGenerateDeploySql },
|
||||
hasPermission(`run-shell-script`) && { text: _t('archiveFolder.shellDeployDb', { defaultMessage: 'Shell: Deploy DB' }), onClick: handleGenerateDeployScript },
|
||||
],
|
||||
data.name != 'default' &&
|
||||
isProApp() &&
|
||||
data.name.endsWith('.zip') && { text: 'Unpack ZIP', onClick: () => handleZipUnzip('archive/unzip') },
|
||||
data.name.endsWith('.zip') && { text: _t('archiveFolder.unpackZip', { defaultMessage: 'Unpack ZIP' }), onClick: () => handleZipUnzip('archive/unzip') },
|
||||
data.name != 'default' &&
|
||||
isProApp() &&
|
||||
!data.name.endsWith('.zip') && { text: 'Pack (create ZIP)', onClick: () => handleZipUnzip('archive/zip') },
|
||||
!data.name.endsWith('.zip') && { text: _t('archiveFolder.packZip', { defaultMessage: 'Pack (create ZIP)' }), onClick: () => handleZipUnzip('archive/zip') },
|
||||
|
||||
isProApp() && { text: 'Download ZIP', onClick: handleDownloadZip },
|
||||
isProApp() && { text: _t('archiveFolder.downloadZip', { defaultMessage: 'Download ZIP' }), onClick: handleDownloadZip },
|
||||
|
||||
data.name != 'default' &&
|
||||
hasPermission('dbops/model/compare') &&
|
||||
isProApp() &&
|
||||
_.get($currentDatabase, 'connection._id') && {
|
||||
onClick: handleCompareWithCurrentDb,
|
||||
text: `Compare with ${_.get($currentDatabase, 'name')}`,
|
||||
text: _t('archiveFolder.compareWithCurrentDb', { defaultMessage: 'Compare with {name}', values: { name: _.get($currentDatabase, 'name') } }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -407,8 +407,8 @@ await dbgateApi.executeQuery(${JSON.stringify(
|
||||
|
||||
const handleCreateNewApp = () => {
|
||||
showModal(InputTextModal, {
|
||||
header: 'New application',
|
||||
label: 'Application name',
|
||||
header: _t('database.newApplication', { defaultMessage: 'New application' }),
|
||||
label: _t('database.applicationName', { defaultMessage: 'Application name' }),
|
||||
value: _.startCase(name),
|
||||
onConfirm: async appName => {
|
||||
const newAppId = await apiCall('apps/create-app-from-db', {
|
||||
|
||||
@@ -199,12 +199,12 @@
|
||||
divider: true,
|
||||
},
|
||||
hasPermission('dbops/export') && {
|
||||
label: 'Export',
|
||||
label: _t('common.export', { defaultMessage: 'Export' }),
|
||||
functionName: 'tableReader',
|
||||
isExport: true,
|
||||
},
|
||||
hasPermission('dbops/import') && {
|
||||
label: 'Import',
|
||||
label: _t('common.import', { defaultMessage: 'Import' }),
|
||||
isImport: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
@@ -260,7 +260,7 @@
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'Export',
|
||||
label: _t('common.export', { defaultMessage: 'Export' }),
|
||||
isExport: true,
|
||||
functionName: 'tableReader',
|
||||
},
|
||||
@@ -310,7 +310,7 @@
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
label: 'Export',
|
||||
label: _t('common.export', { defaultMessage: 'Export' }),
|
||||
isExport: true,
|
||||
functionName: 'tableReader',
|
||||
},
|
||||
@@ -402,7 +402,7 @@
|
||||
icon: 'img perspective',
|
||||
},
|
||||
hasPermission('dbops/export') && {
|
||||
label: 'Export',
|
||||
label: _t('common.export', { defaultMessage: 'Export' }),
|
||||
isExport: true,
|
||||
functionName: 'tableReader',
|
||||
},
|
||||
@@ -754,9 +754,8 @@
|
||||
const coreMenus = createMenusCore(objectTypeField, driver, data);
|
||||
|
||||
const filteredSumenus = coreMenus.map(item => {
|
||||
if (!item) return item;
|
||||
if (!item.submenu) {
|
||||
if (!item) return item;
|
||||
|
||||
return { ...item, label: _tval(item.label) };
|
||||
}
|
||||
return {
|
||||
@@ -773,7 +772,7 @@
|
||||
};
|
||||
});
|
||||
|
||||
const filteredNoEmptySubmenus = filteredSumenus.filter(x => !x.submenu || x.submenu.length > 0);
|
||||
const filteredNoEmptySubmenus = _.compact(filteredSumenus).filter(x => !x.submenu || x.submenu.length > 0);
|
||||
|
||||
return filteredNoEmptySubmenus;
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@
|
||||
import { saveFileToDisk } from '../utility/exportFileTools';
|
||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||
import { showSnackbarError } from '../utility/snackbar';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -214,27 +215,26 @@
|
||||
|
||||
function createMenu() {
|
||||
return [
|
||||
handler?.tabComponent && { text: 'Open', onClick: openTab },
|
||||
handler?.tabComponent && { text: _t('common.open', { defaultMessage: 'Open' }), onClick: openTab },
|
||||
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: 'Rename', onClick: handleRename },
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: 'Create copy', onClick: handleCopy },
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: 'Delete', onClick: handleDelete },
|
||||
|
||||
data.teamFileId && data.allowWrite && { text: 'Rename', onClick: handleRename },
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: _t('common.rename', { defaultMessage: 'Rename' }), onClick: handleRename },
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: _t('common.createCopy', { defaultMessage: 'Create copy' }), onClick: handleCopy },
|
||||
!data.teamFileId && hasPermission(`files/${data.folder}/write`) && { text: _t('common.delete', { defaultMessage: 'Delete' }), onClick: handleDelete },
|
||||
data.teamFileId && data.allowWrite && { text: _t('common.rename', { defaultMessage: 'Rename' }), onClick: handleRename },
|
||||
data.teamFileId &&
|
||||
data.allowRead &&
|
||||
hasPermission('all-team-files/create') && { text: 'Create copy', onClick: handleCopy },
|
||||
data.teamFileId && data.allowWrite && { text: 'Delete', onClick: handleDelete },
|
||||
hasPermission('all-team-files/create') && { text: _t('common.createCopy', { defaultMessage: 'Create copy' }), onClick: handleCopy },
|
||||
data.teamFileId && data.allowWrite && { text: _t('common.delete', { defaultMessage: 'Delete' }), onClick: handleDelete },
|
||||
|
||||
folder == 'markdown' && { text: 'Show page', onClick: showMarkdownPage },
|
||||
!data.teamFileId && { text: 'Download', onClick: handleDownload },
|
||||
data.teamFileId && data.allowRead && { text: 'Download', onClick: handleDownload },
|
||||
folder == 'markdown' && { text: _t('common.showPage', { defaultMessage: 'Show page' }), onClick: showMarkdownPage },
|
||||
!data.teamFileId && { text: _t('common.download', { defaultMessage: 'Download' }), onClick: handleDownload },
|
||||
data.teamFileId && data.allowRead && { text: _t('common.download', { defaultMessage: 'Download' }), onClick: handleDownload },
|
||||
];
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
showModal(ConfirmModal, {
|
||||
message: `Really delete file ${data.file}?`,
|
||||
message: _t('common.reallyDeleteFile', { defaultMessage: 'Really delete file {file}?', values: { file: data.file } }),
|
||||
onConfirm: () => {
|
||||
if (data.teamFileId) {
|
||||
apiCall('team-files/delete', { teamFileId: data.teamFileId });
|
||||
@@ -253,8 +253,8 @@
|
||||
const handleRename = () => {
|
||||
showModal(InputTextModal, {
|
||||
value: data.file,
|
||||
label: 'New file name',
|
||||
header: 'Rename file',
|
||||
label: _t('common.newFileName', { defaultMessage: 'New file name' }),
|
||||
header: _t('common.renameFile', { defaultMessage: 'Rename file' }),
|
||||
onConfirm: newFile => {
|
||||
if (data.teamFileId) {
|
||||
apiCall('team-files/update', { teamFileId: data.teamFileId, name: newFile });
|
||||
@@ -274,8 +274,8 @@
|
||||
const handleCopy = () => {
|
||||
showModal(InputTextModal, {
|
||||
value: data.file,
|
||||
label: 'New file name',
|
||||
header: 'Copy file',
|
||||
label: _t('savedFile.newFileName', { defaultMessage: 'New file name' }),
|
||||
header: _t('savedFile.copyFile', { defaultMessage: 'Copy file' }),
|
||||
onConfirm: newFile => {
|
||||
if (data.teamFileId) {
|
||||
apiCall('team-files/copy', { teamFileId: data.teamFileId, newName: newFile });
|
||||
@@ -323,12 +323,12 @@
|
||||
if (data.teamFileId) {
|
||||
if (data?.metadata?.autoExecute) {
|
||||
if (!data.allowUse) {
|
||||
showSnackbarError('You do not have permission to use this team file');
|
||||
showSnackbarError(_t('savedFile.noPermissionUseTeamFile', { defaultMessage: 'You do not have permission to use this team file' }));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!data.allowRead) {
|
||||
showSnackbarError('You do not have permission to read this team file');
|
||||
showSnackbarError(_t('savedFile.noPermissionReadTeamFile', { defaultMessage: 'You do not have permission to read this team file' }));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
import { getLocalStorage } from '../utility/storageCache';
|
||||
import registerCommand from './registerCommand';
|
||||
import { formatKeyText, switchCurrentDatabase } from '../utility/common';
|
||||
import { _tval, __t } from '../translations';
|
||||
import { _tval, __t, _t } from '../translations';
|
||||
|
||||
let domInput;
|
||||
let filter = '';
|
||||
@@ -181,7 +181,7 @@
|
||||
domInput.focus();
|
||||
}}
|
||||
>
|
||||
<FontIcon icon="icon menu" /> Commands
|
||||
<FontIcon icon="icon menu" /> {_t('commandPalette.commands', { defaultMessage: 'Commands' })}
|
||||
</div>
|
||||
<div
|
||||
class="page"
|
||||
@@ -191,7 +191,7 @@
|
||||
domInput.focus();
|
||||
}}
|
||||
>
|
||||
<FontIcon icon="icon database" /> Database
|
||||
<FontIcon icon="icon database" /> {_t('common.database', { defaultMessage: 'Database' })}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mainInner">
|
||||
@@ -201,8 +201,8 @@
|
||||
bind:this={domInput}
|
||||
bind:value={filter}
|
||||
on:keydown={handleKeyDown}
|
||||
placeholder={parentCommand?.text ||
|
||||
($visibleCommandPalette == 'database' ? 'Search in database' : 'Search in commands')}
|
||||
placeholder={_tval(parentCommand?.text) ||
|
||||
($visibleCommandPalette == 'database' ? _t('commandPalette.searchInDatabase', { defaultMessage: 'Search in database' }) : _t('commandPalette.searchInCommands', { defaultMessage: 'Search in commands' }))}
|
||||
/>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import _ from 'lodash';
|
||||
import { currentDatabase, getCurrentDatabase } from '../stores';
|
||||
import { currentDatabase, getCurrentDatabase, getExtensions } from '../stores';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import registerCommand from './registerCommand';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { getDatabasStatusMenu, switchCurrentDatabase } from '../utility/common';
|
||||
import { __t } from '../translations';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
|
||||
registerCommand({
|
||||
id: 'database.changeState',
|
||||
@@ -18,7 +19,8 @@ registerCommand({
|
||||
conid: connection._id,
|
||||
database: name,
|
||||
};
|
||||
const driver = findEngineDriver(connection, getExtensions());
|
||||
|
||||
return getDatabasStatusMenu(dbid);
|
||||
return getDatabasStatusMenu(dbid, driver);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
import registerCommand from './registerCommand';
|
||||
import { get } from 'svelte/store';
|
||||
import AboutModal from '../modals/AboutModal.svelte';
|
||||
import SettingsModal from '../settings/SettingsModal.svelte';
|
||||
import SqlGeneratorModal from '../modals/SqlGeneratorModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import newQuery, { newDiagram, newPerspective, newQueryDesign } from '../query/newQuery';
|
||||
@@ -41,8 +40,6 @@ import { getSettings } from '../utility/metadataLoaders';
|
||||
import { isMac, switchCurrentDatabase } from '../utility/common';
|
||||
import { doLogout } from '../clientAuth';
|
||||
import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte';
|
||||
import UploadErrorModal from '../modals/UploadErrorModal.svelte';
|
||||
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||
import NewCollectionModal from '../modals/NewCollectionModal.svelte';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import localforage from 'localforage';
|
||||
@@ -74,7 +71,15 @@ registerCommand({
|
||||
category: __t('command.theme', { defaultMessage: 'Theme' }),
|
||||
name: __t('command.theme.change', { defaultMessage: 'Change' }),
|
||||
toolbarName: __t('command.theme.changeToolbar', { defaultMessage: 'Change theme' }),
|
||||
onClick: () => showModal(SettingsModal, { selectedTab: 'theme' }),
|
||||
onClick: () =>
|
||||
openNewTab({
|
||||
title: 'Settings',
|
||||
icon: 'icon settings',
|
||||
tabComponent: 'SettingsTab',
|
||||
props: {
|
||||
selectedItem: 'theme',
|
||||
},
|
||||
}),
|
||||
// getSubCommands: () => get(extensions).themes.map(themeCommand),
|
||||
});
|
||||
|
||||
@@ -1224,8 +1229,7 @@ registerCommand({
|
||||
},
|
||||
});
|
||||
|
||||
if ( hasPermission('application-log'))
|
||||
{
|
||||
if (hasPermission('application-log')) {
|
||||
registerCommand({
|
||||
id: 'app.showLogs',
|
||||
category: __t('command.application', { defaultMessage: 'Application' }),
|
||||
@@ -1240,8 +1244,7 @@ if ( hasPermission('application-log'))
|
||||
});
|
||||
}
|
||||
|
||||
if (hasPermission('widgets/plugins'))
|
||||
{
|
||||
if (hasPermission('widgets/plugins')) {
|
||||
registerCommand({
|
||||
id: 'app.managePlugins',
|
||||
category: __t('command.application', { defaultMessage: 'Application' }),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import { getBoolSettingsValue } from '../settings/settingsTools';
|
||||
import { getStringSettingsValue } from '../settings/settingsTools';
|
||||
import { stringifyCellValue } from 'dbgate-tools';
|
||||
|
||||
export let rowData;
|
||||
@@ -13,7 +13,7 @@
|
||||
value,
|
||||
'gridCellIntent',
|
||||
editorTypes,
|
||||
{ useThousandsSeparator: getBoolSettingsValue('dataGrid.thousandsSeparator', false) },
|
||||
{ thousandsSeparator: getStringSettingsValue('dataGrid.thousandsSeparatorChar', 'none') },
|
||||
jsonParsedValue
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import _, { isPlainObject } from 'lodash';
|
||||
import ShowFormButton from '../formview/ShowFormButton.svelte';
|
||||
import { detectTypeIcon, getConvertValueMenu, isJsonLikeLongString, safeJsonParse } from 'dbgate-tools';
|
||||
import { detectTypeIcon, getConvertValueMenu, isJsonLikeLongString, safeJsonParse, isTypeNumber } from 'dbgate-tools';
|
||||
import { openJsonDocument } from '../tabs/JsonTab.svelte';
|
||||
import CellValue from './CellValue.svelte';
|
||||
import { openJsonLinesData } from '../utility/openJsonLinesData';
|
||||
@@ -57,7 +57,7 @@
|
||||
$: style = computeStyle(maxWidth, col);
|
||||
|
||||
$: isJson =
|
||||
_.isPlainObject(value) && !(value?.type == 'Buffer' && _.isArray(value.data)) && !value.$oid && !value.$bigint;
|
||||
_.isPlainObject(value) && !(value?.type == 'Buffer' && _.isArray(value.data)) && !value.$oid && !value.$bigint && !value.$decimal;
|
||||
|
||||
// don't parse JSON for explicit data types
|
||||
$: jsonParsedValue = !editorTypes?.explicitDataType && isJsonLikeLongString(value) ? safeJsonParse(value) : null;
|
||||
@@ -80,7 +80,7 @@
|
||||
class:isFocusedColumn
|
||||
class:hasOverlayValue
|
||||
class:isMissingOverlayField
|
||||
class:alignRight={_.isNumber(value) && !showHint}
|
||||
class:alignRight={ (_.isNumber(value) || isTypeNumber(col.dataType)) && !showHint}
|
||||
{style}
|
||||
>
|
||||
{#if hasOverlayValue}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script context="module" lang="ts">
|
||||
import { __t } from '../translations'
|
||||
import { __t, _t } from '../translations'
|
||||
const getCurrentEditor = () => getActiveComponent('SqlDataGridCore');
|
||||
|
||||
registerCommand({
|
||||
@@ -127,7 +127,7 @@
|
||||
export function openQuery(sql?) {
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Query #',
|
||||
title: _t('common.queryNumber', { defaultMessage: 'Query #' }),
|
||||
icon: 'img sql-file',
|
||||
tabComponent: 'QueryTab',
|
||||
focused: true,
|
||||
|
||||
@@ -73,6 +73,7 @@ export function countColumnSizes(grider: Grider, columns, containerWidth, displa
|
||||
if (_.isArray(value)) text = `[${value.length} items]`;
|
||||
else if (value?.$oid) text = `ObjectId("${value.$oid}")`;
|
||||
else if (value?.$bigint) text = value.$bigint;
|
||||
else if (value?.$decimal) text = value.$decimal;
|
||||
else if (isJsonLikeLongString(value) && safeJsonParse(value)) text = '(JSON)';
|
||||
const width = context.measureText(typeof text == 'string' ? text.slice(0, MAX_GRID_TEXT_LENGTH) : text).width + 8;
|
||||
// console.log('colName', colName, text, width);
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import dragScroll from '../utility/dragScroll';
|
||||
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||
import { __t } from '../translations';
|
||||
import { __t, _t } from '../translations';
|
||||
|
||||
export let value;
|
||||
export let onChange;
|
||||
@@ -849,45 +849,45 @@
|
||||
settings?.customizeStyle && [
|
||||
{ divider: true },
|
||||
isProApp() && {
|
||||
text: 'Column properties',
|
||||
text: _t('designer.columnProperties', { defaultMessage: 'Column properties' }),
|
||||
submenu: [
|
||||
{
|
||||
text: `Nullability: ${value?.style?.showNullability ? 'YES' : 'NO'}`,
|
||||
text: _t('designer.nullabilityYesNo', { defaultMessage: 'Nullability: {show}', values: { show: value?.style?.showNullability ? 'YES' : 'NO' } }),
|
||||
onClick: changeStyleFunc('showNullability', !value?.style?.showNullability),
|
||||
},
|
||||
{
|
||||
text: `Data type: ${value?.style?.showDataType ? 'YES' : 'NO'}`,
|
||||
text: _t('designer.dataTypeYesNo', { defaultMessage: 'Data type: {show}', values: { show: value?.style?.showDataType ? 'YES' : 'NO' } }),
|
||||
onClick: changeStyleFunc('showDataType', !value?.style?.showDataType),
|
||||
},
|
||||
],
|
||||
},
|
||||
isProApp() && {
|
||||
text: `Columns - ${_.startCase(value?.style?.filterColumns || 'all')}`,
|
||||
text: _t('designer.columns', { defaultMessage: 'Columns - { filterColumns }', values: { filterColumns: _.startCase(value?.style?.filterColumns || 'all') } }),
|
||||
submenu: [
|
||||
{
|
||||
text: 'All',
|
||||
text: _t('designer.all', { defaultMessage: 'All' }),
|
||||
onClick: changeStyleFunc('filterColumns', ''),
|
||||
},
|
||||
{
|
||||
text: 'Primary Key',
|
||||
text: _t('designer.primaryKey', { defaultMessage: 'Primary Key' }),
|
||||
onClick: changeStyleFunc('filterColumns', 'primaryKey'),
|
||||
},
|
||||
{
|
||||
text: 'All Keys',
|
||||
text: _t('designer.allKeys', { defaultMessage: 'All Keys' }),
|
||||
onClick: changeStyleFunc('filterColumns', 'allKeys'),
|
||||
},
|
||||
{
|
||||
text: 'Not Null',
|
||||
text: _t('designer.notNull', { defaultMessage: 'Not Null' }),
|
||||
onClick: changeStyleFunc('filterColumns', 'notNull'),
|
||||
},
|
||||
{
|
||||
text: 'Keys And Not Null',
|
||||
text: _t('designer.keysAndNotNull', { defaultMessage: 'Keys And Not Null' }),
|
||||
onClick: changeStyleFunc('filterColumns', 'keysAndNotNull'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: `Zoom - ${(value?.style?.zoomKoef || 1) * 100}%`,
|
||||
text: _t('designer.zoom', { defaultMessage: 'Zoom - {zoom}%', values: { zoom: ((value?.style?.zoomKoef || 1) * 100) } }),
|
||||
submenu: DIAGRAM_ZOOMS.map(koef => ({
|
||||
text: `${koef * 100} %`,
|
||||
onClick: changeStyleFunc('zoomKoef', koef.toString()),
|
||||
@@ -1016,11 +1016,11 @@
|
||||
use:dragScroll={handleDragScroll}
|
||||
>
|
||||
{#if !(tables?.length > 0)}
|
||||
<div class="empty">Drag & drop tables or views from left panel here</div>
|
||||
<div class="empty">{_t('designer.dragDropTables', { defaultMessage: 'Drag & drop tables or views from left panel here' })}</div>
|
||||
|
||||
{#if allowAddTablesButton}
|
||||
<div class="addAllTables">
|
||||
<FormStyledButton value="Add all tables" on:click={handleAddAllTables} />
|
||||
<FormStyledButton value={_t('designer.addAllTables', { defaultMessage: 'Add all tables' })} on:click={handleAddAllTables} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -1119,7 +1119,7 @@
|
||||
<div class="panel">
|
||||
<DragColumnMemory {settings} {sourceDragColumn$} {targetDragColumn$} />
|
||||
<div class="searchbox">
|
||||
<SearchInput bind:value={columnFilter} placeholder="Filter columns" />
|
||||
<SearchInput bind:value={columnFilter} placeholder={_t('designer.filterColumns', { defaultMessage: 'Filter columns' })} />
|
||||
<CloseSearchButton bind:filter={columnFilter} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</script>
|
||||
|
||||
<div class="main" class:maxHeight100 class:flex1>
|
||||
<HorizontalSplitter initialValue="20%">
|
||||
<HorizontalSplitter initialValue="170px">
|
||||
<svelte:fragment slot="1">
|
||||
<div class="menu">
|
||||
{#each _.compact(items) as item, index}
|
||||
@@ -118,6 +118,10 @@
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-item:first-child {
|
||||
border-top: 1px solid var(--theme-border);
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background-color: var(--theme-bg-hover);
|
||||
}
|
||||
@@ -133,6 +137,7 @@
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
background-color: var(--theme-bg-0);
|
||||
}
|
||||
|
||||
.scrollableContentContainer {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import DropDownButton from '../buttons/DropDownButton.svelte';
|
||||
import { _tval } from '../translations';
|
||||
|
||||
interface TabDef {
|
||||
label: string;
|
||||
@@ -46,7 +47,7 @@
|
||||
data-testid={tab.testid}
|
||||
>
|
||||
<span class="ml-2 noselect">
|
||||
{tab.label}
|
||||
{_tval(tab.label)}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { getFormContext } from './FormProviderCore.svelte';
|
||||
|
||||
import FormSelectField from './FormSelectField.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let additionalFolders = [];
|
||||
export let name;
|
||||
@@ -35,7 +36,7 @@
|
||||
label: folder,
|
||||
})),
|
||||
allowCreateNew && {
|
||||
label: '(Create new)',
|
||||
label: _t('archiveFolder.createNew', { defaultMessage: '(Create new)' }),
|
||||
value: '@create',
|
||||
},
|
||||
];
|
||||
@@ -48,8 +49,8 @@
|
||||
function handleChange(e) {
|
||||
if (e.detail == '@create') {
|
||||
showModal(InputTextModal, {
|
||||
header: 'Archive',
|
||||
label: 'Name of new archive folder',
|
||||
header: _t('archiveFolder.archive', { defaultMessage: 'Archive' }),
|
||||
label: _t('archiveFolder.nameOfNewArchiveFolder', { defaultMessage: 'Name of new archive folder' }),
|
||||
onConfirm: createOption,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script lang="ts">
|
||||
import FontIcon from "../icons/FontIcon.svelte";
|
||||
|
||||
export let type;
|
||||
export let label;
|
||||
export let noMargin = false;
|
||||
export let disabled = false;
|
||||
export let labelProps: any = {};
|
||||
export let labelIcon = null;
|
||||
</script>
|
||||
|
||||
<div class="largeFormMarker" class:noMargin>
|
||||
@@ -12,6 +15,9 @@
|
||||
<span {...labelProps} on:click={labelProps.onClick} class:disabled class='checkLabel'>{label}</span>
|
||||
{:else}
|
||||
<div class="label" {...labelProps} on:click={labelProps.onClick}>
|
||||
{#if labelIcon}
|
||||
<FontIcon icon={labelIcon} padRight />
|
||||
{/if}
|
||||
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span>
|
||||
</div>
|
||||
<slot />
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let value = '';
|
||||
export let conid;
|
||||
@@ -33,8 +34,8 @@
|
||||
|
||||
async function handleAddNewApplication() {
|
||||
showModal(InputTextModal, {
|
||||
header: 'New application',
|
||||
label: 'Application name',
|
||||
header: _t('database.newApplication', { defaultMessage: 'New application' }),
|
||||
label: _t('database.applicationName', { defaultMessage: 'Application name' }),
|
||||
value: _.startCase(database),
|
||||
onConfirm: async appName => {
|
||||
const newAppId = await apiCall('apps/create-app-from-db', {
|
||||
|
||||
@@ -211,8 +211,11 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-2">
|
||||
<div class="title"><FontIcon icon="icon tables" /> {_t('importExport.mapSourceTablesFiles', { defaultMessage: "Map source tables/files" })}</div>
|
||||
<div class="m-2" data-testid="ImportExportConfigurator_tableMappingSection">
|
||||
<div class="title">
|
||||
<FontIcon icon="icon tables" />
|
||||
{_t('importExport.mapSourceTablesFiles', { defaultMessage: 'Map source tables/files' })}
|
||||
</div>
|
||||
|
||||
{#key targetEditKey}
|
||||
{#key progressHolder}
|
||||
@@ -221,34 +224,34 @@
|
||||
columns={[
|
||||
{
|
||||
fieldName: 'source',
|
||||
header: _t('importExport.source', { defaultMessage: "Source" }),
|
||||
header: _t('importExport.source', { defaultMessage: 'Source' }),
|
||||
component: SourceName,
|
||||
getProps: row => ({ name: row }),
|
||||
},
|
||||
{
|
||||
fieldName: 'action',
|
||||
header: _t('importExport.action', { defaultMessage: "Action" }),
|
||||
header: _t('importExport.action', { defaultMessage: 'Action' }),
|
||||
component: SourceAction,
|
||||
getProps: row => ({ name: row, targetDbinfo }),
|
||||
},
|
||||
{
|
||||
fieldName: 'target',
|
||||
header: _t('importExport.target', { defaultMessage: "Target" }),
|
||||
header: _t('importExport.target', { defaultMessage: 'Target' }),
|
||||
slot: 1,
|
||||
},
|
||||
supportsPreview && {
|
||||
fieldName: 'preview',
|
||||
header: _t('importExport.preview', { defaultMessage: "Preview" }),
|
||||
header: _t('importExport.preview', { defaultMessage: 'Preview' }),
|
||||
slot: 0,
|
||||
},
|
||||
!!progressHolder && {
|
||||
fieldName: 'status',
|
||||
header: _t('importExport.status', { defaultMessage: "Status" }),
|
||||
header: _t('importExport.status', { defaultMessage: 'Status' }),
|
||||
slot: 3,
|
||||
},
|
||||
{
|
||||
fieldName: 'columns',
|
||||
header: _t('importExport.columns', { defaultMessage: "Columns" }),
|
||||
header: _t('importExport.columns', { defaultMessage: 'Columns' }),
|
||||
slot: 2,
|
||||
},
|
||||
]}
|
||||
@@ -307,21 +310,21 @@
|
||||
},
|
||||
});
|
||||
}}
|
||||
>{columnCount > 0 ? `(${columnCount} columns)` : '(copy from source)'}
|
||||
>{columnCount > 0 ? _t('importExport.columnsCount', { defaultMessage: '({columnCount} columns)', values: { columnCount } }) : _t('importExport.copyFromSource', { defaultMessage: '(copy from source)' })}
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="3" let:row>
|
||||
{#if progressHolder[row]?.status == 'running' && isRunning}
|
||||
<FontIcon icon="icon loading" />
|
||||
{#if progressHolder[row]?.writtenRowCount}
|
||||
{progressHolder[row]?.writtenRowCount} rows writtem
|
||||
{progressHolder[row]?.writtenRowCount} {_t('importExport.rowsWritten', { defaultMessage: 'rows written' })}
|
||||
{:else if progressHolder[row]?.readRowCount}
|
||||
{progressHolder[row]?.readRowCount} rows read
|
||||
{progressHolder[row]?.readRowCount} {_t('importExport.rowsRead', { defaultMessage: 'rows read' })}
|
||||
{:else}
|
||||
Running
|
||||
{_t('importExport.running', { defaultMessage: 'Running' })}
|
||||
{/if}
|
||||
{:else if progressHolder[row]?.status == 'error'}
|
||||
<FontIcon icon="img error" /> Error
|
||||
<FontIcon icon="img error" /> {_t('common.error', { defaultMessage: 'Error' })}
|
||||
{#if progressHolder[row]?.errorMessage}
|
||||
<FontIcon
|
||||
icon="img info"
|
||||
@@ -334,20 +337,20 @@
|
||||
{:else if progressHolder[row]?.status == 'done'}
|
||||
<FontIcon icon="img ok" />
|
||||
{#if progressHolder[row]?.writtenRowCount}
|
||||
{progressHolder[row]?.writtenRowCount} rows written
|
||||
{progressHolder[row]?.writtenRowCount} {_t('importExport.rowsWritten', { defaultMessage: 'rows written' })}
|
||||
{:else if progressHolder[row]?.readRowCount}
|
||||
{progressHolder[row]?.readRowCount} rows written
|
||||
{progressHolder[row]?.readRowCount} {_t('importExport.rowsWritten', { defaultMessage: 'rows written' })}
|
||||
{:else}
|
||||
Done
|
||||
{_t('common.done', { defaultMessage: 'Done' })}
|
||||
{/if}
|
||||
{:else}
|
||||
<FontIcon icon="icon wait" />
|
||||
{#if progressHolder[row]?.writtenRowCount}
|
||||
{progressHolder[row]?.writtenRowCount} rows writtem
|
||||
{progressHolder[row]?.writtenRowCount} {_t('importExport.rowsWritten', { defaultMessage: 'rows written' })}
|
||||
{:else if progressHolder[row]?.readRowCount}
|
||||
{progressHolder[row]?.readRowCount} rows read
|
||||
{progressHolder[row]?.readRowCount} {_t('importExport.rowsRead', { defaultMessage: 'rows read' })}
|
||||
{:else}
|
||||
Queued
|
||||
{_t('importExport.queued', { defaultMessage: 'Queued' })}
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -114,8 +114,8 @@
|
||||
value={_t('importExport.newArchive', { defaultMessage: "New archive" })}
|
||||
on:click={() => {
|
||||
showModal(InputTextModal, {
|
||||
header: 'Archive',
|
||||
label: 'Name of new archive folder',
|
||||
header: _t('importExport.archive', { defaultMessage: 'Archive' }),
|
||||
label: _t('importExport.nameOfNewArchiveFolder', { defaultMessage: 'Name of new archive folder' }),
|
||||
value: `import-${moment().format('YYYY-MM-DD-hh-mm-ss')}`,
|
||||
onConfirm: value => {
|
||||
values.update(x => ({
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import Link from '../elements/Link.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
const config = useConfig();
|
||||
$: version = $config?.version;
|
||||
@@ -15,18 +16,18 @@
|
||||
</script>
|
||||
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">About DbGate</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('aboutModal.aboutDbGate', { defaultMessage: 'About DbGate' })}</svelte:fragment>
|
||||
<div class="flex">
|
||||
<img src="logo192.png" />
|
||||
<div class="ml-4">
|
||||
<div>
|
||||
Version: <span>{version}</span>
|
||||
{_t('aboutModal.version', { defaultMessage: 'Version' })}: <span>{version}</span>
|
||||
</div>
|
||||
<div>
|
||||
Build date: <span>{moment(buildTime).format('YYYY-MM-DD')}</span>
|
||||
{_t('aboutModal.buildDate', { defaultMessage: 'Build date' })}: <span>{moment(buildTime).format('YYYY-MM-DD')}</span>
|
||||
</div>
|
||||
<div>
|
||||
License type: <span
|
||||
{_t('aboutModal.licenseType', { defaultMessage: 'License type' })}: <span
|
||||
>{$config?.checkedLicense && $config?.checkedLicense?.type != 'community'
|
||||
? ($config?.checkedLicense?.licenseTypeObj?.name ?? 'Unknown')
|
||||
: 'Community'}</span
|
||||
@@ -34,16 +35,16 @@
|
||||
</div>
|
||||
{#if $config?.checkedLicense?.users}
|
||||
<div>
|
||||
User count: <span>{$config?.checkedLicense?.users}</span>
|
||||
{_t('aboutModal.userCount', { defaultMessage: 'User count' })}: <span>{$config?.checkedLicense?.users}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="mt-2">
|
||||
<FontIcon icon="mdi mdi-web color-icon-blue" /> Web: <Link href="https://www.dbgate.io">dbgate.io</Link>
|
||||
<FontIcon icon="mdi mdi-web color-icon-blue" /> {_t('aboutModal.web', { defaultMessage: 'Web' })}: <Link href="https://www.dbgate.io">dbgate.io</Link>
|
||||
</div>
|
||||
{#if isProApp()}
|
||||
<div>
|
||||
<FontIcon icon="mdi mdi-email color-icon-red" /> Support: <Link href="mailto:support@dbgate.io"
|
||||
<FontIcon icon="mdi mdi-email color-icon-red" /> {_t('aboutModal.support', { defaultMessage: 'Support' })}: <Link href="mailto:support@dbgate.io"
|
||||
>support@dbgate.io</Link
|
||||
>
|
||||
</div>
|
||||
@@ -55,10 +56,10 @@
|
||||
</div> -->
|
||||
|
||||
<div class="mt-2">
|
||||
Source codes: <Link href="https://github.com/dbgate/dbgate/">GitHub</Link>
|
||||
{_t('aboutModal.sourceCodes', { defaultMessage: 'Source codes' })}: <Link href="https://github.com/dbgate/dbgate/">GitHub</Link>
|
||||
</div>
|
||||
<div>
|
||||
Docker container: <Link
|
||||
{_t('aboutModal.dockerContainer', { defaultMessage: 'Docker container' })}: <Link
|
||||
href={isProApp()
|
||||
? 'https://hub.docker.com/r/dbgate/dbgate-premium'
|
||||
: 'https://hub.docker.com/r/dbgate/dbgate'}>Docker Hub</Link
|
||||
@@ -69,13 +70,13 @@
|
||||
</div> -->
|
||||
|
||||
<div class="mt-2">
|
||||
Produced by: <span>Sprinx System a.s.</span>
|
||||
{_t('aboutModal.producedBy', { defaultMessage: 'Produced by' })}: <span>Sprinx System a.s.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormStyledButton value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import TextField from '../forms/TextField.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@@ -26,10 +27,10 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Add key</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('addDbKeyModal.addKey', { defaultMessage: 'Add key' })}</svelte:fragment>
|
||||
|
||||
<div class="container">
|
||||
<FormFieldTemplateLarge label="Key" type="text" noMargin>
|
||||
<FormFieldTemplateLarge label={_t('addDbKeyModal.key', { defaultMessage: 'Key' })} type="text" noMargin>
|
||||
<TextField
|
||||
value={keyName}
|
||||
on:change={e => {
|
||||
@@ -41,7 +42,7 @@
|
||||
|
||||
<div class="m-3" />
|
||||
|
||||
<FormFieldTemplateLarge label="Type" type="combo" noMargin>
|
||||
<FormFieldTemplateLarge label={_t('addDbKeyModal.type', { defaultMessage: 'Type' })} type="combo" noMargin>
|
||||
<SelectField
|
||||
options={driver.supportedKeyTypes.map(t => ({ value: t.name, label: t.label }))}
|
||||
value={type}
|
||||
@@ -62,8 +63,8 @@
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormStyledButton value="OK" on:click={e => handleSubmit()} />
|
||||
<FormStyledButton type="button" value="Cancel" on:click={closeCurrentModal} />
|
||||
<FormStyledButton value={_t('common.ok', { defaultMessage: 'OK' })} on:click={e => handleSubmit()} />
|
||||
<FormStyledButton type="button" value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let onConfirm;
|
||||
export let url;
|
||||
@@ -18,13 +19,13 @@
|
||||
|
||||
<FormProvider initialValues={{ url }}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Download imported file from web</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('changeDownloadUrlModal.header', { defaultMessage: 'Download imported file from web' })}</svelte:fragment>
|
||||
|
||||
<FormTextField label="URL" name="url" style={{ width: '30vw' }} focused />
|
||||
<FormTextField label={_t('changeDownloadUrlModal.urlLabel', { defaultMessage: 'URL' })} name="url" style={{ width: '30vw' }} focused />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit value="OK" on:click={handleSubmit} />
|
||||
<FormStyledButton value="Cancel" on:click={closeCurrentModal} />
|
||||
<FormSubmit value={_t('common.ok', { defaultMessage: 'OK' })} on:click={handleSubmit} />
|
||||
<FormStyledButton value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let message = '';
|
||||
export let onConfirm;
|
||||
@@ -13,21 +14,21 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Choose archive folder</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('archiveFolderModal.chooseArchiveFolder', { defaultMessage: 'Choose archive folder' })}</svelte:fragment>
|
||||
|
||||
<div>{message}</div>
|
||||
|
||||
<FormArchiveFolderSelect label="Archive folder" name="archiveFolder" isNative allowCreateNew />
|
||||
<FormArchiveFolderSelect label={_t('archiveFolderModal.archiveFolder', { defaultMessage: 'Archive folder' })} name="archiveFolder" isNative allowCreateNew />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={e => {
|
||||
closeCurrentModal();
|
||||
onConfirm(e.detail.archiveFolder);
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { useCloudContentList } from '../utility/metadataLoaders';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let message = '';
|
||||
export let onConfirm;
|
||||
@@ -18,22 +19,21 @@
|
||||
{#if $cloudContentList}
|
||||
<FormProvider initialValues={{ cloudFolder: $cloudContentList?.find(x => x.isPrivate)?.folid }}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Choose cloud folder</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('cloudFolderModal.chooseCloudFolder', { defaultMessage: 'Choose cloud folder' })}</svelte:fragment>
|
||||
|
||||
<div>{message}</div>
|
||||
|
||||
<FormCloudFolderSelect label="Cloud folder" name="cloudFolder" isNative {requiredRoleVariants} />
|
||||
|
||||
<FormCloudFolderSelect label={_t('cloudFolderModal.cloudFolder', { defaultMessage: 'Cloud folder' })} name="cloudFolder" isNative {requiredRoleVariants} />
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={e => {
|
||||
closeCurrentModal();
|
||||
console.log('onConfirm', e.detail);
|
||||
onConfirm(e.detail.cloudFolder);
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value={_t('datagrid.closeTabs.close', { defaultMessage: 'Close tabs' })}
|
||||
data-testid="CloseTabModal_buttonConfirm"
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onConfirm();
|
||||
@@ -52,6 +53,7 @@
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value={_t('common.cancel', { defaultMessage: 'Cancel' })}
|
||||
data-testid="CloseTabModal_buttonCancel"
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onCancel();
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import _ from 'lodash';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let header = 'Configure columns';
|
||||
export let header = _t('columnMapModal.configureColumns', { defaultMessage: 'Configure columns' });
|
||||
export let onConfirm;
|
||||
|
||||
export let sourceTableInfo;
|
||||
@@ -69,7 +70,7 @@
|
||||
if (!value) return;
|
||||
if (value.length == 0) return;
|
||||
if (value.some(x => !x.src || !x.dst)) {
|
||||
validationError = 'Source and target columns must be defined';
|
||||
validationError = _t('columnMapModal.sourceAndTargetColumnsMustBeDefined', { defaultMessage: 'Source and target columns must be defined' });
|
||||
return;
|
||||
}
|
||||
const duplicates = _.chain(value.map(x => x.dst))
|
||||
@@ -78,7 +79,7 @@
|
||||
.keys()
|
||||
.value();
|
||||
if (duplicates.length > 0) {
|
||||
validationError = 'Target columns must be unique, duplicates found: ' + duplicates.join(', ');
|
||||
validationError = _t('columnMapModal.targetColumnsMustBeUnique', { defaultMessage: 'Target columns must be unique, duplicates found: ' }) + duplicates.join(', ');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -95,19 +96,19 @@
|
||||
|
||||
{#if resetValue.length == 0}
|
||||
<div class="m-3">
|
||||
When no columns are defined in this mapping, source row is copied to target without any modifications
|
||||
{_t('columnMapModal.noColumnsDefined', { defaultMessage: 'When no columns are defined in this mapping, source row is copied to target without any modifications' })}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<TableControl
|
||||
columns={[
|
||||
{ fieldName: 'use', header: 'Use', slot: 4 },
|
||||
{ fieldName: 'src', header: 'Source column', slot: 1 },
|
||||
{ fieldName: 'dst', header: 'Target column', slot: 2 },
|
||||
{ fieldName: 'use', header: _t('columnMapModal.use', { defaultMessage: 'Use' }), slot: 4 },
|
||||
{ fieldName: 'src', header: _t('columnMapModal.sourceColumn', { defaultMessage: 'Source column' }), slot: 1 },
|
||||
{ fieldName: 'dst', header: _t('columnMapModal.targetColumn', { defaultMessage: 'Target column' }), slot: 2 },
|
||||
{ fieldName: 'actions', header: '', slot: 3 },
|
||||
]}
|
||||
rows={value || []}
|
||||
emptyMessage="No transform defined"
|
||||
emptyMessage={_t('columnMapModal.noTransformDefined', { defaultMessage: 'No transform defined' })}
|
||||
>
|
||||
<svelte:fragment slot="4" let:row let:index>
|
||||
<CheckboxField
|
||||
@@ -136,7 +137,7 @@
|
||||
<Link
|
||||
onClick={() => {
|
||||
value = value.filter((x, i) => i != index);
|
||||
}}>Remove</Link
|
||||
}}>{_t('common.Remove', { defaultMessage: 'Remove' })}</Link
|
||||
>
|
||||
</svelte:fragment>
|
||||
</TableControl>
|
||||
@@ -160,14 +161,14 @@
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Add column"
|
||||
value={_t('columnMapModal.addColumn', { defaultMessage: 'Add column' })}
|
||||
on:click={() => {
|
||||
value = [...(value || []), {}];
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Reset"
|
||||
value={_t('columnMapModal.reset', { defaultMessage: 'Reset' })}
|
||||
disabled={!differentFromReset}
|
||||
on:click={() => {
|
||||
value = resetValue;
|
||||
|
||||
@@ -5,17 +5,18 @@
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let message;
|
||||
export let onConfirm;
|
||||
export let confirmLabel = 'OK';
|
||||
export let confirmLabel = _t('common.ok', { defaultMessage: 'OK' });
|
||||
export let header = null;
|
||||
</script>
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">
|
||||
{header || 'Confirm'}
|
||||
{header || _t('common.confirm', { defaultMessage: 'Confirm' })}
|
||||
</svelte:fragment>
|
||||
|
||||
{message}
|
||||
@@ -31,7 +32,7 @@
|
||||
/>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Close"
|
||||
value={_t('common.close', { defaultMessage: 'Close' })}
|
||||
on:click={closeCurrentModal}
|
||||
data-testid="ConfirmModal_closeButton"
|
||||
/>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let script;
|
||||
export let onConfirm;
|
||||
@@ -19,7 +20,7 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">Save changes</div>
|
||||
<div slot="header">{_t('common.saveChanges', { defaultMessage: 'Save changes' })}</div>
|
||||
|
||||
<div class="editor">
|
||||
<AceEditor mode="javascript" readOnly value={script} />
|
||||
@@ -28,7 +29,7 @@
|
||||
{#if skipConfirmSettingKey}
|
||||
<div class="mt-2">
|
||||
<TemplatedCheckboxField
|
||||
label="Don't ask again"
|
||||
label={_t('common.dontAskAgain', { defaultMessage: "Don't ask again" })}
|
||||
templateProps={{ noMargin: true }}
|
||||
checked={dontAskAgain}
|
||||
on:change={e => {
|
||||
@@ -41,16 +42,16 @@
|
||||
|
||||
<div slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onConfirm();
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Open script"
|
||||
value={_t('common.openScript', { defaultMessage: 'Open script' })}
|
||||
on:click={() => {
|
||||
newQuery({
|
||||
initialData: script,
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal, showModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let sql;
|
||||
export let onConfirm;
|
||||
@@ -81,7 +82,7 @@
|
||||
|
||||
<FormProviderCore {values}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">Save changes</div>
|
||||
<div slot="header">{_t('common.saveChanges', { defaultMessage: 'Save changes' })}</div>
|
||||
|
||||
<div class="editor">
|
||||
<SqlEditor {engine} value={currentScript} readOnly />
|
||||
@@ -91,7 +92,7 @@
|
||||
<div class="mt-2">
|
||||
<FormCheckboxField
|
||||
templateProps={{ noMargin: true }}
|
||||
label="Delete references CASCADE"
|
||||
label={_t('sqlModal.deleteReferencesCascade', { defaultMessage: 'Delete references CASCADE' })}
|
||||
name="deleteReferencesCascade"
|
||||
data-testid="ConfirmSqlModal_deleteReferencesCascade"
|
||||
/>
|
||||
@@ -101,13 +102,13 @@
|
||||
{#if $values.deleteReferencesCascade}
|
||||
<div class="form-margin flex">
|
||||
<FormStyledButton
|
||||
value="Check all"
|
||||
value={_t('common.checkAll', { defaultMessage: 'Check all' })}
|
||||
on:click={() => {
|
||||
$values = _.omitBy($values, (v, k) => k.startsWith('deleteReferencesFor_'));
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton
|
||||
value="Uncheck all"
|
||||
value={_t('common.uncheckAll', { defaultMessage: 'Uncheck all' })}
|
||||
on:click={() => {
|
||||
const newValues = { ...$values };
|
||||
for (const item of deleteCascadesScripts) {
|
||||
@@ -135,12 +136,11 @@
|
||||
{#if isRecreated}
|
||||
<div class="form-margin">
|
||||
<div>
|
||||
<FontIcon icon="img warn" /> This operation is not directly supported by SQL engine. DbGate can emulate it, but
|
||||
please check the generated SQL script.
|
||||
<FontIcon icon="img warn" /> {_t('sqlModal.recreateWarning', { defaultMessage: "This operation is not directly supported by SQL engine. DbGate can emulate it, but please check the generated SQL script." })}
|
||||
</div>
|
||||
<FormCheckboxField
|
||||
templateProps={{ noMargin: true }}
|
||||
label="Allow recreate (don't use on production databases)"
|
||||
label={_t('sqlModal.allowRecreate', { defaultMessage: "Allow recreate (don't use on production databases)" })}
|
||||
name="allowRecreate"
|
||||
/>
|
||||
</div>
|
||||
@@ -149,7 +149,7 @@
|
||||
{#if skipConfirmSettingKey}
|
||||
<div class="mt-2">
|
||||
<TemplatedCheckboxField
|
||||
label="Don't ask again"
|
||||
label={_t('common.dontAskAgain', { defaultMessage: "Don't ask again" })}
|
||||
templateProps={{ noMargin: true }}
|
||||
checked={dontAskAgain}
|
||||
on:change={e => {
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
<div slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
disabled={isRecreated && !$values.allowRecreate}
|
||||
on:click={e => {
|
||||
closeCurrentModal();
|
||||
@@ -172,13 +172,13 @@
|
||||
/>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Close"
|
||||
value={_t('common.close', { defaultMessage: 'Close' })}
|
||||
on:click={closeCurrentModal}
|
||||
data-testid="ConfirmSqlModal_closeButton"
|
||||
/>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Open script"
|
||||
value={_t('common.openScript', { defaultMessage: 'Open script' })}
|
||||
on:click={() => {
|
||||
newQuery({
|
||||
initialData: currentScript,
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
import { callServerPing } from '../utility/connectionsPinger';
|
||||
import { getConnectionLabel } from 'dbgate-tools';
|
||||
import { openedConnections } from '../stores';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let conid;
|
||||
export let passwordMode;
|
||||
@@ -125,11 +126,11 @@
|
||||
|
||||
<FormProviderCore {values}>
|
||||
<ModalBase {...$$restProps} simple>
|
||||
<svelte:fragment slot="header">Database Log In ({engineTitle})</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('databaseLoginModal.header', { defaultMessage: 'Database Log In ({engineTitle})', values: {engineTitle} })}</svelte:fragment>
|
||||
|
||||
<FormTextField label="Connection" name="connectionLabel" disabled />
|
||||
<FormTextField label={_t('databaseLoginModal.connection', { defaultMessage: 'Connection' })} name="connectionLabel" disabled />
|
||||
<FormTextField
|
||||
label="Username"
|
||||
label={_t('databaseLoginModal.username', { defaultMessage: 'Username' })}
|
||||
name="user"
|
||||
autocomplete="username"
|
||||
disabled={usedPasswordMode == 'askPassword'}
|
||||
@@ -138,7 +139,7 @@
|
||||
data-testid="DatabaseLoginModal_username"
|
||||
/>
|
||||
<FormPasswordField
|
||||
label="Password"
|
||||
label={_t('databaseLoginModal.password', { defaultMessage: 'Password' })}
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
focused={usedPasswordMode == 'askPassword'}
|
||||
@@ -148,34 +149,34 @@
|
||||
|
||||
{#if isTesting}
|
||||
<div>
|
||||
<FontIcon icon="icon loading" /> Testing connection
|
||||
<FontIcon icon="icon loading" /> {_t('databaseLoginModal.testingConnection', { defaultMessage: 'Testing connection' })}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !isTesting && sqlConnectResult && sqlConnectResult.msgtype == 'error'}
|
||||
<div class="error-result">
|
||||
Connect failed: <FontIcon icon="img error" />
|
||||
{_t('databaseLoginModal.connectFailed', { defaultMessage: 'Connect failed:' })} <FontIcon icon="img error" />
|
||||
{sqlConnectResult.error}
|
||||
<Link
|
||||
onClick={() =>
|
||||
showModal(ErrorMessageModal, {
|
||||
message: sqlConnectResult.detail,
|
||||
showAsCode: true,
|
||||
title: 'Database connection error',
|
||||
title: _t('databaseLoginModal.connectionError', { defaultMessage: 'Database connection error' }),
|
||||
})}
|
||||
>
|
||||
Show detail
|
||||
{_t('databaseLoginModal.showDetail', { defaultMessage: 'Show detail' })}
|
||||
</Link>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
{#if isTesting}
|
||||
<FormStyledButton value="Stop connecting" on:click={handleCancelTest} data-testid="DatabaseLoginModal_stop" />
|
||||
<FormStyledButton value={_t('databaseLoginModal.stopConnecting', { defaultMessage: 'Stop connecting' })} on:click={handleCancelTest} data-testid="DatabaseLoginModal_stop" />
|
||||
{:else}
|
||||
<FormSubmit value="Connect" on:click={handleSubmit} data-testid="DatabaseLoginModal_connect" />
|
||||
<FormSubmit value={_t('databaseLoginModal.connect', { defaultMessage: 'Connect' })} on:click={handleSubmit} data-testid="DatabaseLoginModal_connect" />
|
||||
{/if}
|
||||
<FormStyledButton value="Close" on:click={closeCurrentModal} data-testid="DatabaseLoginModal_close" />
|
||||
<FormStyledButton value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} data-testid="DatabaseLoginModal_close" />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProviderCore>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let keyInfo;
|
||||
export let label;
|
||||
@@ -21,7 +22,7 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Add item</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('dbKeyAddItemModal.header', { defaultMessage: 'Add item' })}</svelte:fragment>
|
||||
|
||||
<div class="container">
|
||||
<DbKeyItemDetail
|
||||
@@ -34,8 +35,8 @@
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormStyledButton value="OK" on:click={e => handleSubmit()} />
|
||||
<FormStyledButton type="button" value="Cancel" on:click={closeCurrentModal} />
|
||||
<FormStyledButton value={_t('common.ok', { defaultMessage: 'OK' })} on:click={e => handleSubmit()} />
|
||||
<FormStyledButton type="button" value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import { currentDatabase } from '../stores';
|
||||
import { filterAppsForDatabase } from '../utility/appTools';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@@ -52,10 +53,10 @@
|
||||
|
||||
<FormProviderCore {values}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Define description</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('defineDictionaryDescriptionModal.header', { defaultMessage: 'Define description' })}</svelte:fragment>
|
||||
|
||||
<FormSelectField
|
||||
label="Target application (mandatory)"
|
||||
label={_t('defineDictionaryDescriptionModal.targetApplication', { defaultMessage: 'Target application (mandatory)' })}
|
||||
name="targetApplication"
|
||||
disableInitialize
|
||||
selectFieldComponent={TargetApplicationSelect}
|
||||
@@ -68,8 +69,8 @@
|
||||
rows={$tableInfo?.columns || []}
|
||||
columns={[
|
||||
{ fieldName: 'checked', header: '', slot: 1 },
|
||||
{ fieldName: 'columnName', header: 'Column' },
|
||||
{ fieldName: 'dataType', header: 'Data type' },
|
||||
{ fieldName: 'columnName', header: _t('defineDictionaryDescriptionModal.column', { defaultMessage: 'Column' }) },
|
||||
{ fieldName: 'dataType', header: _t('defineDictionaryDescriptionModal.dataType', { defaultMessage: 'Data type' }) },
|
||||
]}
|
||||
>
|
||||
<input
|
||||
@@ -88,15 +89,15 @@
|
||||
</TableControl>
|
||||
</div>
|
||||
|
||||
<FormTextField name="columns" label="Show columns" />
|
||||
<FormTextField name="columns" label={_t('defineDictionaryDescriptionModal.showColumns', { defaultMessage: 'Show columns' })} />
|
||||
|
||||
<FormTextField name="delimiter" label="Delimiter" />
|
||||
<FormTextField name="delimiter" label={_t('defineDictionaryDescriptionModal.delimiter', { defaultMessage: 'Delimiter' })} />
|
||||
|
||||
<!-- <FormCheckboxField name="useForAllDatabases" label="Use for all databases" /> -->
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
disabled={!checkDescriptionExpression($values?.columns, $tableInfo) || !$values.targetApplication}
|
||||
on:click={async () => {
|
||||
closeCurrentModal();
|
||||
@@ -122,7 +123,7 @@
|
||||
onConfirm?.();
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProviderCore>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import _ from 'lodash';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let onConfirm;
|
||||
export let conid;
|
||||
@@ -126,15 +127,15 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Lookup from {pureName}</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('dictionaryLookupModal.header', { defaultMessage: 'Lookup from {pureName}', values: {pureName} })}</svelte:fragment>
|
||||
|
||||
<!-- <FormTextField name="search" label='Search' placeholder="Search" bind:value={search} /> -->
|
||||
<div class="largeFormMarker">
|
||||
<SearchInput placeholder="Search" bind:value={search} isDebounced />
|
||||
<SearchInput placeholder={_t("common.search", { defaultMessage: "Search" })} bind:value={search} isDebounced />
|
||||
</div>
|
||||
|
||||
{#if isLoading}
|
||||
<LoadingInfo message="Loading data" />
|
||||
<LoadingInfo message={_t('dictionaryLookupModal.loadingData', { defaultMessage: "Loading data" })} />
|
||||
{/if}
|
||||
|
||||
{#if !isLoading && tableInfo && description && rows && tableInfo?.primaryKey?.columns?.length == 1}
|
||||
@@ -161,13 +162,13 @@
|
||||
},
|
||||
{
|
||||
fieldName: 'value',
|
||||
header: 'Value',
|
||||
header: _t('dictionaryLookupModal.value', { defaultMessage: 'Value' }),
|
||||
formatter: row => row[tableInfo.primaryKey.columns[0].columnName],
|
||||
width: '100px',
|
||||
},
|
||||
{
|
||||
fieldName: 'description',
|
||||
header: 'Description',
|
||||
header: _t('dictionaryLookupModal.description', { defaultMessage: 'Description' }),
|
||||
formatter: row => description.columns.map(col => row[col]).join(description.delimiter || ' '),
|
||||
},
|
||||
]}
|
||||
@@ -194,15 +195,15 @@
|
||||
<svelte:fragment slot="footer">
|
||||
{#if multiselect}
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onConfirm(checkedKeys);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value="Customize" on:click={defineDescription} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('dictionaryLookupModal.customize', { defaultMessage: 'Customize' })} on:click={defineDescription} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -164,7 +164,12 @@
|
||||
changeActiveSubmenu();
|
||||
}}
|
||||
>
|
||||
<a on:click={e => handleClick(e, item)} class:disabled={item.disabled} class:bold={item.isBold}>
|
||||
<a
|
||||
on:click={e => handleClick(e, item)}
|
||||
class:disabled={item.disabled}
|
||||
class:bold={item.isBold}
|
||||
data-testid={item.testid}
|
||||
>
|
||||
<span>
|
||||
{#if item.switchValue && item.switchStoreGetter}
|
||||
{#key switchIndex}
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<div slot="footer" class="footer">
|
||||
<div>
|
||||
<FormStyledButton
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
title="Ctrl+Enter"
|
||||
on:click={() => {
|
||||
onSave(parseCellValue(textValue, dataEditorTypesBehaviour));
|
||||
|
||||
@@ -30,11 +30,10 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">Edit JSON value</div>
|
||||
<div slot="header">{_t('editJsonModal.header', { defaultMessage: 'Edit JSON value' })}</div>
|
||||
{#if showPasteInfo}
|
||||
<div class="m-2">
|
||||
Edit JSON object or array. You can paste JSON array or object directly into data grid, new row(s) will be added
|
||||
to recordset.
|
||||
{_t('editJsonModal.pasteInfo', { defaultMessage: "Edit JSON object or array. You can paste JSON array or object directly into data grid, new row(s) will be added to recordset." })}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -168,11 +168,11 @@
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps} fullScreen>
|
||||
<div slot="header">
|
||||
{mode == 'export' ? 'Export' : 'Import'} connections & settings
|
||||
{mode == 'export' ? 'Export' : 'Import'} {_t('importExport.connectionsSettings', { defaultMessage: 'connections & settings' })}
|
||||
<span class="check-uncheck">
|
||||
<Link onClick={() => handleCheckAll(true)}>Check all</Link>
|
||||
<Link onClick={() => handleCheckAll(true)}>{_t('common.checkAll', { defaultMessage: 'Check all' })}</Link>
|
||||
|
|
||||
<Link onClick={() => handleCheckAll(false)}>Uncheck all</Link>
|
||||
<Link onClick={() => handleCheckAll(false)}>{_t('common.uncheckAll', { defaultMessage: 'Uncheck all' })}</Link>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -180,16 +180,16 @@
|
||||
<TabControl
|
||||
tabs={_.compact([
|
||||
connections?.length && {
|
||||
label: `Connections (${checkedConnections?.length}/${connections?.length})`,
|
||||
label: _t('importExport.connectionsNum', { defaultMessage:'Connections ({checkedConnections}/{connections})', values: { checkedConnections: checkedConnections?.length, connections: connections?.length } }),
|
||||
slot: 1,
|
||||
},
|
||||
users?.length && { label: `Users (${checkedUsers?.length}/${users?.length})`, slot: 2 },
|
||||
roles?.length && { label: `Roles (${checkedRoles?.length}/${roles?.length})`, slot: 3 },
|
||||
users?.length && { label: _t('importExport.usersNum', { defaultMessage:'Users ({checkedUsers}/{users})', values: { checkedUsers: checkedUsers?.length, users: users?.length } }), slot: 2 },
|
||||
roles?.length && { label: _t('importExport.rolesNum', { defaultMessage:'Roles ({checkedRoles}/{roles})', values: { checkedRoles: checkedRoles?.length, roles: roles?.length } }), slot: 3 },
|
||||
authMethods?.length && {
|
||||
label: `Auth methods (${checkedAuthMethods?.length}/${authMethods?.length})`,
|
||||
label: _t('importExport.authMethodsNum', { defaultMessage:'Auth methods ({checkedAuthMethods}/{authMethods})', values: { checkedAuthMethods: checkedAuthMethods?.length, authMethods: authMethods?.length } }),
|
||||
slot: 4,
|
||||
},
|
||||
config?.length && { label: `Config (${checkedConfig?.length}/${config?.length})`, slot: 5 },
|
||||
config?.length && { label: _t('importExport.configNum', { defaultMessage:'Config ({checkedConfig}/{config})', values: { checkedConfig: checkedConfig?.length, config: config?.length } }), slot: 5 },
|
||||
])}
|
||||
>
|
||||
<svelte:fragment slot="1">
|
||||
@@ -199,10 +199,10 @@
|
||||
stickyHeader
|
||||
columns={[
|
||||
{ header: 'ID', fieldName: 'id', sortable: true, filterable: true },
|
||||
{ header: 'Display name', fieldName: 'displayName', sortable: true, filterable: true },
|
||||
{ header: 'Engine', fieldName: 'engine', sortable: true, filterable: true },
|
||||
{ header: 'Server', fieldName: 'server', sortable: true, filterable: true },
|
||||
{ header: 'User', fieldName: 'user', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.displayName', { defaultMessage: 'Display name' }), fieldName: 'displayName', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.engine', { defaultMessage: 'Engine' }), fieldName: 'engine', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.server', { defaultMessage: 'Server' }), fieldName: 'server', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.user', { defaultMessage: 'User' }), fieldName: 'user', sortable: true, filterable: true },
|
||||
]}
|
||||
clickable
|
||||
rows={connections}
|
||||
@@ -225,8 +225,8 @@
|
||||
stickyHeader
|
||||
columns={[
|
||||
{ header: 'ID', fieldName: 'id', sortable: true, filterable: true },
|
||||
{ header: 'Login', fieldName: 'login', sortable: true, filterable: true },
|
||||
{ header: 'E-mail', fieldName: 'email', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.login', { defaultMessage: 'Login' }), fieldName: 'login', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.email', { defaultMessage: 'E-mail' }), fieldName: 'email', sortable: true, filterable: true },
|
||||
]}
|
||||
clickable
|
||||
rows={users}
|
||||
@@ -249,7 +249,7 @@
|
||||
stickyHeader
|
||||
columns={[
|
||||
{ header: 'ID', fieldName: 'id', sortable: true, filterable: true },
|
||||
{ header: 'Name', fieldName: 'name', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.name', { defaultMessage: 'Name' }), fieldName: 'name', sortable: true, filterable: true },
|
||||
]}
|
||||
clickable
|
||||
rows={roles}
|
||||
@@ -272,8 +272,8 @@
|
||||
stickyHeader
|
||||
columns={[
|
||||
{ header: 'ID', fieldName: 'id', sortable: true, filterable: true },
|
||||
{ header: 'Name', fieldName: 'name', sortable: true, filterable: true },
|
||||
{ header: 'Type', fieldName: 'type', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.name', { defaultMessage: 'Name' }), fieldName: 'name', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.type', { defaultMessage: 'Type' }), fieldName: 'type', sortable: true, filterable: true },
|
||||
]}
|
||||
clickable
|
||||
rows={authMethods}
|
||||
@@ -296,9 +296,9 @@
|
||||
stickyHeader
|
||||
columns={[
|
||||
{ header: 'ID', fieldName: 'id', sortable: true, filterable: true },
|
||||
{ header: 'Group', fieldName: 'group', sortable: true, filterable: true },
|
||||
{ header: 'Key', fieldName: 'key', sortable: true, filterable: true },
|
||||
{ header: 'Value', fieldName: 'value', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.group', { defaultMessage: 'Group' }), fieldName: 'group', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.key', { defaultMessage: 'Key' }), fieldName: 'key', sortable: true, filterable: true },
|
||||
{ header: _t('importExport.value', { defaultMessage: 'Value' }), fieldName: 'value', sortable: true, filterable: true },
|
||||
]}
|
||||
clickable
|
||||
rows={config}
|
||||
@@ -340,7 +340,7 @@
|
||||
>
|
||||
{/if}
|
||||
<LargeButton icon="icon close" on:click={closeCurrentModal} data-testid="EditJsonModal_closeButton"
|
||||
>Close</LargeButton
|
||||
>{_t('common.close', { defaultMessage: 'Close' })}</LargeButton
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormButton from '../forms/FormButton.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let editingData;
|
||||
export let savingTab;
|
||||
@@ -113,28 +114,28 @@
|
||||
|
||||
<FormProvider {initialValues}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">{editingData ? 'Edit favorite' : 'Share / add to favorites'}</svelte:fragment>
|
||||
<svelte:fragment slot="header">{editingData ? _t('favorite.editFavorite', { defaultMessage: 'Edit favorite' }) : _t('favorite.shareAddToFavorites', { defaultMessage: 'Share / add to favorites' })}</svelte:fragment>
|
||||
|
||||
<FormTextField label="Title" name="title" focused />
|
||||
<FormTextField label="Icon" name="icon" />
|
||||
<FormTextField label={_t('favorite.title', { defaultMessage: 'Title' })} name="title" focused />
|
||||
<FormTextField label={_t('favorite.icon', { defaultMessage: 'Icon' })} name="icon" />
|
||||
|
||||
<FormTextField label="URL path" name="urlPath" />
|
||||
<FormTextField label={_t('favorite.urlPath', { defaultMessage: 'URL path' })} name="urlPath" />
|
||||
{#if !!savingTab && !electron && canWriteFavorite}
|
||||
<FormCheckboxField label="Share as link" name="shareAsLink" />
|
||||
<FormCheckboxField label={_t('favorite.shareAsLink', { defaultMessage: 'Share as link' })} name="shareAsLink" />
|
||||
{/if}
|
||||
<FormValues let:values>
|
||||
{#if !values.shareAsLink && canWriteFavorite}
|
||||
<FormCheckboxField label="Show in toolbar" name="showInToolbar" />
|
||||
<FormCheckboxField label="Open on startup" name="openOnStartup" />
|
||||
<FormCheckboxField label={_t('favorite.showInToolbar', { defaultMessage: 'Show in toolbar' })} name="showInToolbar" />
|
||||
<FormCheckboxField label={_t('favorite.openOnStartup', { defaultMessage: 'Open on startup' })} name="openOnStartup" />
|
||||
{/if}
|
||||
</FormValues>
|
||||
{#if !!savingTab && !!savedFile}
|
||||
<FormSelectField
|
||||
label="What to save"
|
||||
label={_t('favorite.whatToSave', { defaultMessage: 'What to save' })}
|
||||
name="whatToSave"
|
||||
options={[
|
||||
{ label: 'Link to file', value: 'fileName' },
|
||||
{ label: 'Content', value: 'content' },
|
||||
{ label: _t('favorite.linkToFile', { defaultMessage: 'Link to file' }), value: 'fileName' },
|
||||
{ label: _t('favorite.content', { defaultMessage: 'Content' }), value: 'content' },
|
||||
]}
|
||||
/>
|
||||
{/if}
|
||||
@@ -142,12 +143,12 @@
|
||||
<svelte:fragment slot="footer">
|
||||
<FormValues let:values>
|
||||
{#if !values.shareAsLink && canWriteFavorite}
|
||||
<FormSubmit value="OK" on:click={handleSubmit} />
|
||||
<FormSubmit value={_t('common.ok', { defaultMessage: 'OK' })} on:click={handleSubmit} />
|
||||
{/if}
|
||||
{#if values.shareAsLink || !canWriteFavorite}
|
||||
<FormButton value="Copy link" on:click={handleCopyLink} />
|
||||
<FormButton value={_t('common.copyLink', { defaultMessage: 'Copy link' })} on:click={handleCopyLink} />
|
||||
{/if}
|
||||
<FormButton value="Cancel" on:click={closeCurrentModal} />
|
||||
<FormButton value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} />
|
||||
</FormValues>
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let onFilter;
|
||||
|
||||
@@ -16,35 +17,35 @@
|
||||
</script>
|
||||
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">Filter multiple values</div>
|
||||
<div slot="header">{_t('filterMultipleValues.filterMultipleValues', { defaultMessage: 'Filter multiple values' })}</div>
|
||||
|
||||
<div class="flex">
|
||||
<TextAreaField rows={10} bind:value focused />
|
||||
<div>
|
||||
<div>
|
||||
<input type="radio" bind:group value="is" id="__is" />
|
||||
<label for="__is">Is one of line</label>'
|
||||
<label for="__is">{_t('filterMultipleValues.isOneOfLine', { defaultMessage: 'Is one of line' })}</label>'
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" bind:group value="is_not" id="__is_not" />
|
||||
<label for="__is_not">Is not one of line</label>'
|
||||
<label for="__is_not">{_t('filterMultipleValues.isNotOneOfLine', { defaultMessage: 'Is not one of line' })}</label>'
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" bind:group value="contains" id="__contains" />
|
||||
<label for="__contains">Contains</label>'
|
||||
<label for="__contains">{_t('filterMultipleValues.contains', { defaultMessage: 'Contains' })}</label>'
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" bind:group value="begins" id="__begins" />
|
||||
<label for="__begins">Begins</label>'
|
||||
<label for="__begins">{_t('filterMultipleValues.begins', { defaultMessage: 'Begins' })}</label>'
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" bind:group value="ends" id="__ends" />
|
||||
<label for="__ends">Ends</label>'
|
||||
<label for="__ends">{_t('filterMultipleValues.ends', { defaultMessage: 'Ends' })}</label>'
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<FormStyledButton value="OK" on:click={handleOk} />
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton value={_t('common.ok', { defaultMessage: 'OK' })} on:click={handleOk} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</div>
|
||||
</ModalBase>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import newQuery from '../query/newQuery';
|
||||
import SqlEditor from '../query/SqlEditor.svelte';
|
||||
import keycodes from '../utility/keycodes';
|
||||
import { _t } from '../translations';
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
@@ -77,24 +78,23 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Generate SQL from data</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('generateSqlFromData.generateSqlFromData', { defaultMessage: 'Generate SQL from data' })}</svelte:fragment>
|
||||
|
||||
<div class="flex mb-3">
|
||||
<div class="m-1 col-4">
|
||||
<div class="m-1">Choose query type</div>
|
||||
|
||||
<div class="m-1">{_t('generateSqlFromData.chooseQueryType', { defaultMessage: 'Choose query type' })}</div>
|
||||
<TableControl
|
||||
rows={QUERY_TYPES.map(name => ({ name }))}
|
||||
bind:selectedIndex={queryTypeIndex}
|
||||
bind:domTable={domQueryType}
|
||||
focusOnCreate
|
||||
selectable
|
||||
columns={[{ fieldName: 'name', header: 'Query type' }]}
|
||||
columns={[{ fieldName: 'name', header: _t('generateSqlFromData.queryType', { defaultMessage: 'Query type' }) }]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-1 col-4">
|
||||
<div class="m-1">Value columns</div>
|
||||
<div class="m-1">{_t('generateSqlFromData.valueColumns', { defaultMessage: 'Value columns' })}</div>
|
||||
|
||||
<CheckableColumnList
|
||||
{allColumns}
|
||||
@@ -120,13 +120,13 @@
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={() => {
|
||||
newQuery({ initialData: sqlPreview });
|
||||
closeCurrentModal();
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<FormTextField {label} name="value" focused data-testid="InputTextModal_value" />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit value="OK" on:click={e => handleSubmit(e.detail)} data-testid="InputTextModal_ok" />
|
||||
<FormSubmit value={_t('common.ok', { defaultMessage: 'OK' })} on:click={e => handleSubmit(e.detail)} data-testid="InputTextModal_ok" />
|
||||
<FormStyledButton type="button" value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} data-testid="InputTextModal_cancel" />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
import _ from 'lodash';
|
||||
|
||||
export let sql;
|
||||
export let onInsert;
|
||||
@@ -104,12 +106,11 @@
|
||||
</script>
|
||||
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Insert join</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('insertJoin.insertJoin', { defaultMessage: 'Insert join' })}</svelte:fragment>
|
||||
|
||||
<div class="flex mb-3">
|
||||
<div class="m-1 col-3">
|
||||
<div class="m-1">Existing table</div>
|
||||
|
||||
<div class="m-1">{_t('insertJoin.existingTable', { defaultMessage: 'Existing table' })}</div>
|
||||
<TableControl
|
||||
rows={sources}
|
||||
focusOnCreate
|
||||
@@ -118,14 +119,14 @@
|
||||
selectable
|
||||
on:keydown={sourceKeyDown}
|
||||
columns={[
|
||||
{ fieldName: 'alias', header: 'Alias' },
|
||||
{ fieldName: 'name', header: 'Name' },
|
||||
{ fieldName: 'alias', header: _t('insertJoin.alias', { defaultMessage: 'Alias' }) },
|
||||
{ fieldName: 'name', header: _t('insertJoin.name', { defaultMessage: 'Name' }) },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-1 col-6">
|
||||
<div class="m-1">New table</div>
|
||||
<div class="m-1">{_t('insertJoin.newTable', { defaultMessage: 'New table' })}</div>
|
||||
|
||||
<TableControl
|
||||
rows={targets}
|
||||
@@ -134,15 +135,15 @@
|
||||
selectable
|
||||
on:keydown={targetKeyDown}
|
||||
columns={[
|
||||
{ fieldName: 'baseColumns', header: 'Column from' },
|
||||
{ fieldName: 'refTable', header: 'Table to' },
|
||||
{ fieldName: 'refColumns', header: 'Column to' },
|
||||
{ fieldName: 'baseColumns', header: _t('insertJoin.columnFrom', { defaultMessage: 'Column from' }) },
|
||||
{ fieldName: 'refTable', header: _t('insertJoin.tableTo', { defaultMessage: 'Table to' }) },
|
||||
{ fieldName: 'refColumns', header: _t('insertJoin.columnTo', { defaultMessage: 'Column to' }) },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-1 col-3">
|
||||
<div class="m-1">Join</div>
|
||||
<div class="m-1">{_t('insertJoin.join', { defaultMessage: 'Join' })}</div>
|
||||
|
||||
<TableControl
|
||||
rows={JOIN_TYPES.map(name => ({ name }))}
|
||||
@@ -150,10 +151,10 @@
|
||||
bind:domTable={domJoin}
|
||||
selectable
|
||||
on:keydown={joinKeyDown}
|
||||
columns={[{ fieldName: 'name', header: 'Join type' }]}
|
||||
columns={[{ fieldName: 'name', header: _t('insertJoin.joinType', { defaultMessage: 'Join type' }) }]}
|
||||
/>
|
||||
|
||||
<div class="m-1">Alias</div>
|
||||
<div class="m-1">{_t('insertJoin.alias', { defaultMessage: 'Alias' })}</div>
|
||||
<TextField
|
||||
value={alias}
|
||||
on:input={e => {
|
||||
@@ -171,13 +172,13 @@
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormStyledButton
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onInsert(sqlPreview);
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</script>
|
||||
|
||||
<ModalBase {...$$restProps} simple>
|
||||
<div class="mb-2">_{_t('commandModal.showKeyCombination', { defaultMessage: 'Show desired key combination and press ENTER' })}</div>
|
||||
<div class="mb-2">{_t('commandModal.showKeyCombination', { defaultMessage: 'Show desired key combination and press ENTER' })}</div>
|
||||
<div class="largeFormMarker">
|
||||
<TextField on:keydown={handleKeyDown} bind:value focused />
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import { openWebLink } from '../utility/simpleTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
@@ -15,7 +16,7 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">License limit error</div>
|
||||
<div slot="header">{_t('licenseLimit.licenseLimitError', { defaultMessage: 'License limit error' })}</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="icon">
|
||||
@@ -23,15 +24,15 @@
|
||||
</div>
|
||||
<div data-testid="LicenseLimitMessageModal_message">
|
||||
<p>
|
||||
Cloud operation ended with error:<br />
|
||||
{_t('licenseLimit.cloudOperationEndedWithError', { defaultMessage: 'Cloud operation ended with error:' })}<br />
|
||||
{message}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This is a limitation of the free version of DbGate. To continue using cloud operations, please {#if !isProApp()}download
|
||||
and{/if} purchase DbGate Premium.
|
||||
{_t('licenseLimit.limitationMessage', { defaultMessage: 'This is a limitation of the free version of DbGate. To continue using cloud operations, please' })} {#if !isProApp()} {_t('licenseLimit.download', { defaultMessage: 'download and' })}
|
||||
{/if} {_t('licenseLimit.purchase', { defaultMessage: 'purchase DbGate Premium.' })}
|
||||
</p>
|
||||
<p>Free version limit:</p>
|
||||
<p>{_t('licenseLimit.freeVersionLimit', { defaultMessage: 'Free version limit:' })}</p>
|
||||
<ul>
|
||||
{#each licenseLimits || [] as limit}
|
||||
<li>{limit}</li>
|
||||
@@ -41,16 +42,16 @@
|
||||
</div>
|
||||
|
||||
<div slot="footer">
|
||||
<FormSubmit value="Close" on:click={closeCurrentModal} data-testid="LicenseLimitMessageModal_closeButton" />
|
||||
<FormSubmit value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} data-testid="LicenseLimitMessageModal_closeButton" />
|
||||
{#if !isProApp()}
|
||||
<FormStyledButton
|
||||
value="Download DbGate Premium"
|
||||
value={_t('licenseLimit.downloadDbGatePremium', { defaultMessage: 'Download DbGate Premium' })}
|
||||
on:click={() => openWebLink('https://www.dbgate.io/download/')}
|
||||
skipWidth
|
||||
/>
|
||||
{/if}
|
||||
<FormStyledButton
|
||||
value="Purchase DbGate Premium"
|
||||
value={_t('licenseLimit.purchaseDbGatePremium', { defaultMessage: 'Purchase DbGate Premium' })}
|
||||
on:click={() => openWebLink('https://www.dbgate.io/purchase/premium/')}
|
||||
skipWidth
|
||||
/>
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
|
||||
const { errorMessage } = resp || {};
|
||||
if (errorMessage) {
|
||||
showModal(ErrorMessageModal, { title: 'Error when executing operation', message: errorMessage });
|
||||
showModal(ErrorMessageModal, { title: _t('error.executingOperation', { defaultMessage: 'Error when executing operation' }), message: errorMessage });
|
||||
} else {
|
||||
showSnackbarSuccess('Saved to database');
|
||||
showSnackbarSuccess(_t('common.savedToDatabase', { defaultMessage: 'Saved to database' }));
|
||||
apiCall('database-connections/sync-model', dbid);
|
||||
closeCurrentModal();
|
||||
}
|
||||
@@ -51,7 +51,7 @@
|
||||
<FormArgumentList args={driver?.newCollectionFormParams} />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit value="OK" on:click={e => handleSubmit(e.detail)} disabled={isSaving} />
|
||||
<FormSubmit value={_t('common.ok', { defaultMessage: 'OK' })} on:click={e => handleSubmit(e.detail)} disabled={isSaving} />
|
||||
<FormStyledButton type="button" value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
{
|
||||
icon: 'icon compare',
|
||||
colorClass: 'color-icon-red',
|
||||
title: _t('common.compare', { defaultMessage: 'Compare database' }),
|
||||
title: _t('common.compareDatabase', { defaultMessage: 'Compare database' }),
|
||||
description: _t('newObject.compareDescription', { defaultMessage: 'Compare database schemas' }),
|
||||
command: 'database.compare',
|
||||
testid: 'NewObjectModal_databaseCompare',
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { _t } from '../translations';
|
||||
|
||||
import getElectron from '../utility/getElectron';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
@@ -26,7 +27,7 @@
|
||||
|
||||
<FormProvider initialValues={parameterValues}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Edit query parameters</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('queryParameters.editQueryParameters', { defaultMessage: 'Edit query parameters' })}</svelte:fragment>
|
||||
|
||||
<div class="params">
|
||||
{#each parameterNames as parameterName, index}
|
||||
@@ -34,11 +35,11 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div>String values must be 'quoted'. You can use valid SQL expressions.</div>
|
||||
<div>{_t('queryParameters.stringValuesMustBeQuoted', { defaultMessage: "String values must be 'quoted'. You can use valid SQL expressions." })}</div>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit value="Run query" on:click={handleSubmit} />
|
||||
<FormStyledButton value="Close" on:click={handleClose} />
|
||||
<FormSubmit value={_t('queryParameters.runQuery', { defaultMessage: 'Run query' })} on:click={handleSubmit} />
|
||||
<FormStyledButton value={_t('common.close', { defaultMessage: 'Close' })} on:click={handleClose} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let value;
|
||||
export let onConfirm;
|
||||
@@ -18,24 +19,24 @@
|
||||
|
||||
<FormProvider initialValues={{ value }}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Rows limit</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('query.rowsLimit', { defaultMessage: 'Rows limit' })}</svelte:fragment>
|
||||
|
||||
<FormTextField
|
||||
label="Return only N rows from query"
|
||||
label={_t('query.returnOnlyNRows', { defaultMessage: 'Return only N rows from query' })}
|
||||
name="value"
|
||||
focused
|
||||
data-testid="RowsLimitModal_value"
|
||||
placeholder="(No rows limit)"
|
||||
placeholder={_t('query.noRowsLimit', { defaultMessage: '(No rows limit)' })}
|
||||
/>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={e => handleSubmit(parseInt(e.detail.value) || null)}
|
||||
data-testid="RowsLimitModal_setLimit"
|
||||
/>
|
||||
<FormStyledButton value="Set no limit" on:click={e => handleSubmit(null)} data-testid="RowsLimitModal_setNoLimit" />
|
||||
<FormStyledButton type="button" value="Cancel" on:click={closeCurrentModal} data-testid="RowsLimitModal_cancel" />
|
||||
<FormStyledButton value={_t('common.setNoLimit', { defaultMessage: 'Set no limit' })} on:click={e => handleSubmit(null)} data-testid="RowsLimitModal_setNoLimit" />
|
||||
<FormStyledButton type="button" value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} data-testid="RowsLimitModal_cancel" />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { showSnackbarError } from '../utility/snackbar';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let script;
|
||||
export let header;
|
||||
@@ -77,14 +78,14 @@
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
{#if isRunning}
|
||||
<FormStyledButton value="Stop" on:click={handleStop} data-testid="RunScriptModal_stop" />
|
||||
<FormStyledButton value={_t('script.stop', { defaultMessage: 'Stop' })} on:click={handleStop} data-testid="RunScriptModal_stop" />
|
||||
{:else}
|
||||
<FormStyledButton value="Close" on:click={handleClose} data-testid="RunScriptModal_close" />
|
||||
<FormStyledButton value={_t('common.close', { defaultMessage: 'Close' })} on:click={handleClose} data-testid="RunScriptModal_close" />
|
||||
{/if}
|
||||
|
||||
{#if onOpenResult && !isRunning}
|
||||
<FormStyledButton
|
||||
value={openResultLabel || 'Open result'}
|
||||
value={openResultLabel || _t('script.openResult', { defaultMessage: 'Open result' })}
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onOpenResult();
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
|
||||
<FormProvider initialValues={{ file, folder }}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Save to archive</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('archive.saveToArchive', { defaultMessage: 'Save to archive' })}</svelte:fragment>
|
||||
|
||||
<FormArchiveFolderSelect label="Folder" name="folder" isNative allowCreateNew skipZipFiles />
|
||||
<FormTextField label="File name" name="file" disabled={fileIsReadOnly} />
|
||||
<FormArchiveFolderSelect label={_t('archive.folder', { defaultMessage: 'Folder' })} name="folder" isNative allowCreateNew skipZipFiles />
|
||||
<FormTextField label={_t('archive.fileName', { defaultMessage: 'File name' })} name="file" disabled={fileIsReadOnly} />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit value={_t('common.save', { defaultMessage: 'Save' })} on:click={handleSubmit} />
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
<FormTextField label="File name" name="name" focused />
|
||||
{#if $cloudSigninTokenHolder && !$values['saveToTeamFolder']}
|
||||
<FormCloudFolderSelect
|
||||
label="Choose cloud folder"
|
||||
label={_t('cloud.chooseCloudFolder', { defaultMessage: "Choose cloud folder" })}
|
||||
name="cloudFolder"
|
||||
isNative
|
||||
requiredRoleVariants={['write', 'admin']}
|
||||
@@ -134,13 +134,13 @@
|
||||
: [
|
||||
{
|
||||
folid: '__local',
|
||||
name: "Local folder (don't store on cloud)",
|
||||
name: _t('cloud.localFolder', { defaultMessage: "Local folder (don't store on cloud)" }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{/if}
|
||||
{#if $configValue?.storageDatabase}
|
||||
<FormCheckboxField label="Save to team folder" name="saveToTeamFolder" />
|
||||
<FormCheckboxField label={_t('cloud.saveToTeamFolder', { defaultMessage: "Save to team folder" })} name="saveToTeamFolder" />
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
@@ -148,12 +148,12 @@
|
||||
{#if electron}
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Save to disk"
|
||||
value={_t('common.saveToDisk', { defaultMessage: 'Save to disk' })}
|
||||
on:click={async () => {
|
||||
const file = await electron.showSaveDialog({
|
||||
filters: [
|
||||
{ name: `${fileExtension.toUpperCase()} files`, extensions: [fileExtension] },
|
||||
{ name: `All files`, extensions: ['*'] },
|
||||
{ name: _t('common.fileType', { defaultMessage: '{extension} files', values: {extension: fileExtension.toUpperCase()} }), extensions: [fileExtension] },
|
||||
{ name: _t('common.allFiles', { defaultMessage: 'All files' }), extensions: ['*'] },
|
||||
],
|
||||
defaultPath: filePath || `${name}.${fileExtension}`,
|
||||
properties: ['showOverwriteConfirmation'],
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</div>
|
||||
|
||||
<div slot="footer">
|
||||
<FormSubmit value="OK" on:click={handleOk} />
|
||||
<FormSubmit value={_t('common.ok', {defaultMessage: "OK"})} on:click={handleOk} />
|
||||
<FormButton type="button" value={_t('common.close', {defaultMessage: 'Close'})} on:click={closeCurrentModal} />
|
||||
</div>
|
||||
</ModalBase>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</script>
|
||||
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">SQL Script</div>
|
||||
<div slot="header">{_t('script.sqlScript', { defaultMessage: 'SQL Script' })}</div>
|
||||
|
||||
<div class="editor">
|
||||
<SqlEditor {engine} value={sql} readOnly />
|
||||
@@ -29,7 +29,7 @@
|
||||
/>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Open script"
|
||||
value={_t('common.openScript', { defaultMessage: "Open script" })}
|
||||
on:click={() => {
|
||||
newQuery({
|
||||
initialData: sql,
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||
import { getObjectTypeFieldLabel } from '../utility/common';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@@ -113,7 +114,7 @@
|
||||
function editSql() {
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Query #',
|
||||
title: _t('query.queryNumber', { defaultMessage: 'Query #' }),
|
||||
icon: 'img sql-file',
|
||||
tabComponent: 'QueryTab',
|
||||
focused: true,
|
||||
@@ -133,7 +134,7 @@
|
||||
<FormProviderCore values={valuesStore} template={FormFieldTemplateTiny}>
|
||||
<ModalBase {...$$restProps} fullScreen>
|
||||
<svelte:fragment slot="header">
|
||||
SQL Generator
|
||||
{_t('sqlGenerator.sqlGenerator', { defaultMessage: 'SQL Generator' })}
|
||||
<span class="dbname">
|
||||
<FontIcon icon="icon database" />
|
||||
{database}
|
||||
@@ -146,9 +147,9 @@
|
||||
<HorizontalSplitter initialValue="300px" bind:size={managerSize}>
|
||||
<svelte:fragment slot="1">
|
||||
<div class="flexcol flex1">
|
||||
<WidgetTitle>Choose objects</WidgetTitle>
|
||||
<WidgetTitle>{_t('sqlGenerator.chooseObjects', { defaultMessage: 'Choose objects' })}</WidgetTitle>
|
||||
<SearchBoxWrapper>
|
||||
<SearchInput placeholder="Search tables or objects" bind:value={objectsFilter} />
|
||||
<SearchInput placeholder={_t('sqlGenerator.searchTablesOrObjects', { defaultMessage: 'Search tables or objects' })} bind:value={objectsFilter} />
|
||||
</SearchBoxWrapper>
|
||||
|
||||
<WidgetsInnerContainer>
|
||||
@@ -174,54 +175,53 @@
|
||||
{:else}
|
||||
<div class="flexcol flex1">
|
||||
{#if truncated}
|
||||
<ErrorInfo icon="img warn" message="SQL truncated, file size limit exceed" />
|
||||
<ErrorInfo icon="img warn" message={_t('sqlGenerator.sqlTruncated', { defaultMessage: 'SQL truncated, file size limit exceed' })} />
|
||||
{/if}
|
||||
<div class="relative flex1">
|
||||
<SqlEditor readOnly value={sqlPreview} />
|
||||
</div>
|
||||
</div>
|
||||
{#if busy}
|
||||
<LoadingInfo wrapper message="Loading SQL preview" />
|
||||
<LoadingInfo wrapper message={_t('sqlGenerator.loadingSqlPreview', { defaultMessage: "Loading SQL preview" })} />
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="2">
|
||||
<div class="flexcol flex1">
|
||||
<WidgetTitle>Generator settings</WidgetTitle>
|
||||
<WidgetTitle>{_t('sqlGenerator.generatorSettings', { defaultMessage: 'Generator settings' })}</WidgetTitle>
|
||||
<WidgetsInnerContainer>
|
||||
<FormValues let:values>
|
||||
<div class="obj-heading">Tables</div>
|
||||
<FormCheckboxField label="Drop tables" name="dropTables" />
|
||||
<div class="obj-heading">{_t('sqlGenerator.tables', { defaultMessage: 'Tables' })}</div>
|
||||
<FormCheckboxField label={_t('sqlGenerator.dropTables', { defaultMessage: 'Drop tables' })} name="dropTables" />
|
||||
{#if values.dropTables}
|
||||
<div class="ml-2">
|
||||
<FormCheckboxField label="Test if exists" name="checkIfTableExists" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.testIfExists', { defaultMessage: 'Test if exists' })} name="checkIfTableExists" />
|
||||
</div>
|
||||
{/if}
|
||||
<FormCheckboxField label="Drop references" name="dropReferences" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.dropReferences', { defaultMessage: 'Drop references' })} name="dropReferences" />
|
||||
|
||||
<FormCheckboxField label="Create tables" name="createTables" />
|
||||
<FormCheckboxField label="Create references" name="createReferences" />
|
||||
<FormCheckboxField label="Create foreign keys" name="createForeignKeys" />
|
||||
<FormCheckboxField label="Create indexes" name="createIndexes" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.createTables', { defaultMessage: 'Create tables' })} name="createTables" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.createReferences', { defaultMessage: 'Create references' })} name="createReferences" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.createForeignKeys', { defaultMessage: 'Create foreign keys' })} name="createForeignKeys" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.createIndexes', { defaultMessage: 'Create indexes' })} name="createIndexes" />
|
||||
|
||||
<FormCheckboxField label="Insert" name="insert" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.insert', { defaultMessage: 'Insert' })} name="insert" />
|
||||
{#if values.insert}
|
||||
<div class="ml-2">
|
||||
<FormCheckboxField label="Skip autoincrement column" name="skipAutoincrementColumn" />
|
||||
<FormCheckboxField label="Disable constraints" name="disableConstraints" />
|
||||
<FormCheckboxField label="Omit NULL values" name="omitNulls" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.skipAutoincrementColumn', { defaultMessage: 'Skip autoincrement column' })} name="skipAutoincrementColumn" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.disableConstraints', { defaultMessage: 'Disable constraints' })} name="disableConstraints" />
|
||||
<FormCheckboxField label={_t('sqlGenerator.omitNulls', { defaultMessage: 'Omit NULL values' })} name="omitNulls" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<FormCheckboxField label="Truncate tables (delete all rows)" name="truncate" />
|
||||
|
||||
<FormCheckboxField label={_t('sqlGenerator.truncate', { defaultMessage: 'Truncate tables (delete all rows)' })} name="truncate" />
|
||||
{#each ['View', 'Matview', 'Procedure', 'Function', 'Trigger', 'SchedulerEvent'] as objtype}
|
||||
<div class="obj-heading">{getObjectTypeFieldLabel(objtype + 's')}</div>
|
||||
<FormCheckboxField label="Create" name={`create${objtype}s`} />
|
||||
<FormCheckboxField label="Drop" name={`drop${objtype}s`} />
|
||||
<FormCheckboxField label={_t('sqlGenerator.create', { defaultMessage: 'Create {objtype}s', values: {objtype} })} name={`create${objtype}s`} />
|
||||
<FormCheckboxField label={_t('sqlGenerator.drop', { defaultMessage: `Drop ${objtype}s` })} name={`drop${objtype}s`} />
|
||||
{#if values[`drop${objtype}s`]}
|
||||
<div class="ml-2">
|
||||
<FormCheckboxField label="Check if exists" name={`checkIf${objtype}Exists`} />
|
||||
<FormCheckboxField label={_t('sqlGenerator.checkIfExists', { defaultMessage: 'Check if exists' })} name={`checkIf${objtype}Exists`} />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -241,8 +241,8 @@
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<div class="flex m-2">
|
||||
<LargeButton on:click={editSql} icon="icon sql-file">Edit SQL</LargeButton>
|
||||
<LargeButton on:click={closeCurrentModal} icon="icon close">Close</LargeButton>
|
||||
<LargeButton on:click={editSql} icon="icon sql-file">{_t('sqlGenerator.editSql', { defaultMessage: 'Edit SQL' })}</LargeButton>
|
||||
<LargeButton on:click={closeCurrentModal} icon="icon close">{_t('common.close', { defaultMessage: 'Close' })}</LargeButton>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import FormConnectionSelect from '../impexp/FormConnectionSelect.svelte';
|
||||
import FormDatabaseSelect from '../impexp/FormDatabaseSelect.svelte';
|
||||
import { changeTab } from '../utility/common';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let editingData;
|
||||
export let callingTab;
|
||||
@@ -46,15 +47,15 @@
|
||||
|
||||
<FormProvider {initialValues}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Switch database</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('switchDatabase.switchDatabase', { defaultMessage: 'Switch database' })}</svelte:fragment>
|
||||
|
||||
<FormConnectionSelect name="conid" label="Server" direction="source" isNative />
|
||||
<FormDatabaseSelect conidName="conid" name="database" label="Database" isNative />
|
||||
<FormConnectionSelect name="conid" label={_t('switchDatabase.server', { defaultMessage: 'Server' })} direction="source" isNative />
|
||||
<FormDatabaseSelect conidName="conid" name="database" label={_t('common.database', { defaultMessage: 'Database' })} isNative />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormValues let:values>
|
||||
<FormSubmit value="OK" on:click={handleSubmit} />
|
||||
<FormButton value="Cancel" on:click={closeCurrentModal} />
|
||||
<FormSubmit value={_t('common.ok', { defaultMessage: 'OK' })} on:click={handleSubmit} />
|
||||
<FormButton value={_t('common.cancel', { defaultMessage: 'Cancel' })} on:click={closeCurrentModal} />
|
||||
</FormValues>
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
<svelte:fragment slot="footer">
|
||||
{#if multiselect}
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
value={_t('common.ok', { defaultMessage: 'OK' })}
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onConfirm(checkedKeys);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import type { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveCustomJoinConfig } from 'dbgate-datalib';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import TextField from '../forms/TextField.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@@ -90,7 +91,7 @@
|
||||
|
||||
$: connections = useConnectionList();
|
||||
$: connectionOptions = [
|
||||
{ value: null, label: 'The same as root' },
|
||||
{ value: null, label: _t('customJoin.theSameAsRoot', { defaultMessage: 'The same as root' }) },
|
||||
..._.sortBy(
|
||||
($connections || [])
|
||||
// .filter(x => !x.unsaved)
|
||||
@@ -107,7 +108,7 @@
|
||||
$: databases = useDatabaseList({ conid: conidOverride || conid });
|
||||
|
||||
$: databaseOptions = [
|
||||
{ value: null, label: 'The same as root' },
|
||||
{ value: null, label: _t('customJoin.theSameAsRoot', { defaultMessage: 'The same as root' }) },
|
||||
..._.sortBy(
|
||||
($databases || []).map(db => ({
|
||||
value: db.name,
|
||||
@@ -147,11 +148,11 @@
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Define custom join</svelte:fragment>
|
||||
<svelte:fragment slot="header">{_t('customJoin.defineCustomJoin', { defaultMessage: 'Define custom join' })}</svelte:fragment>
|
||||
|
||||
<div class="largeFormMarker">
|
||||
<div class="row">
|
||||
<div class="label col-3">Join name</div>
|
||||
<div class="label col-3">{_t('customJoin.joinName', { defaultMessage: 'Join name' })}</div>
|
||||
<div class="col-9">
|
||||
<TextField
|
||||
value={joinName}
|
||||
@@ -164,7 +165,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Base table</div>
|
||||
<div class="label col-3">{_t('customJoin.baseTable', { defaultMessage: 'Base table' })}</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={fromDesignerId}
|
||||
@@ -181,7 +182,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Connection</div>
|
||||
<div class="label col-3">{_t('customJoin.connection', { defaultMessage: 'Connection' })}</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={conidOverride}
|
||||
@@ -195,7 +196,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Database</div>
|
||||
<div class="label col-3">{_t('customJoin.database', { defaultMessage: 'Database' })}</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={databaseOverride}
|
||||
@@ -212,7 +213,7 @@
|
||||
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} label="Database" /> -->
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Referenced table</div>
|
||||
<div class="label col-3">{_t('customJoin.referencedTable', { defaultMessage: 'Referenced table' })}</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })}
|
||||
@@ -239,10 +240,10 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-5 mr-1">
|
||||
Base column - {fromTableInfo?.pureName}
|
||||
{_t('customJoin.baseColumn', { defaultMessage: 'Base column' })} - {fromTableInfo?.pureName}
|
||||
</div>
|
||||
<div class="col-5 ml-1">
|
||||
Ref column - {refTableName || '(table not set)'}
|
||||
{_t('customJoin.refColumn', { defaultMessage: 'Ref column' })} - {refTableName || _t('customJoin.tableNotSet', { defaultMessage: '(table not set)' })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -286,7 +287,7 @@
|
||||
</div>
|
||||
<div class="col-2 button">
|
||||
<FormStyledButton
|
||||
value="Delete"
|
||||
value={_t('common.delete', { defaultMessage: 'Delete' })}
|
||||
on:click={e => {
|
||||
const x = [...columns];
|
||||
x.splice(index, 1);
|
||||
@@ -299,7 +300,7 @@
|
||||
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Add column"
|
||||
value={_t('customJoin.addColumn', { defaultMessage: 'Add column' })}
|
||||
on:click={() => {
|
||||
columns = [
|
||||
...columns,
|
||||
@@ -314,7 +315,7 @@
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value={'Save'}
|
||||
value={_t('common.save', { defaultMessage: 'Save' })}
|
||||
on:click={() => {
|
||||
setConfig(cfg => {
|
||||
const newNode = createPerspectiveNodeConfig({ pureName: refTableName, schemaName: refSchemaName });
|
||||
@@ -361,7 +362,7 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { filterName } from 'dbgate-tools';
|
||||
import InlineButton from '../buttons/InlineButton.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let items: any[];
|
||||
export let showProcedure = false;
|
||||
@@ -49,49 +50,49 @@
|
||||
}}
|
||||
>
|
||||
<FontIcon icon="icon delete" padRight />
|
||||
Clear
|
||||
{_t('messageView.clear', { defaultMessage: "Clear" })}
|
||||
</InlineButton>
|
||||
{/if}
|
||||
<RowsFilterSwitcher
|
||||
icon="img debug"
|
||||
label="Debug"
|
||||
label={_t('messageView.debug', { defaultMessage: "Debug" })}
|
||||
{values}
|
||||
field="hideDebug"
|
||||
count={items.filter(x => x.severity == 'debug').length}
|
||||
/>
|
||||
<RowsFilterSwitcher
|
||||
icon="img info"
|
||||
label="Info"
|
||||
label={_t('messageView.info', { defaultMessage: "Info" })}
|
||||
{values}
|
||||
field="hideInfo"
|
||||
count={items.filter(x => x.severity == 'info').length}
|
||||
/>
|
||||
<RowsFilterSwitcher
|
||||
icon="img error"
|
||||
label="Error"
|
||||
label={_t('messageView.error', { defaultMessage: "Error" })}
|
||||
{values}
|
||||
field="hideError"
|
||||
count={items.filter(x => x.severity == 'error').length}
|
||||
/>
|
||||
<SearchInput placeholder="Filter log messages" bind:value={filter} />
|
||||
<SearchInput placeholder={_t('messageView.filterLogMessages', { defaultMessage: "Filter log messages" })} bind:value={filter} />
|
||||
</div>
|
||||
<div class="tablewrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="header">Number</td>
|
||||
<td class="header">Message</td>
|
||||
<td class="header">Time</td>
|
||||
<td class="header">Delta</td>
|
||||
<td class="header">Duration</td>
|
||||
<td class="header">{_t('messageView.number', { defaultMessage: 'Number' })}</td>
|
||||
<td class="header">{_t('messageView.message', { defaultMessage: 'Message' })}</td>
|
||||
<td class="header">{_t('messageView.time', { defaultMessage: 'Time' })}</td>
|
||||
<td class="header">{_t('messageView.delta', { defaultMessage: 'Delta' })}</td>
|
||||
<td class="header">{_t('messageView.duration', { defaultMessage: 'Duration' })}</td>
|
||||
{#if showProcedure}
|
||||
<td class="header">Procedure</td>
|
||||
<td class="header">{_t('messageView.procedure', { defaultMessage: 'Procedure' })}</td>
|
||||
{/if}
|
||||
{#if showLine}
|
||||
<td class="header">Line</td>
|
||||
<td class="header">{_t('messageView.line', { defaultMessage: 'Line' })}</td>
|
||||
{/if}
|
||||
{#if showCaller}
|
||||
<td class="header">Caller</td>
|
||||
<td class="header">{_t('messageView.caller', { defaultMessage: 'Caller' })}</td>
|
||||
{/if}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import AllResultsTab from './AllResultsTab.svelte';
|
||||
import JslChart from '../charts/JslChart.svelte';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import { __t, _t } from '../translations';
|
||||
|
||||
export let tabs = [];
|
||||
export let sessionId;
|
||||
@@ -68,7 +69,7 @@
|
||||
...(oneTab && resultInfos.length > 0
|
||||
? [
|
||||
{
|
||||
label: 'Results',
|
||||
label: _t('resultTabs.results', { defaultMessage: 'Results' }),
|
||||
isResult: true,
|
||||
component: AllResultsTab,
|
||||
props: {
|
||||
@@ -77,14 +78,14 @@
|
||||
},
|
||||
]
|
||||
: resultInfos.map((info, index) => ({
|
||||
label: `Result ${index + 1}`,
|
||||
label: _t('resultTabs.resultNumber', { defaultMessage: 'Result {number}', values: { number: index + 1 } }),
|
||||
isResult: true,
|
||||
component: JslDataGrid,
|
||||
resultIndex: info.resultIndex,
|
||||
props: { jslid: info.jslid, driver, onOpenChart: () => handleOpenChart(info.resultIndex) },
|
||||
}))),
|
||||
...charts.map((info, index) => ({
|
||||
label: `Chart ${info.resultIndex + 1}`,
|
||||
label: _t('resultTabs.chartNumber', { defaultMessage: 'Chart {number}', values: { number: info.resultIndex + 1 } }),
|
||||
isChart: true,
|
||||
resultIndex: info.resultIndex,
|
||||
component: JslChart,
|
||||
@@ -174,8 +175,8 @@
|
||||
tabs={allTabs}
|
||||
menu={resultInfos.length > 0 && [
|
||||
oneTab
|
||||
? { text: 'Every result in single tab', onClick: () => setOneTabValue(false) }
|
||||
: { text: 'All results in one tab', onClick: () => setOneTabValue(true) },
|
||||
? { text: _t('resultTabs.everyResultInSingleTab', { defaultMessage: 'Every result in single tab' }), onClick: () => setOneTabValue(false) }
|
||||
: { text: _t('resultTabs.allResultsInOneTab', { defaultMessage: 'All results in one tab' }), onClick: () => setOneTabValue(true) },
|
||||
]}
|
||||
onUserChange={value => {
|
||||
if (allTabs[value].isChart) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { downloadFromApi } from '../utility/exportFileTools';
|
||||
import useEffect from '../utility/useEffect';
|
||||
import Link from '../elements/Link.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let runnerId;
|
||||
export let executeNumber;
|
||||
@@ -40,28 +41,28 @@
|
||||
</script>
|
||||
|
||||
{#if !files || files.length == 0}
|
||||
<ErrorInfo message="No output files" icon="img alert" />
|
||||
<ErrorInfo message={_t('query.NoOutputFiles', { defaultMessage: 'No output files' })} icon="img alert" />
|
||||
{:else}
|
||||
<div class="flex1 scroll">
|
||||
<TableControl
|
||||
rows={files}
|
||||
stickyHeader
|
||||
columns={[
|
||||
{ fieldName: 'name', header: 'Name' },
|
||||
{ fieldName: 'size', header: 'Size', formatter: row => formatFileSize(row.size) },
|
||||
{ fieldName: 'name', header: _t('query.Name', { defaultMessage: 'Name' }) },
|
||||
{ fieldName: 'size', header: _t('query.Size', { defaultMessage: 'Size' }), formatter: row => formatFileSize(row.size) },
|
||||
!electron && {
|
||||
fieldName: 'download',
|
||||
header: 'Download',
|
||||
header: _t('query.Download', { defaultMessage: 'Download' }),
|
||||
slot: 0,
|
||||
},
|
||||
electron && {
|
||||
fieldName: 'copy',
|
||||
header: 'Copy',
|
||||
header: _t('query.Copy', { defaultMessage: 'Copy' }),
|
||||
slot: 1,
|
||||
},
|
||||
electron && {
|
||||
fieldName: 'show',
|
||||
header: 'Show',
|
||||
header: _t('query.Show', { defaultMessage: 'Show' }),
|
||||
slot: 2,
|
||||
},
|
||||
]}
|
||||
@@ -72,7 +73,7 @@
|
||||
downloadFromApi(`runners/data/${runnerId}/${row.name}`, row.name);
|
||||
}}
|
||||
>
|
||||
download
|
||||
{_t('query.download', { defaultMessage: 'download' })}
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -86,7 +87,7 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
save
|
||||
{_t('query.save', { defaultMessage: 'save' })}
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -96,7 +97,7 @@
|
||||
electron.showItemInFolder(row.path);
|
||||
}}
|
||||
>
|
||||
show
|
||||
{_t('query.show', { defaultMessage: 'show' })}
|
||||
</Link>
|
||||
</svelte:fragment>
|
||||
</TableControl>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import WidgetTitle from '../widgets/WidgetTitle.svelte';
|
||||
import RunnerOutputFiles from './RunnerOutputFiles.svelte';
|
||||
import SocketMessageView from './SocketMessageView.svelte';
|
||||
import { _t } from '../translations';
|
||||
|
||||
export let runnerId;
|
||||
export let executeNumber;
|
||||
@@ -10,7 +11,7 @@
|
||||
|
||||
<HorizontalSplitter>
|
||||
<div class="container" slot="1">
|
||||
<WidgetTitle>Messages</WidgetTitle>
|
||||
<WidgetTitle>{_t('query.Messages', { defaultMessage: 'Messages' })}</WidgetTitle>
|
||||
<SocketMessageView
|
||||
eventName={runnerId ? `runner-info-${runnerId}` : null}
|
||||
{executeNumber}
|
||||
@@ -19,7 +20,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="container" slot="2">
|
||||
<WidgetTitle>Output files</WidgetTitle>
|
||||
<WidgetTitle>{_t('query.OutputFiles', { defaultMessage: 'Output files' })}</WidgetTitle>
|
||||
<RunnerOutputFiles {runnerId} {executeNumber} />
|
||||
</div>
|
||||
</HorizontalSplitter>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||
import { apiOff, apiOn } from '../utility/api';
|
||||
import createRef from '../utility/createRef';
|
||||
import { _t } from '../translations';
|
||||
|
||||
import useEffect from '../utility/useEffect';
|
||||
|
||||
@@ -75,7 +76,7 @@
|
||||
</script>
|
||||
|
||||
{#if showNoMessagesAlert && (!displayedMessages || displayedMessages.length == 0)}
|
||||
<ErrorInfo message="No messages" icon="img alert" />
|
||||
<ErrorInfo message={_t('message.NoMessages', { defaultMessage: 'No messages' })} icon="img alert" />
|
||||
{:else}
|
||||
<MessageView
|
||||
items={displayedMessages}
|
||||
|
||||
@@ -1,58 +1,61 @@
|
||||
<script lang="ts">
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import { _t } from "../translations";
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import FormValues from "../forms/FormValues.svelte";
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import { _t } from '../translations';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import FormValues from '../forms/FormValues.svelte';
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<FormValues let:values>
|
||||
<FormValues let:values>
|
||||
<div class="heading">{_t('settings.behaviour', { defaultMessage: 'Behaviour' })}</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.useTabPreviewMode"
|
||||
label={_t('settings.behaviour.useTabPreviewMode', { defaultMessage: 'Use tab preview mode' })}
|
||||
defaultValue={true}
|
||||
name="behaviour.jsonPreviewWrap"
|
||||
label={_t('settings.behaviour.jsonPreviewWrap', { defaultMessage: 'Wrap JSON in preview' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.jsonPreviewWrap"
|
||||
label={_t('settings.behaviour.jsonPreviewWrap', { defaultMessage: 'Wrap JSON in preview' })}
|
||||
defaultValue={false}
|
||||
name="behaviour.openDetailOnArrows"
|
||||
label={_t('settings.behaviour.openDetailOnArrows', {
|
||||
defaultMessage: 'Open detail on keyboard navigation',
|
||||
})}
|
||||
defaultValue={true}
|
||||
disabled={values['behaviour.useTabPreviewMode'] === false}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.tabPreviewMode', { defaultMessage: 'Tab Preview Mode' })}</div>
|
||||
|
||||
<div class="tip">
|
||||
<FontIcon icon="img tip" />
|
||||
{_t('settings.behaviour.singleClickPreview', {
|
||||
<FontIcon icon="img tip" />
|
||||
{_t('settings.behaviour.singleClickPreview', {
|
||||
defaultMessage:
|
||||
'When you single-click or select a file in the "Tables, Views, Functions" view, it is shown in a preview mode and reuses an existing tab (preview tab). This is useful if you are quickly browsing tables and don\'t want every visited table to have its own tab. When you start editing the table or use double-click to open the table from the "Tables" view, a new tab is dedicated to that table.',
|
||||
})}
|
||||
'When you single-click or select a file in the "Tables, Views, Functions" view, it is shown in a preview mode and reuses an existing tab (preview tab). This is useful if you are quickly browsing tables and don\'t want every visited table to have its own tab. When you start editing the table or use double-click to open the table from the "Tables" view, a new tab is dedicated to that table.',
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.openDetailOnArrows"
|
||||
label={_t('settings.behaviour.openDetailOnArrows', {
|
||||
defaultMessage: 'Open detail on keyboard navigation',
|
||||
})}
|
||||
defaultValue={true}
|
||||
disabled={values['behaviour.useTabPreviewMode'] === false}
|
||||
name="behaviour.useTabPreviewMode"
|
||||
label={_t('settings.behaviour.useTabPreviewMode', { defaultMessage: 'Use tab preview mode' })}
|
||||
defaultValue={true}
|
||||
data-testid="BehaviourSettings_useTabPreviewMode"
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.confirmations', { defaultMessage: 'Confirmations' })}</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.tableDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.tableDataSave', {
|
||||
name="skipConfirm.tableDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.tableDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving table data (SQL)',
|
||||
})}
|
||||
})}
|
||||
/>
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.collectionDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.collectionDataSave', {
|
||||
name="skipConfirm.collectionDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.collectionDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving collection data (NoSQL)',
|
||||
})}
|
||||
})}
|
||||
/>
|
||||
</FormValues>
|
||||
</FormValues>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -67,4 +70,4 @@
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,81 +1,64 @@
|
||||
<script lang="ts">
|
||||
import CheckboxField from "../forms/CheckboxField.svelte";
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormFieldTemplateLarge from "../forms/FormFieldTemplateLarge.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import FormValues from "../forms/FormValues.svelte";
|
||||
import { lockedDatabaseMode } from "../stores";
|
||||
import { _t } from "../translations";
|
||||
|
||||
|
||||
import CheckboxField from '../forms/CheckboxField.svelte';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import FormValues from '../forms/FormValues.svelte';
|
||||
import { lockedDatabaseMode } from '../stores';
|
||||
import { _t } from '../translations';
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<FormValues let:values>
|
||||
<FormValues let:values>
|
||||
<div class="heading">{_t('settings.connection', { defaultMessage: 'Connection' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.connection.showOnlyTabsFromSelectedDatabase', {
|
||||
defaultMessage: 'Show only tabs from selected database',
|
||||
})}
|
||||
type="checkbox"
|
||||
labelProps={{
|
||||
onClick: () => {
|
||||
$lockedDatabaseMode = !$lockedDatabaseMode;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckboxField checked={$lockedDatabaseMode} on:change={e => ($lockedDatabaseMode = e.target.checked)} />
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<FormCheckboxField
|
||||
name="connection.autoRefresh"
|
||||
label={_t('settings.connection.autoRefresh', {
|
||||
name="connection.autoRefresh"
|
||||
label={_t('settings.connection.autoRefresh', {
|
||||
defaultMessage: 'Automatic refresh of database model on background',
|
||||
})}
|
||||
defaultValue={false}
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<FormTextField
|
||||
name="connection.autoRefreshInterval"
|
||||
label={_t('settings.connection.autoRefreshInterval', {
|
||||
name="connection.autoRefreshInterval"
|
||||
label={_t('settings.connection.autoRefreshInterval', {
|
||||
defaultMessage: 'Interval between automatic DB structure reloads in seconds',
|
||||
})}
|
||||
defaultValue="30"
|
||||
disabled={values['connection.autoRefresh'] === false}
|
||||
})}
|
||||
defaultValue="30"
|
||||
disabled={values['connection.autoRefresh'] === false}
|
||||
/>
|
||||
<FormSelectField
|
||||
label={_t('settings.connection.sshBindHost', { defaultMessage: 'Local host address for SSH connections' })}
|
||||
name="connection.sshBindHost"
|
||||
isNative
|
||||
defaultValue="127.0.0.1"
|
||||
options={[
|
||||
label={_t('settings.connection.sshBindHost', { defaultMessage: 'Local host address for SSH connections' })}
|
||||
name="connection.sshBindHost"
|
||||
isNative
|
||||
defaultValue="127.0.0.1"
|
||||
options={[
|
||||
{ value: '127.0.0.1', label: '127.0.0.1 (IPv4)' },
|
||||
{ value: '::1', label: '::1 (IPv6)' },
|
||||
{ value: 'localhost', label: 'localhost (domain name)' },
|
||||
]}
|
||||
]}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.session', { defaultMessage: 'Query sessions' })}</div>
|
||||
<FormCheckboxField
|
||||
name="session.autoClose"
|
||||
label={_t('settings.session.autoClose', {
|
||||
name="session.autoClose"
|
||||
label={_t('settings.session.autoClose', {
|
||||
defaultMessage: 'Automatic close query sessions after period without any activity',
|
||||
})}
|
||||
defaultValue={true}
|
||||
})}
|
||||
defaultValue={true}
|
||||
/>
|
||||
<FormTextField
|
||||
name="session.autoCloseTimeout"
|
||||
label={_t('settings.session.autoCloseTimeout', {
|
||||
name="session.autoCloseTimeout"
|
||||
label={_t('settings.session.autoCloseTimeout', {
|
||||
defaultMessage: 'Interval, after which query session without activity is closed (in minutes)',
|
||||
})}
|
||||
defaultValue="15"
|
||||
disabled={values['session.autoClose'] === false}
|
||||
})}
|
||||
defaultValue="15"
|
||||
disabled={values['session.autoClose'] === false}
|
||||
/>
|
||||
</FormValues>
|
||||
</FormValues>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
@@ -84,4 +67,11 @@
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
</style>
|
||||
.wrapper :global(input) {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.wrapper :global(select) {
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,93 +1,105 @@
|
||||
<script lang="ts">
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import { _t } from "../translations";
|
||||
import { isProApp } from "../utility/proTools";
|
||||
|
||||
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import { _t } from '../translations';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.dataGrid.title', { defaultMessage: 'Data grid' })}</div>
|
||||
<FormTextField
|
||||
name="dataGrid.pageSize"
|
||||
label={_t('settings.dataGrid.pageSize', {
|
||||
defaultMessage: 'Page size (number of rows for incremental loading, must be between 5 and 50000)',
|
||||
})}
|
||||
defaultValue="100"
|
||||
/>
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showHintColumns"
|
||||
label={_t('settings.dataGrid.showHintColumns', { defaultMessage: 'Show foreign key hints' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
{/if}
|
||||
<!-- <FormCheckboxField name="dataGrid.showHintColumns" label="Show foreign key hints" defaultValue={true} /> -->
|
||||
<div class="heading">{_t('settings.dataGrid.title', { defaultMessage: 'Data grid' })}</div>
|
||||
<FormTextField
|
||||
name="dataGrid.pageSize"
|
||||
label={_t('settings.dataGrid.pageSize', {
|
||||
defaultMessage: 'Page size (number of rows for incremental loading, must be between 5 and 50000)',
|
||||
})}
|
||||
defaultValue="100"
|
||||
/>
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showHintColumns"
|
||||
label={_t('settings.dataGrid.showHintColumns', { defaultMessage: 'Show foreign key hints' })}
|
||||
defaultValue={true}
|
||||
data-testid="DataGridSettings_showHintColumns"
|
||||
/>
|
||||
{/if}
|
||||
<!-- <FormCheckboxField name="dataGrid.showHintColumns" label="Show foreign key hints" defaultValue={true} /> -->
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.thousandsSeparator"
|
||||
label={_t('settings.dataGrid.thousandsSeparator', {
|
||||
defaultMessage: 'Use thousands separator for numbers',
|
||||
})}
|
||||
/>
|
||||
<FormSelectField
|
||||
label={_t('settings.dataGrid.thousandsSeparator', { defaultMessage: 'Thousands separator for numbers' })}
|
||||
name="dataGrid.thousandsSeparatorChar"
|
||||
isNative
|
||||
defaultValue="none"
|
||||
options={[
|
||||
{ value: 'none', label: _t('settings.dataGrid.thousandsSeparator.none', { defaultMessage: 'None' }) },
|
||||
{ value: 'system', label: _t('settings.dataGrid.thousandsSeparator.system', { defaultMessage: 'System' }) },
|
||||
{ value: 'space', label: _t('settings.dataGrid.thousandsSeparator.space', { defaultMessage: 'Space' }) },
|
||||
{
|
||||
value: 'narrowspace',
|
||||
label: _t('settings.dataGrid.thousandsSeparator.narrowSpace', {
|
||||
defaultMessage: 'Narrow space',
|
||||
}),
|
||||
},
|
||||
{ value: 'comma', label: _t('settings.dataGrid.thousandsSeparator.comma', { defaultMessage: 'Comma (,)' }) },
|
||||
{ value: 'dot', label: _t('settings.dataGrid.thousandsSeparator.dot', { defaultMessage: 'Dot (.)' }) },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.defaultAutoRefreshInterval"
|
||||
label={_t('settings.dataGrid.defaultAutoRefreshInterval', {
|
||||
defaultMessage: 'Default grid auto refresh interval in seconds',
|
||||
})}
|
||||
defaultValue="10"
|
||||
/>
|
||||
<FormTextField
|
||||
name="dataGrid.defaultAutoRefreshInterval"
|
||||
label={_t('settings.dataGrid.defaultAutoRefreshInterval', {
|
||||
defaultMessage: 'Default grid auto refresh interval in seconds',
|
||||
})}
|
||||
defaultValue="10"
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.alignNumbersRight"
|
||||
label={_t('settings.dataGrid.alignNumbersRight', { defaultMessage: 'Align numbers to right' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<FormCheckboxField
|
||||
name="dataGrid.alignNumbersRight"
|
||||
label={_t('settings.dataGrid.alignNumbersRight', { defaultMessage: 'Align numbers to right' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.collectionPageSize"
|
||||
label={_t('settings.dataGrid.collectionPageSize', {
|
||||
defaultMessage: 'Collection page size (for MongoDB JSON view, must be between 5 and 1000)',
|
||||
})}
|
||||
defaultValue="50"
|
||||
/>
|
||||
<FormTextField
|
||||
name="dataGrid.collectionPageSize"
|
||||
label={_t('settings.dataGrid.collectionPageSize', {
|
||||
defaultMessage: 'Collection page size (for MongoDB JSON view, must be between 5 and 1000)',
|
||||
})}
|
||||
defaultValue="50"
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.dataGrid.coloringMode', { defaultMessage: 'Row coloring mode' })}
|
||||
name="dataGrid.coloringMode"
|
||||
isNative
|
||||
defaultValue="36"
|
||||
options={[
|
||||
{
|
||||
value: '36',
|
||||
label: _t('settings.dataGrid.coloringMode.36', { defaultMessage: 'Every 3rd and 6th row' }),
|
||||
},
|
||||
{
|
||||
value: '2-primary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-primary', {
|
||||
defaultMessage: 'Every 2-nd row, primary color',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '2-secondary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-secondary', {
|
||||
defaultMessage: 'Every 2-nd row, secondary color',
|
||||
}),
|
||||
},
|
||||
{ value: 'none', label: _t('settings.dataGrid.coloringMode.none', { defaultMessage: 'None' }) },
|
||||
]}
|
||||
/>
|
||||
<FormSelectField
|
||||
label={_t('settings.dataGrid.coloringMode', { defaultMessage: 'Row coloring mode' })}
|
||||
name="dataGrid.coloringMode"
|
||||
isNative
|
||||
defaultValue="36"
|
||||
options={[
|
||||
{
|
||||
value: '36',
|
||||
label: _t('settings.dataGrid.coloringMode.36', { defaultMessage: 'Every 3rd and 6th row' }),
|
||||
},
|
||||
{
|
||||
value: '2-primary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-primary', {
|
||||
defaultMessage: 'Every 2-nd row, primary color',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '2-secondary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-secondary', {
|
||||
defaultMessage: 'Every 2-nd row, secondary color',
|
||||
}),
|
||||
},
|
||||
{ value: 'none', label: _t('settings.dataGrid.coloringMode.none', { defaultMessage: 'None' }) },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showAllColumnsWhenSearch"
|
||||
label={_t('settings.dataGrid.showAllColumnsWhenSearch', {
|
||||
defaultMessage: 'Show all columns when searching',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showAllColumnsWhenSearch"
|
||||
label={_t('settings.dataGrid.showAllColumnsWhenSearch', {
|
||||
defaultMessage: 'Show all columns when searching',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -97,4 +109,12 @@ defaultValue={false}
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
|
||||
.wrapper :global(select) {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.wrapper :global(input) {
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<div class="wrapper">
|
||||
<FormValues let:values>
|
||||
<div class="heading">{_t('settings.defaultActions', { defaultMessage: 'Default actions' })}</div>
|
||||
<div class="heading">{_t('settings.defaultActions', { defaultMessage: 'Default Actions' })}</div>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.defaultActions.connectionClick', { defaultMessage: 'Connection click' })}
|
||||
@@ -56,6 +56,7 @@
|
||||
name="defaultAction.useLastUsedAction"
|
||||
label={_t('settings.defaultActions.useLastUsedAction', { defaultMessage: 'Use last used action' })}
|
||||
defaultValue={true}
|
||||
data-testid="DefaultActionsSettings_useLastUsedAction"
|
||||
/>
|
||||
|
||||
<FormDefaultActionField
|
||||
@@ -100,4 +101,8 @@
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.wrapper :global(select){
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.externalTools', { defaultMessage: 'External tools' })}</div>
|
||||
<div class="heading">{_t('settings.externalTools', { defaultMessage: 'External Tools' })}</div>
|
||||
<FormTextField
|
||||
name="externalTools.mysqldump"
|
||||
label={_t('settings.other.externalTools.mysqldump', {
|
||||
|
||||
@@ -1,92 +1,138 @@
|
||||
<script lang="ts">
|
||||
import { internalRedirectTo } from '../clientAuth';
|
||||
import CheckboxField from '../forms/CheckboxField.svelte';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { EDITOR_KEYBINDINGS_MODES } from '../query/AceEditor.svelte';
|
||||
import { currentEditorKeybindigMode, currentEditorWrapEnabled } from '../stores';
|
||||
import { _t, getSelectedLanguage, setSelectedLanguage } from '../translations';
|
||||
import { isMac } from '../utility/common';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import CheckboxField from '../forms/CheckboxField.svelte';
|
||||
import { lockedDatabaseMode } from '../stores';
|
||||
|
||||
const electron = getElectron();
|
||||
let restartWarning = false;
|
||||
const electron = getElectron();
|
||||
let restartWarning = false;
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.general', { defaultMessage: 'General' })}</div>
|
||||
{#if electron}
|
||||
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Appearance' })}</div>
|
||||
<div class="heading">{_t('settings.application', { defaultMessage: 'Application' })}</div>
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.localization.language', { defaultMessage: 'Language' })}
|
||||
type="combo"
|
||||
labelIcon="mdi mdi-translate"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
data-testid="SettingsModal_languageSelect"
|
||||
options={[
|
||||
{ value: 'cs', label: 'Čeština' },
|
||||
{ value: 'de', label: 'Deutsch' },
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'es', label: 'Español' },
|
||||
{ value: 'fr', label: 'Français' },
|
||||
{ value: 'it', label: 'Italiano' },
|
||||
{ value: 'pt', label: 'Português (Brasil)' },
|
||||
{ value: 'sk', label: 'Slovenčina' },
|
||||
{ value: 'ja', label: '日本語' },
|
||||
{ value: 'zh', label: '中文' },
|
||||
]}
|
||||
defaultValue={getSelectedLanguage()}
|
||||
value={getSelectedLanguage()}
|
||||
on:change={e => {
|
||||
setSelectedLanguage(e.detail);
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('settings.localization.reloadWarning', {
|
||||
defaultMessage: 'Application will be reloaded to apply new language settings',
|
||||
}),
|
||||
onConfirm: () => {
|
||||
setTimeout(() => {
|
||||
internalRedirectTo(electron ? '/index.html' : '/');
|
||||
}, 100);
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
{#if electron}
|
||||
<FormSelectField
|
||||
label={_t('settings.other.autoUpdateApplication', { defaultMessage: 'Auto update application' })}
|
||||
name="app.autoUpdateMode"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{
|
||||
value: 'skip',
|
||||
label: _t('settings.other.autoUpdateApplication.skip', {
|
||||
defaultMessage: 'Do not check for new versions',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
label: _t('settings.other.autoUpdateApplication.check', { defaultMessage: 'Check for new versions' }),
|
||||
},
|
||||
{
|
||||
value: 'download',
|
||||
label: _t('settings.other.autoUpdateApplication.download', {
|
||||
defaultMessage: 'Check and download new versions',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.connection.showOnlyTabsFromSelectedDatabase', {
|
||||
defaultMessage: 'Show only tabs from selected database',
|
||||
})}
|
||||
type="checkbox"
|
||||
labelProps={{
|
||||
onClick: () => {
|
||||
$lockedDatabaseMode = !$lockedDatabaseMode;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckboxField
|
||||
checked={$lockedDatabaseMode}
|
||||
on:change={e => ($lockedDatabaseMode = e.target['checked'])}
|
||||
data-testid="GeneralSettings_lockedDatabaseMode"
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Appearance' })}</div>
|
||||
|
||||
{#if electron}
|
||||
<FormCheckboxField
|
||||
name="app.useNativeMenu"
|
||||
label={isMac()
|
||||
name="app.useNativeMenu"
|
||||
label={isMac()
|
||||
? _t('settings.useNativeWindowTitle', { defaultMessage: 'Use native window title' })
|
||||
: _t('settings.useSystemNativeMenu', { defaultMessage: 'Use system native menu' })}
|
||||
on:change={() => {
|
||||
on:change={() => {
|
||||
restartWarning = true;
|
||||
}}
|
||||
}}
|
||||
/>
|
||||
{#if restartWarning}
|
||||
<div class="ml-5 mb-3">
|
||||
<div class="ml-5 mb-3">
|
||||
<FontIcon icon="img warn" />
|
||||
{_t('settings.nativeMenuRestartWarning', {
|
||||
defaultMessage: 'Native menu settings will be applied after app restart',
|
||||
defaultMessage: 'Native menu settings will be applied after app restart',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<FormCheckboxField
|
||||
name="tabGroup.showServerName"
|
||||
label={_t('settings.tabGroup.showServerName', {
|
||||
defaultMessage: 'Show server name alongside database name in title of the tab group',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<div class="heading">{_t('settings.localization', { defaultMessage: 'Localization' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.localization.language', { defaultMessage: 'Language' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
data-testid="SettingsModal_languageSelect"
|
||||
options={[
|
||||
{ value: 'cs', label: 'Čeština' },
|
||||
{ value: 'de', label: 'Deutsch' },
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'es', label: 'Español' },
|
||||
{ value: 'fr', label: 'Français' },
|
||||
{ value: 'it', label: 'Italiano' },
|
||||
{ value: 'pt', label: 'Português (Brasil)' },
|
||||
{ value: 'sk', label: 'Slovenčina' },
|
||||
{ value: 'ja', label: '日本語' },
|
||||
{ value: 'zh', label: '中文' },
|
||||
]}
|
||||
defaultValue={getSelectedLanguage()}
|
||||
value={getSelectedLanguage()}
|
||||
on:change={e => {
|
||||
setSelectedLanguage(e.detail);
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('settings.localization.reloadWarning', {
|
||||
defaultMessage: 'Application will be reloaded to apply new language settings',
|
||||
}),
|
||||
onConfirm: () => {
|
||||
setTimeout(() => {
|
||||
internalRedirectTo(electron ? '/index.html' : '/');
|
||||
}, 100);
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
<FormCheckboxField
|
||||
name="tabGroup.showServerName"
|
||||
label={_t('settings.tabGroup.showServerName', {
|
||||
defaultMessage: 'Show server name alongside database name in title of the tab group',
|
||||
})}
|
||||
defaultValue={false}
|
||||
disabled={!hasPermission('settings/change')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -97,4 +143,7 @@ type="combo"
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
</style>
|
||||
.wrapper :global(select) {
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
<script lang="ts">
|
||||
import FormCheckboxField from "../forms/FormCheckboxField.svelte";
|
||||
import FormSelectField from "../forms/FormSelectField.svelte";
|
||||
import FormTextField from "../forms/FormTextField.svelte";
|
||||
import { _t } from "../translations";
|
||||
import { isProApp } from "../utility/proTools";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="heading">{_t('settings.other', { defaultMessage: 'Other' })}</div>
|
||||
|
||||
<FormTextField
|
||||
name="other.gistCreateToken"
|
||||
label={_t('settings.other.gistCreateToken', { defaultMessage: 'API token for creating error gists' })}
|
||||
defaultValue=""
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.other.autoUpdateApplication', { defaultMessage: 'Auto update application' })}
|
||||
name="app.autoUpdateMode"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{
|
||||
value: 'skip',
|
||||
label: _t('settings.other.autoUpdateApplication.skip', {
|
||||
defaultMessage: 'Do not check for new versions',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
label: _t('settings.other.autoUpdateApplication.check', { defaultMessage: 'Check for new versions' }),
|
||||
},
|
||||
{
|
||||
value: 'download',
|
||||
label: _t('settings.other.autoUpdateApplication.download', {
|
||||
defaultMessage: 'Check and download new versions',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="ai.allowSendModels"
|
||||
label={_t('settings.other.ai.allowSendModels', {
|
||||
defaultMessage: 'Allow to send DB models and query snippets to AI service',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
@@ -26,6 +26,7 @@
|
||||
{ value: 'upperCase', label: 'UPPER CASE' },
|
||||
{ value: 'lowerCase', label: 'lower case' },
|
||||
]}
|
||||
data-testid="SQLEditorSettings_sqlCommandsCase"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
@@ -97,4 +98,8 @@ defaultValue={false}
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.wrapper :global(input){
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,850 +0,0 @@
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||
import Link from '../elements/Link.svelte';
|
||||
import TabControl from '../elements/TabControl.svelte';
|
||||
import CheckboxField from '../forms/CheckboxField.svelte';
|
||||
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import FormValues from '../forms/FormValues.svelte';
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
import SettingsFormProvider from '../forms/SettingsFormProvider.svelte';
|
||||
import TextField from '../forms/TextField.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
|
||||
import ModalBase from '../modals/ModalBase.svelte';
|
||||
import { closeCurrentModal } from '../modals/modalTools';
|
||||
import { EDITOR_KEYBINDINGS_MODES, EDITOR_THEMES, FONT_SIZES } from '../query/AceEditor.svelte';
|
||||
import SqlEditor from '../query/SqlEditor.svelte';
|
||||
import {
|
||||
currentEditorFontSize,
|
||||
currentEditorWrapEnabled,
|
||||
currentEditorTheme,
|
||||
currentEditorKeybindigMode,
|
||||
extensions,
|
||||
selectedWidget,
|
||||
lockedDatabaseMode,
|
||||
visibleWidgetSideBar,
|
||||
currentTheme,
|
||||
getSystemTheme,
|
||||
} from '../stores';
|
||||
import { isMac } from '../utility/common';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import ThemeSkeleton from './ThemeSkeleton.svelte';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import FormTextAreaField from '../forms/FormTextAreaField.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { useSettings } from '../utility/metadataLoaders';
|
||||
import { derived } from 'svelte/store';
|
||||
import { safeFormatDate } from 'dbgate-tools';
|
||||
import FormDefaultActionField from './FormDefaultActionField.svelte';
|
||||
import AiSettingsTab from './AiSettingsTab.svelte';
|
||||
import { _t, setSelectedLanguage } from '../translations';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { internalRedirectTo } from '../clientAuth';
|
||||
import { getSelectedLanguage } from '../translations';
|
||||
|
||||
const electron = getElectron();
|
||||
let restartWarning = false;
|
||||
let licenseKeyCheckResult = null;
|
||||
|
||||
export let selectedTab = 'general';
|
||||
|
||||
const sqlPreview = `-- example query
|
||||
SELECT
|
||||
MAX(Album.AlbumId) AS max_album,
|
||||
MAX(Album.Title) AS max_title,
|
||||
Artist.ArtistId,
|
||||
'album' AS test_string,
|
||||
123 AS test_number
|
||||
FROM
|
||||
Album
|
||||
INNER JOIN Artist ON Album.ArtistId = Artist.ArtistId
|
||||
GROUP BY
|
||||
Artist.ArtistId
|
||||
ORDER BY
|
||||
Artist.Name ASC
|
||||
`;
|
||||
|
||||
function openThemePlugins() {
|
||||
closeCurrentModal();
|
||||
$selectedWidget = 'plugins';
|
||||
$visibleWidgetSideBar = true;
|
||||
}
|
||||
|
||||
const settings = useSettings();
|
||||
const settingsValues = derived(settings, $settings => {
|
||||
if (!$settings) {
|
||||
return {};
|
||||
}
|
||||
return $settings;
|
||||
});
|
||||
|
||||
$: licenseKey = $settingsValues['other.licenseKey'];
|
||||
let checkedLicenseKey = false;
|
||||
$: if (licenseKey && !checkedLicenseKey) {
|
||||
checkedLicenseKey = true;
|
||||
apiCall('config/check-license', { licenseKey }).then(result => {
|
||||
licenseKeyCheckResult = result;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<SettingsFormProvider>
|
||||
<ModalBase {...$$restProps} noPadding fixedHeight>
|
||||
<div slot="header">{_t('settings.title', { defaultMessage: 'Settings' })}</div>
|
||||
|
||||
<FormValues let:values>
|
||||
<TabControl
|
||||
bind:value={selectedTab}
|
||||
isInline
|
||||
inlineTabs
|
||||
scrollableContentContainer
|
||||
containerMaxWidth="100%"
|
||||
containerMaxHeight="calc(100% - 34px)"
|
||||
maxHeight100
|
||||
flex1
|
||||
tabs={[
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'general',
|
||||
label: _t('settings.general', { defaultMessage: 'General' }),
|
||||
slot: 1,
|
||||
},
|
||||
isProApp() &&
|
||||
electron && {
|
||||
identifier: 'license',
|
||||
label: _t('settings.license', { defaultMessage: 'License' }),
|
||||
slot: 7,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'connection',
|
||||
label: _t('settings.connection', { defaultMessage: 'Connection' }),
|
||||
slot: 2,
|
||||
},
|
||||
{ identifier: 'theme', label: _t('settings.theme', { defaultMessage: 'Themes' }), slot: 3 },
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'default-actions',
|
||||
label: _t('settings.defaultActions', { defaultMessage: 'Default Actions' }),
|
||||
slot: 4,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'behaviour',
|
||||
label: _t('settings.behaviour', { defaultMessage: 'Behaviour' }),
|
||||
slot: 5,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'external-tools',
|
||||
label: _t('settings.externalTools', { defaultMessage: 'External tools' }),
|
||||
slot: 8,
|
||||
},
|
||||
hasPermission('settings/change') && {
|
||||
identifier: 'other',
|
||||
label: _t('settings.other', { defaultMessage: 'Other' }),
|
||||
slot: 6,
|
||||
},
|
||||
isProApp() && hasPermission('settings/change') && { identifier: 'ai', label: 'AI', slot: 9 },
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="1">
|
||||
{#if electron}
|
||||
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Appearance' })}</div>
|
||||
<FormCheckboxField
|
||||
name="app.useNativeMenu"
|
||||
label={isMac()
|
||||
? _t('settings.useNativeWindowTitle', { defaultMessage: 'Use native window title' })
|
||||
: _t('settings.useSystemNativeMenu', { defaultMessage: 'Use system native menu' })}
|
||||
on:change={() => {
|
||||
restartWarning = true;
|
||||
}}
|
||||
/>
|
||||
{#if restartWarning}
|
||||
<div class="ml-5 mb-3">
|
||||
<FontIcon icon="img warn" />
|
||||
{_t('settings.nativeMenuRestartWarning', {
|
||||
defaultMessage: 'Native menu settings will be applied after app restart',
|
||||
})}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<FormCheckboxField
|
||||
name="tabGroup.showServerName"
|
||||
label={_t('settings.tabGroup.showServerName', {
|
||||
defaultMessage: 'Show server name alongside database name in title of the tab group',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<div class="heading">{_t('settings.localization', { defaultMessage: 'Localization' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.localization.language', { defaultMessage: 'Language' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
data-testid="SettingsModal_languageSelect"
|
||||
options={[
|
||||
{ value: 'cs', label: 'Čeština' },
|
||||
{ value: 'de', label: 'Deutsch' },
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'es', label: 'Español' },
|
||||
{ value: 'fr', label: 'Français' },
|
||||
{ value: 'it', label: 'Italiano' },
|
||||
{ value: 'pt', label: 'Português (Brasil)' },
|
||||
{ value: 'sk', label: 'Slovenčina' },
|
||||
{ value: 'ja', label: '日本語' },
|
||||
{ value: 'zh', label: '中文' },
|
||||
]}
|
||||
defaultValue={getSelectedLanguage()}
|
||||
value={getSelectedLanguage()}
|
||||
on:change={e => {
|
||||
setSelectedLanguage(e.detail);
|
||||
showModal(ConfirmModal, {
|
||||
message: _t('settings.localization.reloadWarning', {
|
||||
defaultMessage: 'Application will be reloaded to apply new language settings',
|
||||
}),
|
||||
onConfirm: () => {
|
||||
setTimeout(() => {
|
||||
internalRedirectTo(electron ? '/index.html' : '/');
|
||||
}, 100);
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<div class="heading">{_t('settings.dataGrid.title', { defaultMessage: 'Data grid' })}</div>
|
||||
<FormTextField
|
||||
name="dataGrid.pageSize"
|
||||
label={_t('settings.dataGrid.pageSize', {
|
||||
defaultMessage: 'Page size (number of rows for incremental loading, must be between 5 and 50000)',
|
||||
})}
|
||||
defaultValue="100"
|
||||
/>
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showHintColumns"
|
||||
label={_t('settings.dataGrid.showHintColumns', { defaultMessage: 'Show foreign key hints' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
{/if}
|
||||
<!-- <FormCheckboxField name="dataGrid.showHintColumns" label="Show foreign key hints" defaultValue={true} /> -->
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.thousandsSeparator"
|
||||
label={_t('settings.dataGrid.thousandsSeparator', {
|
||||
defaultMessage: 'Use thousands separator for numbers',
|
||||
})}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.defaultAutoRefreshInterval"
|
||||
label={_t('settings.dataGrid.defaultAutoRefreshInterval', {
|
||||
defaultMessage: 'Default grid auto refresh interval in seconds',
|
||||
})}
|
||||
defaultValue="10"
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.alignNumbersRight"
|
||||
label={_t('settings.dataGrid.alignNumbersRight', { defaultMessage: 'Align numbers to right' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
name="dataGrid.collectionPageSize"
|
||||
label={_t('settings.dataGrid.collectionPageSize', {
|
||||
defaultMessage: 'Collection page size (for MongoDB JSON view, must be between 5 and 1000)',
|
||||
})}
|
||||
defaultValue="50"
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.dataGrid.coloringMode', { defaultMessage: 'Row coloring mode' })}
|
||||
name="dataGrid.coloringMode"
|
||||
isNative
|
||||
defaultValue="36"
|
||||
options={[
|
||||
{
|
||||
value: '36',
|
||||
label: _t('settings.dataGrid.coloringMode.36', { defaultMessage: 'Every 3rd and 6th row' }),
|
||||
},
|
||||
{
|
||||
value: '2-primary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-primary', {
|
||||
defaultMessage: 'Every 2-nd row, primary color',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '2-secondary',
|
||||
label: _t('settings.dataGrid.coloringMode.2-secondary', {
|
||||
defaultMessage: 'Every 2-nd row, secondary color',
|
||||
}),
|
||||
},
|
||||
{ value: 'none', label: _t('settings.dataGrid.coloringMode.none', { defaultMessage: 'None' }) },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="dataGrid.showAllColumnsWhenSearch"
|
||||
label={_t('settings.dataGrid.showAllColumnsWhenSearch', {
|
||||
defaultMessage: 'Show all columns when searching',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.sqlEditor', { defaultMessage: 'SQL editor' })}</div>
|
||||
|
||||
<div class="flex">
|
||||
<div class="col-3">
|
||||
<FormSelectField
|
||||
label={_t('settings.sqlEditor.sqlCommandsCase', { defaultMessage: 'SQL commands case' })}
|
||||
name="sqlEditor.sqlCommandsCase"
|
||||
isNative
|
||||
defaultValue="upperCase"
|
||||
options={[
|
||||
{ value: 'upperCase', label: 'UPPER CASE' },
|
||||
{ value: 'lowerCase', label: 'lower case' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.editor.keybinds', { defaultMessage: 'Editor keybinds' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
defaultValue="default"
|
||||
options={EDITOR_KEYBINDINGS_MODES.map(mode => ({ label: mode.label, value: mode.value }))}
|
||||
value={$currentEditorKeybindigMode}
|
||||
on:change={e => ($currentEditorKeybindigMode = e.detail)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.editor.wordWrap', { defaultMessage: 'Enable word wrap' })}
|
||||
type="combo"
|
||||
>
|
||||
<CheckboxField
|
||||
checked={$currentEditorWrapEnabled}
|
||||
on:change={e => ($currentEditorWrapEnabled = e.target.checked)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FormTextField
|
||||
name="sqlEditor.limitRows"
|
||||
label={_t('settings.sqlEditor.limitRows', { defaultMessage: 'Return only N rows from query' })}
|
||||
placeholder={_t('settings.sqlEditor.limitRowsPlaceholder', { defaultMessage: '(No rows limit)' })}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.showTableAliasesInCodeCompletion"
|
||||
label={_t('settings.sqlEditor.showTableAliasesInCodeCompletion', {
|
||||
defaultMessage: 'Show table aliases in code completion',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.disableSplitByEmptyLine"
|
||||
label={_t('settings.sqlEditor.disableSplitByEmptyLine', { defaultMessage: 'Disable split by empty line' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.disableExecuteCurrentLine"
|
||||
label={_t('settings.sqlEditor.disableExecuteCurrentLine', {
|
||||
defaultMessage: 'Disable current line execution (Execute current)',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="sqlEditor.hideColumnsPanel"
|
||||
label={_t('settings.sqlEditor.hideColumnsPanel', { defaultMessage: 'Hide Columns/Filters panel by default' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="2">
|
||||
<div class="heading">{_t('settings.connection', { defaultMessage: 'Connection' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.connection.showOnlyTabsFromSelectedDatabase', {
|
||||
defaultMessage: 'Show only tabs from selected database',
|
||||
})}
|
||||
type="checkbox"
|
||||
labelProps={{
|
||||
onClick: () => {
|
||||
$lockedDatabaseMode = !$lockedDatabaseMode;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckboxField checked={$lockedDatabaseMode} on:change={e => ($lockedDatabaseMode = e.target.checked)} />
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<FormCheckboxField
|
||||
name="connection.autoRefresh"
|
||||
label={_t('settings.connection.autoRefresh', {
|
||||
defaultMessage: 'Automatic refresh of database model on background',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
<FormTextField
|
||||
name="connection.autoRefreshInterval"
|
||||
label={_t('settings.connection.autoRefreshInterval', {
|
||||
defaultMessage: 'Interval between automatic DB structure reloads in seconds',
|
||||
})}
|
||||
defaultValue="30"
|
||||
disabled={values['connection.autoRefresh'] === false}
|
||||
/>
|
||||
<FormSelectField
|
||||
label={_t('settings.connection.sshBindHost', { defaultMessage: 'Local host address for SSH connections' })}
|
||||
name="connection.sshBindHost"
|
||||
isNative
|
||||
defaultValue="127.0.0.1"
|
||||
options={[
|
||||
{ value: '127.0.0.1', label: '127.0.0.1 (IPv4)' },
|
||||
{ value: '::1', label: '::1 (IPv6)' },
|
||||
{ value: 'localhost', label: 'localhost (domain name)' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.session', { defaultMessage: 'Query sessions' })}</div>
|
||||
<FormCheckboxField
|
||||
name="session.autoClose"
|
||||
label={_t('settings.session.autoClose', {
|
||||
defaultMessage: 'Automatic close query sessions after period without any activity',
|
||||
})}
|
||||
defaultValue={true}
|
||||
/>
|
||||
<FormTextField
|
||||
name="session.autoCloseTimeout"
|
||||
label={_t('settings.session.autoCloseTimeout', {
|
||||
defaultMessage: 'Interval, after which query session without activity is closed (in minutes)',
|
||||
})}
|
||||
defaultValue="15"
|
||||
disabled={values['session.autoClose'] === false}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="3">
|
||||
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Application theme' })}</div>
|
||||
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.useSystemTheme', { defaultMessage: 'Use system theme' })}
|
||||
type="checkbox"
|
||||
labelProps={{
|
||||
onClick: () => {
|
||||
if ($currentTheme) {
|
||||
$currentTheme = null;
|
||||
} else {
|
||||
$currentTheme = getSystemTheme();
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CheckboxField
|
||||
checked={!$currentTheme}
|
||||
on:change={e => {
|
||||
if (e.target['checked']) {
|
||||
$currentTheme = null;
|
||||
} else {
|
||||
$currentTheme = getSystemTheme();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
|
||||
<div class="themes">
|
||||
{#each $extensions.themes as theme}
|
||||
<ThemeSkeleton {theme} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="m-5">
|
||||
{_t('settings.appearance.moreThemes', { defaultMessage: 'More themes are available as' })}
|
||||
<Link onClick={openThemePlugins}>plugins</Link>
|
||||
<br />
|
||||
{_t('settings.appearance.afterInstalling', {
|
||||
defaultMessage:
|
||||
'After installing theme plugin (try search "theme" in available extensions) new themes will be available here.',
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div class="heading">{_t('settings.appearance.editorTheme', { defaultMessage: 'Editor theme' })}</div>
|
||||
|
||||
<div class="flex">
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.editorTheme', { defaultMessage: 'Theme' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
notSelected={_t('settings.appearance.editorTheme.default', { defaultMessage: '(use theme default)' })}
|
||||
options={EDITOR_THEMES.map(theme => ({ label: theme, value: theme }))}
|
||||
value={$currentEditorTheme}
|
||||
on:change={e => ($currentEditorTheme = e.detail)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.fontSize', { defaultMessage: 'Font size' })}
|
||||
type="combo"
|
||||
>
|
||||
<SelectField
|
||||
isNative
|
||||
notSelected="(default)"
|
||||
options={FONT_SIZES}
|
||||
value={FONT_SIZES.find(x => x.value == $currentEditorFontSize) ? $currentEditorFontSize : 'custom'}
|
||||
on:change={e => ($currentEditorFontSize = e.detail)}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormFieldTemplateLarge
|
||||
label={_t('settings.appearance.customSize', { defaultMessage: 'Custom size' })}
|
||||
type="text"
|
||||
>
|
||||
<TextField
|
||||
value={$currentEditorFontSize == 'custom' ? '' : $currentEditorFontSize}
|
||||
on:change={e => ($currentEditorFontSize = e.target['value'])}
|
||||
disabled={!!FONT_SIZES.find(x => x.value == $currentEditorFontSize) &&
|
||||
$currentEditorFontSize != 'custom'}
|
||||
/>
|
||||
</FormFieldTemplateLarge>
|
||||
</div>
|
||||
|
||||
<div class="col-3">
|
||||
<FormTextField
|
||||
name="editor.fontFamily"
|
||||
label={_t('settings.appearance.fontFamily', { defaultMessage: 'Editor font family' })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor">
|
||||
<SqlEditor value={sqlPreview} readOnly />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="4">
|
||||
<div class="heading">{_t('settings.defaultActions', { defaultMessage: 'Default actions' })}</div>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.defaultActions.connectionClick', { defaultMessage: 'Connection click' })}
|
||||
name="defaultAction.connectionClick"
|
||||
isNative
|
||||
defaultValue="connect"
|
||||
options={[
|
||||
{
|
||||
value: 'openDetails',
|
||||
label: _t('settings.defaultActions.connectionClick.openDetails', {
|
||||
defaultMessage: 'Edit / open details',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: 'connect',
|
||||
label: _t('settings.defaultActions.connectionClick.connect', { defaultMessage: 'Connect' }),
|
||||
},
|
||||
{
|
||||
value: 'none',
|
||||
label: _t('settings.defaultActions.connectionClick.none', { defaultMessage: 'Do nothing' }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.defaultActions.databaseClick', { defaultMessage: 'Database click' })}
|
||||
name="defaultAction.databaseClick"
|
||||
isNative
|
||||
defaultValue="switch"
|
||||
options={[
|
||||
{
|
||||
value: 'switch',
|
||||
label: _t('settings.defaultActions.databaseClick.switch', { defaultMessage: 'Switch database' }),
|
||||
},
|
||||
{
|
||||
value: 'none',
|
||||
label: _t('settings.defaultActions.databaseClick.none', { defaultMessage: 'Do nothing' }),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="defaultAction.useLastUsedAction"
|
||||
label={_t('settings.defaultActions.useLastUsedAction', { defaultMessage: 'Use last used action' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.tableClick', { defaultMessage: 'Table click' })}
|
||||
objectTypeField="tables"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.viewClick', { defaultMessage: 'View click' })}
|
||||
objectTypeField="views"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.materializedViewClick', { defaultMessage: 'Materialized view click' })}
|
||||
objectTypeField="matviews"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.procedureClick', { defaultMessage: 'Procedure click' })}
|
||||
objectTypeField="procedures"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.functionClick', { defaultMessage: 'Function click' })}
|
||||
objectTypeField="functions"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
<FormDefaultActionField
|
||||
label={_t('settings.defaultActions.collectionClick', { defaultMessage: 'NoSQL collection click' })}
|
||||
objectTypeField="collections"
|
||||
disabled={values['defaultAction.useLastUsedAction'] !== false}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="5">
|
||||
<div class="heading">{_t('settings.behaviour', { defaultMessage: 'Behaviour' })}</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.useTabPreviewMode"
|
||||
label={_t('settings.behaviour.useTabPreviewMode', { defaultMessage: 'Use tab preview mode' })}
|
||||
defaultValue={true}
|
||||
/>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.jsonPreviewWrap"
|
||||
label={_t('settings.behaviour.jsonPreviewWrap', { defaultMessage: 'Wrap JSON in preview' })}
|
||||
defaultValue={false}
|
||||
/>
|
||||
|
||||
<div class="tip">
|
||||
<FontIcon icon="img tip" />
|
||||
{_t('settings.behaviour.singleClickPreview', {
|
||||
defaultMessage:
|
||||
'When you single-click or select a file in the "Tables, Views, Functions" view, it is shown in a preview mode and reuses an existing tab (preview tab). This is useful if you are quickly browsing tables and don\'t want every visited table to have its own tab. When you start editing the table or use double-click to open the table from the "Tables" view, a new tab is dedicated to that table.',
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="behaviour.openDetailOnArrows"
|
||||
label={_t('settings.behaviour.openDetailOnArrows', {
|
||||
defaultMessage: 'Open detail on keyboard navigation',
|
||||
})}
|
||||
defaultValue={true}
|
||||
disabled={values['behaviour.useTabPreviewMode'] === false}
|
||||
/>
|
||||
|
||||
<div class="heading">{_t('settings.confirmations', { defaultMessage: 'Confirmations' })}</div>
|
||||
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.tableDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.tableDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving table data (SQL)',
|
||||
})}
|
||||
/>
|
||||
<FormCheckboxField
|
||||
name="skipConfirm.collectionDataSave"
|
||||
label={_t('settings.confirmations.skipConfirm.collectionDataSave', {
|
||||
defaultMessage: 'Skip confirmation when saving collection data (NoSQL)',
|
||||
})}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="6">
|
||||
<div class="heading">{_t('settings.other', { defaultMessage: 'Other' })}</div>
|
||||
|
||||
<FormTextField
|
||||
name="other.gistCreateToken"
|
||||
label={_t('settings.other.gistCreateToken', { defaultMessage: 'API token for creating error gists' })}
|
||||
defaultValue=""
|
||||
/>
|
||||
|
||||
<FormSelectField
|
||||
label={_t('settings.other.autoUpdateApplication', { defaultMessage: 'Auto update application' })}
|
||||
name="app.autoUpdateMode"
|
||||
isNative
|
||||
defaultValue=""
|
||||
options={[
|
||||
{
|
||||
value: 'skip',
|
||||
label: _t('settings.other.autoUpdateApplication.skip', {
|
||||
defaultMessage: 'Do not check for new versions',
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
label: _t('settings.other.autoUpdateApplication.check', { defaultMessage: 'Check for new versions' }),
|
||||
},
|
||||
{
|
||||
value: 'download',
|
||||
label: _t('settings.other.autoUpdateApplication.download', {
|
||||
defaultMessage: 'Check and download new versions',
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{#if isProApp()}
|
||||
<FormCheckboxField
|
||||
name="ai.allowSendModels"
|
||||
label={_t('settings.other.ai.allowSendModels', {
|
||||
defaultMessage: 'Allow to send DB models and query snippets to AI service',
|
||||
})}
|
||||
defaultValue={false}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="7">
|
||||
<div class="heading">{_t('settings.other.license', { defaultMessage: 'License' })}</div>
|
||||
<FormTextAreaField
|
||||
name="other.licenseKey"
|
||||
label={_t('settings.other.licenseKey', { defaultMessage: 'License key' })}
|
||||
rows={7}
|
||||
onChange={async value => {
|
||||
licenseKeyCheckResult = await apiCall('config/check-license', { licenseKey: value });
|
||||
}}
|
||||
/>
|
||||
{#if licenseKeyCheckResult}
|
||||
<div class="m-3 ml-5">
|
||||
{#if licenseKeyCheckResult.status == 'ok'}
|
||||
<div>
|
||||
<FontIcon icon="img ok" />
|
||||
{_t('settings.other.licenseKey.valid', { defaultMessage: 'License key is valid' })}
|
||||
</div>
|
||||
{#if licenseKeyCheckResult.validTo}
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.validTo', { defaultMessage: 'License valid to:' })}
|
||||
{licenseKeyCheckResult.validTo}
|
||||
</div>
|
||||
{/if}
|
||||
{#if licenseKeyCheckResult.expiration}
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' })}
|
||||
<b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if licenseKeyCheckResult.status == 'error'}
|
||||
<div>
|
||||
<FontIcon icon="img error" />
|
||||
{licenseKeyCheckResult.errorMessage ??
|
||||
_t('settings.other.licenseKey.invalid', { defaultMessage: 'License key is invalid' })}
|
||||
{#if licenseKeyCheckResult.expiration}
|
||||
<div>
|
||||
{_t('settings.other.licenseKey.expiration', { defaultMessage: 'License key expiration:' })}
|
||||
<b>{safeFormatDate(licenseKeyCheckResult.expiration)}</b>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if licenseKeyCheckResult.isExpired}
|
||||
<div class="mt-2">
|
||||
<FormStyledButton
|
||||
value={_t('settings.other.licenseKey.checkForNew', {
|
||||
defaultMessage: 'Check for new license key',
|
||||
})}
|
||||
skipWidth
|
||||
on:click={async () => {
|
||||
licenseKeyCheckResult = await apiCall('config/get-new-license', { oldLicenseKey: licenseKey });
|
||||
if (licenseKeyCheckResult.licenseKey) {
|
||||
apiCall('config/update-settings', { 'other.licenseKey': licenseKeyCheckResult.licenseKey });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="8">
|
||||
<div class="heading">{_t('settings.externalTools', { defaultMessage: 'External tools' })}</div>
|
||||
<FormTextField
|
||||
name="externalTools.mysqldump"
|
||||
label={_t('settings.other.externalTools.mysqldump', {
|
||||
defaultMessage: 'mysqldump (backup MySQL database)',
|
||||
})}
|
||||
defaultValue="mysqldump"
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.mysql"
|
||||
label={_t('settings.other.externalTools.mysql', { defaultMessage: 'mysql (restore MySQL database)' })}
|
||||
defaultValue="mysql"
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.mysqlPlugins"
|
||||
label={_t('settings.other.externalTools.mysqlPlugins', {
|
||||
defaultMessage:
|
||||
'Folder with mysql plugins (for example for authentication). Set only in case of problems',
|
||||
})}
|
||||
defaultValue=""
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.pg_dump"
|
||||
label={_t('settings.other.externalTools.pg_dump', {
|
||||
defaultMessage: 'pg_dump (backup PostgreSQL database)',
|
||||
})}
|
||||
defaultValue="pg_dump"
|
||||
/>
|
||||
<FormTextField
|
||||
name="externalTools.psql"
|
||||
label={_t('settings.other.externalTools.psql', { defaultMessage: 'psql (restore PostgreSQL database)' })}
|
||||
defaultValue="psql"
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="9">
|
||||
<AiSettingsTab {values} />
|
||||
</svelte:fragment>
|
||||
</TabControl>
|
||||
</FormValues>
|
||||
|
||||
<div slot="footer">
|
||||
<!-- <FormSubmit value="OK" on:click={handleOk} /> -->
|
||||
<FormStyledButton value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
|
||||
</div>
|
||||
</ModalBase>
|
||||
</SettingsFormProvider>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
font-size: 20px;
|
||||
margin: 5px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.tip {
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
margin-top: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.themes {
|
||||
overflow-x: scroll;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.editor {
|
||||
position: relative;
|
||||
height: 200px;
|
||||
width: 400px;
|
||||
margin-left: var(--dim-large-form-margin);
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user