Compare commits

..

68 Commits

Author SHA1 Message Date
Stela Augustinova a02a3230f1 Added isTypeNumber check for right alignment in DataGridCell 2025-12-03 10:34:09 +01:00
SPRINX0\prochazka d12ad7b882 SYNC: sorted translation keys 2025-12-02 13:42:48 +00:00
SPRINX0\prochazka f8e9f07a00 SYNC: translated files 2025-12-02 13:42:46 +00:00
Jan Prochazka 4ac3891e07 Merge pull request #1281 from dbgate/feature/translation4
Feature/translation4
2025-12-02 14:11:51 +01:00
SPRINX0\prochazka a2d55d3fdd SYNC: removed button 2025-12-02 13:09:32 +00:00
Stela Augustinova d015a24300 Merge branch 'master' into feature/translation4 2025-12-02 14:03:59 +01:00
Stela Augustinova be38acbede Update translation tag for columns count 2025-12-02 14:01:04 +01:00
Stela Augustinova 34d0cb4dc7 Refactor translation tags in ResultTabs component to use _t function 2025-12-02 13:58:31 +01:00
SPRINX0\prochazka 18d0558b19 SYNC: fixed language test 2025-12-02 12:47:33 +00:00
SPRINX0\prochazka d4469f3a2d SYNC: fixed import CSV test 2025-12-02 12:08:16 +00:00
SPRINX0\prochazka 43b760b4bf fix 2025-12-02 12:34:57 +01:00
SPRINX0\prochazka 3d47932c09 v6.7.2-premium-beta.4 2025-12-02 12:26:52 +01:00
SPRINX0\prochazka 7196d6e1bf SYNC: fixed plugin tyab test, moived to add-connection tests 2025-12-02 11:16:05 +00:00
SPRINX0\prochazka ec06a7d861 SYNC: restore table fixes & backup table tests 2025-12-02 10:08:38 +00:00
Jan Prochazka ef23f0d18e Merge pull request #1280 from dbgate/feature/settings-tab-update2
Feature/settings tab update2
2025-12-02 10:28:21 +01:00
Stela Augustinova e9abc5f07f Changed translation tag 2025-12-02 10:27:21 +01:00
Stela Augustinova 6f9d6ff849 Changed initial width of settings menu items 2025-12-02 10:21:39 +01:00
Stela Augustinova eab18d3c11 Update general settings layout 2025-12-02 10:16:16 +01:00
SPRINX0\prochazka 3a509a6a97 SYNC: backup test WIP 2025-12-01 16:09:20 +00:00
SPRINX0\prochazka 615c6f4e24 SYNC: popup menu fix 2025-12-01 15:43:24 +00:00
CI workflows 96660b4539 chore: auto-update github workflows 2025-12-01 15:37:27 +00:00
CI workflows 1be284974b Update pro ref 2025-12-01 15:37:11 +00:00
Jan Prochazka 790b6478dc Merge pull request #1279 from dbgate/feature/settings-tab-update
Feature/settings tab update
2025-12-01 16:28:21 +01:00
Stela Augustinova 106344b33e Deleted Settings Modal 2025-12-01 16:26:20 +01:00
Stela Augustinova 85a2a4b873 Revert "Button for svg export"
This reverts commit 352b6cbe04.
2025-12-01 16:22:35 +01:00
Stela Augustinova 4ab694de0c Added top border for first menu item 2025-12-01 16:14:23 +01:00
Stela Augustinova 5e193c1725 Removed Other settings component 2025-12-01 16:10:30 +01:00
Stela Augustinova 2b055c028c Update theme command to open settings tab with selected item 2025-12-01 16:00:05 +01:00
SPRINX0\prochazka b341749e45 v6.7.2-premium-beta.3 2025-12-01 15:48:06 +01:00
Stela Augustinova 6ae381b1fd Merge branch 'master' of https://github.com/dbgate/dbgate 2025-12-01 15:45:42 +01:00
Stela Augustinova 352b6cbe04 Button for svg export 2025-12-01 15:45:16 +01:00
CI workflows e5e6d2701e chore: auto-update github workflows 2025-12-01 14:28:22 +00:00
CI workflows 9ad1924488 Update pro ref 2025-12-01 14:28:07 +00:00
SPRINX0\prochazka 2aadbfc64a v6.7.2-premium-beta.2 2025-12-01 14:51:22 +01:00
Jan Prochazka 1c5d652f93 SYNC: fixed test 2025-12-01 13:34:34 +00:00
SPRINX0\prochazka b2355a3b2d Merge branch 'feature/restore-script' 2025-12-01 14:27:29 +01:00
Stela Augustinova 4ee6d089d5 Added translation tags for objects, widgets, forms 2025-12-01 12:54:42 +01:00
SPRINX0\prochazka 6bd81cbff5 restore table - comments 2025-12-01 12:17:00 +01:00
SPRINX0\prochazka b912190c5e delete part of restore script 2025-12-01 12:07:44 +01:00
Jan Prochazka 43a826e2e5 Merge pull request #1277 from dbgate/feature/handle-full-refresh
Update refresh button to handle full database refresh
2025-12-01 11:07:18 +01:00
SPRINX0\prochazka 8ae64a9dcf restore script - update 2025-12-01 11:06:18 +01:00
Stela Augustinova 4ce9faf39b Update refresh button to handle full database refresh 2025-12-01 10:42:49 +01:00
Stela Augustinova d94a67d0af Added translation tags for modals 2025-12-01 10:25:43 +01:00
SPRINX0\prochazka d650d91d82 table restore script WIP 2025-12-01 10:08:00 +01:00
Stela Augustinova e14f59256d Added translation tags for settings, tabs, modals 2025-11-30 19:38:01 +01:00
SPRINX0\prochazka d3322a4a15 Merge branch 'feature/table-backups' 2025-11-28 16:10:18 +01:00
SPRINX0\prochazka a65842e31f table backups 2025-11-28 16:07:36 +01:00
SPRINX0\prochazka 74fde66b51 lang texts 2025-11-28 15:37:34 +01:00
SPRINX0\prochazka c3ea155a7b refresh database options 2025-11-28 15:35:38 +01:00
Jan Prochazka c4b81e3d2c Merge pull request #1275 from dbgate/feature/settings-tab
Feature/settings tab
2025-11-28 13:59:41 +01:00
SPRINX0\prochazka 5e54aa553a skip incremental analysis test for postgres 2025-11-28 12:47:59 +01:00
SPRINX0\prochazka 6913970830 postgresql primary key loading optimalization 2025-11-28 12:32:18 +01:00
Jan Prochazka 014e453e57 removed ttable incremental analysis for postgres 2025-11-28 12:13:32 +01:00
Jan Prochazka 25b5341f76 fix tests 2025-11-28 11:54:24 +01:00
SPRINX0\prochazka 1df51f9609 skip incremental analysis check 2025-11-28 11:01:22 +01:00
Jan Prochazka 65d13189b3 postgres analyser fixed - broken loading FKs in incremental analysis 2025-11-28 10:24:53 +01:00
Jan Prochazka 0913011120 test fix 2025-11-28 08:21:04 +01:00
Jan Prochazka 697d755744 test fix 2025-11-27 16:25:24 +01:00
Jan Prochazka e3f23ddc79 fixed test 2025-11-27 15:44:31 +01:00
Jan Prochazka 094acc40e8 jest reporters 2025-11-27 15:13:28 +01:00
Jan Prochazka ebd4991de8 Revert "jest reporters"
This reverts commit d04a8fad4c.
2025-11-27 15:12:15 +01:00
Jan Prochazka d04a8fad4c jest reporters 2025-11-27 14:55:40 +01:00
CI workflows cb14bffc5a chore: auto-update github workflows 2025-11-27 13:48:04 +00:00
Jan Prochazka a4c4d17381 test reporters 2025-11-27 14:47:43 +01:00
Jan Prochazka abf0fc7942 incremental analysis in alter table tests 2025-11-27 14:06:48 +01:00
SPRINX0\prochazka c2703edfde prettier 2025-11-27 11:03:12 +01:00
SPRINX0\prochazka d14b90ab20 foreign key editor UX 2025-11-27 10:29:33 +01:00
SPRINX0\prochazka 48f4924932 prettier format 2025-11-27 09:29:39 +01:00
124 changed files with 4744 additions and 1781 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
ref: ca69c4857d7d93c4b066018e8a9a0a0ece2300e7
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
ref: ca69c4857d7d93c4b066018e8a9a0a0ece2300e7
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -39,7 +39,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
ref: ca69c4857d7d93c4b066018e8a9a0a0ece2300e7
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
ref: ca69c4857d7d93c4b066018e8a9a0a0ece2300e7
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
ref: ca69c4857d7d93c4b066018e8a9a0a0ece2300e7
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 3b9ca48888d17d96806820c4e54bb047c18d6278
ref: ca69c4857d7d93c4b066018e8a9a0a0ece2300e7
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
-18
View File
@@ -42,24 +42,6 @@ jobs:
run: |
cd packages/tools
yarn test:ci
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: integration-tests/result.json
action-name: Integration tests
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/filterparser/result.json
action-name: Filter parser test results
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-file: packages/datalib/result.json
action-name: Datalib (perspectives) test results
services:
postgres-integr:
image: postgres
@@ -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();
-11
View File
@@ -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
+5 -11
View File
@@ -163,41 +163,35 @@ 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');
+63 -5
View File
@@ -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,14 @@ 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.contains('Refresh DB structure (incremental)').click();
cy.testid('SqlObjectList_container').contains('customers-20').click();
cy.contains('Rows: 20').should('be.visible');
@@ -12,6 +12,7 @@ const {
} = require('dbgate-tools');
function pickImportantTableInfo(engine, table) {
if (!table) return table;
const props = ['columnName', 'defaultValue'];
if (!engine.skipNullability) props.push('notNull');
if (!engine.skipAutoIncrement) props.push('autoIncrement');
@@ -25,6 +26,13 @@ function pickImportantTableInfo(engine, table) {
.map(props =>
_.omitBy(props, (v, k) => k == 'defaultValue' && v == 'NULL' && engine.setNullDefaultInsteadOfDrop)
),
// foreignKeys: table.foreignKeys
// .sort((a, b) => a.refTableName.localeCompare(b.refTableName))
// .map(fk => ({
// constraintType: fk.constraintType,
// refTableName: fk.refTableName,
// columns: fk.columns.map(col => ({ columnName: col.columnName, refColumnName: col.refColumnName })),
// })),
};
}
@@ -33,7 +41,7 @@ function checkTableStructure(engine, t1, t2) {
expect(pickImportantTableInfo(engine, t1)).toEqual(pickImportantTableInfo(engine, t2));
}
async function testTableDiff(engine, conn, driver, mangle) {
async function testTableDiff(engine, conn, driver, mangle, changedTable = 't1') {
const initQuery = formatQueryWithoutParams(driver, `create table ~t0 (~id int not null primary key)`);
await driver.query(conn, transformSqlForEngine(engine, initQuery));
@@ -68,17 +76,38 @@ async function testTableDiff(engine, conn, driver, mangle) {
await driver.query(conn, transformSqlForEngine(engine, query));
}
const tget = x => x.tables.find(y => y.pureName == 't1');
const structure1 = generateDbPairingId(extendDatabaseInfo(await driver.analyseFull(conn)));
if (!engine.skipReferences) {
const query = formatQueryWithoutParams(
driver,
`create table ~t3 (~id int not null primary key, ~fkval int ${
driver.dialect.implicitNullDeclaration ? '' : 'null'
})`
);
await driver.query(conn, transformSqlForEngine(engine, query));
}
const tget = x => x?.tables?.find(y => y.pureName == changedTable);
const structure1Source = await driver.analyseFull(conn);
const structure1 = generateDbPairingId(extendDatabaseInfo(structure1Source));
let structure2 = _.cloneDeep(structure1);
mangle(tget(structure2));
structure2 = extendDatabaseInfo(structure2);
const { sql } = getAlterTableScript(tget(structure1), tget(structure2), {}, structure1, structure2, driver);
// sleep 1s - some engines have update datetime precision only to seconds
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('RUNNING ALTER SQL', driver.engine, ':', sql);
await driver.script(conn, sql);
// if (!engine.skipIncrementalAnalysis) {
// const structure2RealIncremental = await driver.analyseIncremental(conn, structure1Source);
// checkTableStructure(engine, tget(structure2RealIncremental), tget(structure2));
// }
const structure2Real = extendDatabaseInfo(await driver.analyseFull(conn));
checkTableStructure(engine, tget(structure2Real), tget(structure2));
@@ -214,6 +243,48 @@ describe('Alter table', () => {
})
);
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
'Drop FK - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(
engine,
conn,
driver,
tbl => {
tbl.foreignKeys = [];
},
't2'
);
})
);
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
'Create FK - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(
engine,
conn,
driver,
tbl => {
tbl.foreignKeys = [
{
constraintType: 'foreignKey',
pureName: 't3',
refTableName: 't1',
columns: [
{
columnName: 'fkval',
refColumnName: 'col_ref',
},
],
},
];
},
't3'
);
})
);
// test.each(engines.map(engine => [engine.label, engine]))(
// 'Change autoincrement - %s',
// testWrapper(async (conn, driver, engine) => {
@@ -28,12 +28,14 @@ describe('Schema tests', () => {
const count = schemas1.length;
expect(structure1.tables.length).toEqual(2);
await runCommandOnDriver(conn, driver, dmp => dmp.createSchema('myschema'));
const structure2 = await driver.analyseIncremental(conn, structure1);
const schemas2 = await driver.listSchemas(conn);
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) {
const structure2 = await driver.analyseIncremental(conn, structure1);
const schemas2 = await driver.listSchemas(conn);
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();
}
})
);
@@ -48,10 +50,12 @@ describe('Schema tests', () => {
expect(schemas1.find(x => x.schemaName == 'myschema')).toBeTruthy();
expect(structure1.tables.length).toEqual(2);
await runCommandOnDriver(conn, driver, dmp => dmp.dropSchema('myschema'));
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) {
const structure2 = await driver.analyseIncremental(conn, structure1);
const schemas2 = await driver.listSchemas(conn);
expect(schemas2.find(x => x.schemaName == 'myschema')).toBeFalsy();
expect(structure2).toBeNull();
}
})
);
+4 -3
View File
@@ -187,6 +187,7 @@ const mariaDbEngine = {
/** @type {import('dbgate-types').TestEngineInfo} */
const postgreSqlEngine = {
label: 'PostgreSQL',
skipIncrementalAnalysis: true,
connection: {
engine: 'postgres@dbgate-plugin-postgres',
password: 'Pwd2020Db',
@@ -757,11 +758,11 @@ const enginesOnCi = [
const enginesOnLocal = [
// all engines, which would be run on local test
// cassandraEngine,
//mysqlEngine,
// mysqlEngine,
// mariaDbEngine,
//postgreSqlEngine,
postgreSqlEngine,
//sqlServerEngine,
sqliteEngine,
// sqliteEngine,
// cockroachDbEngine,
// clickhouseEngine,
// libsqlFileEngine,
+1
View File
@@ -1,3 +1,4 @@
module.exports = {
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
reporters: ['default', 'github-actions'],
};
+1 -1
View File
@@ -18,7 +18,7 @@
},
"devDependencies": {
"cross-env": "^7.0.3",
"jest": "^27.0.1",
"jest": "^28.1.3",
"pino-pretty": "^11.2.2",
"tmp": "^0.2.3"
}
+2 -1
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "6.7.2-alpha.1",
"version": "6.7.2-premium-beta.4",
"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": {
+1
View File
@@ -2,4 +2,5 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
reporters: ['default', 'github-actions'],
};
+1
View File
@@ -2,4 +2,5 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
reporters: ['default', 'github-actions'],
};
+5 -2
View File
@@ -1,7 +1,7 @@
import type { SqlDumper } from 'dbgate-types';
import { Command, Select, Update, Delete, Insert } from './types';
import { dumpSqlExpression } from './dumpSqlExpression';
import { dumpSqlFromDefinition, dumpSqlSourceRef } from './dumpSqlSource';
import { dumpSqlFromDefinition, dumpSqlSourceDef, dumpSqlSourceRef } from './dumpSqlSource';
import { dumpSqlCondition } from './dumpSqlCondition';
export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
@@ -115,7 +115,10 @@ export function dumpSqlInsert(dmp: SqlDumper, cmd: Insert) {
cmd.fields.map(x => x.targetColumn)
);
dmp.putCollection(',', cmd.fields, x => dumpSqlExpression(dmp, x));
if (dmp.dialect.requireFromDual) {
if (cmd.whereNotExistsSource) {
dmp.put(' ^from ');
dumpSqlSourceDef(dmp, cmd.whereNotExistsSource);
} else if (dmp.dialect.requireFromDual) {
dmp.put(' ^from ^dual ');
}
dmp.put(' ^where ^not ^exists (^select * ^from %f ^where ', cmd.targetTable);
@@ -2,6 +2,7 @@ import _ from 'lodash';
import type { SqlDumper } from 'dbgate-types';
import { Expression, ColumnRefExpression } from './types';
import { dumpSqlSourceRef } from './dumpSqlSource';
import { dumpSqlSelect } from './dumpSqlCommand';
export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) {
switch (expr.exprType) {
@@ -67,5 +68,11 @@ export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) {
});
dmp.put(')');
break;
case 'select':
dmp.put('(');
dumpSqlSelect(dmp, expr.select);
dmp.put(')');
break;
}
}
+8 -1
View File
@@ -44,6 +44,7 @@ export interface Insert {
fields: UpdateField[];
targetTable: NamedObjectInfo;
insertWhereNotExistsCondition?: Condition;
whereNotExistsSource?: Source;
}
export interface AllowIdentityInsert {
@@ -226,6 +227,11 @@ export interface RowNumberExpression {
orderBy: OrderByExpression[];
}
export interface SelectExpression {
exprType: 'select';
select: Select;
}
export type Expression =
| ColumnRefExpression
| ValueExpression
@@ -235,7 +241,8 @@ export type Expression =
| CallExpression
| MethodCallExpression
| TranformExpression
| RowNumberExpression;
| RowNumberExpression
| SelectExpression;
export type OrderByExpression = Expression & { direction: 'ASC' | 'DESC' };
export type ResultField = Expression & { alias?: string };
+1
View File
@@ -2,4 +2,5 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
reporters: ['default', 'github-actions'],
};
+1
View File
@@ -16,6 +16,7 @@ export interface SqlDumper extends AlterProcessor {
transform(type: TransformType, dumpExpr: () => void);
createDatabase(name: string);
dropDatabase(name: string);
comment(value: string);
callableTemplate(func: CallableObjectInfo);
@@ -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', {
@@ -1,6 +1,7 @@
<script lang="ts" context="module">
import { copyTextToClipboard } from '../utility/clipboard';
import { _t, _tval, DefferedTranslationResult } from '../translations';
import sqlFormatter from 'sql-formatter';
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
export const createMatcher =
@@ -88,7 +89,8 @@
isRename?: boolean;
isTruncate?: boolean;
isCopyTableName?: boolean;
isDuplicateTable?: boolean;
isTableBackup?: boolean;
isTableRestore?: boolean;
isDiagram?: boolean;
functionName?: string;
isExport?: boolean;
@@ -106,6 +108,8 @@
}
function createMenusCore(objectTypeField, driver, data): DbObjMenuItem[] {
const backupMatch = data.objectTypeField === 'tables' ? data.pureName.match(TABLE_BACKUP_REGEX) : null;
switch (objectTypeField) {
case 'tables':
return [
@@ -175,11 +179,18 @@
isCopyTableName: true,
requiresWriteAccess: false,
},
hasPermission('dbops/table/backup') && {
label: _t('dbObject.createTableBackup', { defaultMessage: 'Create table backup' }),
isDuplicateTable: true,
requiresWriteAccess: true,
},
hasPermission('dbops/table/backup') &&
!backupMatch && {
label: _t('dbObject.createTableBackup', { defaultMessage: 'Create table backup' }),
isTableBackup: true,
requiresWriteAccess: true,
},
hasPermission('dbops/table/restore') &&
backupMatch && {
label: _t('dbObject.createRestoreScript', { defaultMessage: 'Create restore script' }),
isTableRestore: true,
requiresWriteAccess: true,
},
hasPermission('dbops/model/view') && {
label: _t('dbObject.showDiagram', { defaultMessage: 'Show diagram' }),
isDiagram: true,
@@ -188,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,
},
@@ -249,7 +260,7 @@
divider: true,
},
{
label: 'Export',
label: _t('common.export', { defaultMessage: 'Export' }),
isExport: true,
functionName: 'tableReader',
},
@@ -299,7 +310,7 @@
divider: true,
},
{
label: 'Export',
label: _t('common.export', { defaultMessage: 'Export' }),
isExport: true,
functionName: 'tableReader',
},
@@ -391,7 +402,7 @@
icon: 'img perspective',
},
hasPermission('dbops/export') && {
label: 'Export',
label: _t('common.export', { defaultMessage: 'Export' }),
isExport: true,
functionName: 'tableReader',
},
@@ -637,7 +648,7 @@
});
},
});
} else if (menu.isDuplicateTable) {
} else if (menu.isTableBackup) {
const driver = await getDriver();
const dmp = driver.createDumper();
const newTable = _.cloneDeep(data);
@@ -671,6 +682,25 @@
},
engine: driver.engine,
});
} else if (menu.isTableRestore) {
const backupMatch = data.objectTypeField === 'tables' ? data.pureName.match(TABLE_BACKUP_REGEX) : null;
const driver = await getDriver();
const dmp = driver.createDumper();
const db = await getDatabaseInfo(data);
if (db) {
const originalTable = db?.tables?.find(x => x.pureName == backupMatch[1] && x.schemaName == data.schemaName);
if (originalTable) {
createTableRestoreScript(data, originalTable, dmp);
newQuery({
title: _t('dbObject.restoreScript', {
defaultMessage: 'Restore {name} #',
values: { name: backupMatch[1] },
}),
initialData: sqlFormatter.format(dmp.s),
});
}
}
} else if (menu.isImport) {
const { conid, database } = data;
openImportExportTab({
@@ -724,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 {
@@ -743,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;
}
@@ -1008,6 +1037,8 @@
return handleDatabaseObjectClick(data, { forceNewTab, tabPreviewMode, focusTab });
}
export const TABLE_BACKUP_REGEX = /^_(.*)_(\d\d\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)-(\d\d)$/;
</script>
<script lang="ts">
@@ -1025,7 +1056,7 @@
} from '../stores';
import openNewTab from '../utility/openNewTab';
import { extractDbNameFromComposite, filterNameCompoud, getConnectionLabel } from 'dbgate-tools';
import { getConnectionInfo } from '../utility/metadataLoaders';
import { getConnectionInfo, getDatabaseInfo } from '../utility/metadataLoaders';
import fullDisplayName from '../utility/fullDisplayName';
import { showModal } from '../modals/modalTools';
import { findEngineDriver } from 'dbgate-tools';
@@ -1047,6 +1078,8 @@
import { getBoolSettingsValue, getOpenDetailOnArrowsSettings } from '../settings/settingsTools';
import { isProApp } from '../utility/proTools';
import formatFileSize from '../utility/formatFileSize';
import { createTableRestoreScript } from '../utility/tableRestoreScript';
import newQuery from '../query/newQuery';
export let data;
export let passProps;
@@ -1086,14 +1119,21 @@
}
$: isPinned = !!$pinnedTables.find(x => testEqual(data, x));
$: backupParsed = data.objectTypeField === 'tables' ? data.pureName.match(TABLE_BACKUP_REGEX) : null;
$: backupTitle =
backupParsed != null
? `${backupParsed[1]} (${backupParsed[2]}-${backupParsed[3]}-${backupParsed[4]} ${backupParsed[5]}:${backupParsed[6]}:${backupParsed[7]})`
: null;
</script>
<AppObjectCore
{...$$restProps}
module={$$props.module}
{data}
title={data.schemaName && !passProps?.hideSchemaName ? `${data.schemaName}.${data.pureName}` : data.pureName}
icon={databaseObjectIcons[data.objectTypeField]}
title={backupTitle ??
(data.schemaName && !passProps?.hideSchemaName ? `${data.schemaName}.${data.pureName}` : data.pureName)}
icon={backupParsed ? 'img table-backup' : databaseObjectIcons[data.objectTypeField]}
menu={createMenu}
showPinnedInsteadOfUnpin={passProps?.showPinnedInsteadOfUnpin}
onPin={passProps?.ingorePin ? null : isPinned ? null : () => pinnedTables.update(list => [...list, data])}
@@ -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">
@@ -3,7 +3,7 @@ import { currentDatabase, getCurrentDatabase } from '../stores';
import getElectron from '../utility/getElectron';
import registerCommand from './registerCommand';
import { apiCall } from '../utility/api';
import { switchCurrentDatabase } from '../utility/common';
import { getDatabasStatusMenu, switchCurrentDatabase } from '../utility/common';
import { __t } from '../translations';
registerCommand({
@@ -18,33 +18,7 @@ registerCommand({
conid: connection._id,
database: name,
};
return [
{
text: 'Sync model (incremental)',
onClick: () => {
apiCall('database-connections/sync-model', dbid);
},
},
{
text: 'Sync model (full)',
onClick: () => {
apiCall('database-connections/sync-model', { ...dbid, isFullRefresh: true });
},
},
{
text: 'Reopen',
onClick: () => {
apiCall('database-connections/refresh', dbid);
},
},
{
text: 'Disconnect',
onClick: () => {
const electron = getElectron();
if (electron) apiCall('database-connections/disconnect', dbid);
switchCurrentDatabase(null);
},
},
];
return getDatabasStatusMenu(dbid);
},
});
+8 -2
View File
@@ -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';
@@ -74,7 +73,14 @@ 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),
});
@@ -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';
@@ -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,
+14 -14
View File
@@ -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 &amp; 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);
}
+2 -1
View File
@@ -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,
});
}
@@ -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', {
+1
View File
@@ -353,6 +353,7 @@
'img data-deploy': 'mdi mdi-database-settings color-icon-green',
'img arrow-start-here': 'mdi mdi-arrow-down-bold-circle color-icon-green',
'img team-file': 'mdi mdi-account-file color-icon-red',
'img table-backup': 'mdi mdi-cube color-icon-yellow',
};
</script>
@@ -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 => ({
+12 -11
View File
@@ -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>
+6 -5
View File
@@ -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();
+12 -11
View File
@@ -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;
+4 -3
View File
@@ -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,
+11 -11
View File
@@ -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>
@@ -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));
+2 -3
View File
@@ -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 &amp; 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>
+14 -13
View File
@@ -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>
+15 -14
View File
@@ -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>
+1 -1
View File
@@ -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} />
+6 -6
View File
@@ -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>
+2 -2
View File
@@ -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>
+14 -13
View File
@@ -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>
+6 -5
View File
@@ -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}
@@ -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' })}
@@ -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,61 +1,27 @@
<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 { internalRedirectTo } from '../clientAuth';
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
import FormSelectField from '../forms/FormSelectField.svelte';
import SelectField from '../forms/SelectField.svelte';
import FontIcon from '../icons/FontIcon.svelte';
import { showModal } from '../modals/modalTools';
import { _t, getSelectedLanguage, setSelectedLanguage } from '../translations';
import { isMac } from '../utility/common';
import getElectron from '../utility/getElectron';
import ConfirmModal from '../modals/ConfirmModal.svelte';
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>
<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
<div class="heading">{_t('settings.application', { defaultMessage: 'Application' })}</div>
<FormFieldTemplateLarge
label={_t('settings.localization.language', { defaultMessage: 'Language' })}
type="combo"
>
<SelectField
isNative
data-testid="SettingsModal_languageSelect"
options={[
@@ -86,7 +52,64 @@ type="combo"
});
}}
/>
</FormFieldTemplateLarge>
</FormFieldTemplateLarge>
<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',
}),
},
]}
/>
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Appearance' })}</div>
{#if electron}
<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>
<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>
@@ -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>
@@ -36,7 +36,7 @@ ORDER BY
</script>
<div class="wrapper">
<div class="heading">{_t('settings.appearance', { defaultMessage: 'Application theme' })}</div>
<div class="heading">{_t('settings.applicationTheme', { defaultMessage: 'Application theme' })}</div>
<FormFieldTemplateLarge
label={_t('settings.appearance.useSystemTheme', { defaultMessage: 'Use system theme' })}
@@ -60,7 +60,11 @@
<FormProvider>
<ModalBase {...$$restProps}>
<svelte:fragment slot="header">{constraintInfo ? _t('foreignKeyEditor.editForeignKey', { defaultMessage: 'Edit foreign key' }) : _t('foreignKeyEditor.addForeignKey', { defaultMessage: 'Add foreign key' })}</svelte:fragment>
<svelte:fragment slot="header"
>{constraintInfo
? _t('foreignKeyEditor.editForeignKey', { defaultMessage: 'Edit foreign key' })
: _t('foreignKeyEditor.addForeignKey', { defaultMessage: 'Add foreign key' })}</svelte:fragment
>
<div class="largeFormMarker">
<div class="row">
@@ -92,6 +96,19 @@
const name = fullNameFromString(e.detail);
refTableName = name.pureName;
refSchemaName = name.schemaName;
if (!columns?.find(x => x.columnName)) {
const refTable = dbInfo?.tables?.find(
x => x.pureName == refTableName && x.schemaName == refSchemaName
);
if (refTable?.primaryKey) {
columns = refTable.primaryKey.columns.map(col => ({
refColumnName: col.columnName,
}));
} else {
columns = [];
}
}
}
}}
/>
@@ -135,7 +152,8 @@
{_t('foreignKeyEditor.baseColumn', { defaultMessage: 'Base column - ' })}{tableInfo.pureName}
</div>
<div class="col-5 ml-1">
{_t('foreignKeyEditor.refColumn', { defaultMessage: 'Ref column - ' })}{refTableName || _t('foreignKeyEditor.tableNotSet', { defaultMessage: '(table not set)' })}
{_t('foreignKeyEditor.refColumn', { defaultMessage: 'Ref column - ' })}{refTableName ||
_t('foreignKeyEditor.tableNotSet', { defaultMessage: '(table not set)' })}
</div>
</div>
@@ -217,7 +235,11 @@
}}
/>
<FormStyledButton type="button" value={_t('common.close', { defaultMessage: 'Close' })} on:click={closeCurrentModal} />
<FormStyledButton
type="button"
value={_t('common.close', { defaultMessage: 'Close' })}
on:click={closeCurrentModal}
/>
{#if constraintInfo}
<FormStyledButton
type="button"
+64 -25
View File
@@ -3,8 +3,8 @@
registerCommand({
id: 'tableEditor.addColumn',
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
name: __t('tableEditor.addColumn', { defaultMessage: 'Add column'}),
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
name: __t('tableEditor.addColumn', { defaultMessage: 'Add column' }),
icon: 'icon add-column',
toolbar: true,
isRelatedToTab: true,
@@ -14,8 +14,8 @@
registerCommand({
id: 'tableEditor.addPrimaryKey',
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
name: __t('tableEditor.addPrimaryKey', { defaultMessage: 'Add primary key'}),
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
name: __t('tableEditor.addPrimaryKey', { defaultMessage: 'Add primary key' }),
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
@@ -25,8 +25,8 @@
registerCommand({
id: 'tableEditor.addForeignKey',
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
name: __t('tableEditor.addForeignKey', { defaultMessage: 'Add foreign key'}),
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
name: __t('tableEditor.addForeignKey', { defaultMessage: 'Add foreign key' }),
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
@@ -36,8 +36,8 @@
registerCommand({
id: 'tableEditor.addIndex',
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
name: __t('tableEditor.addIndex', { defaultMessage: 'Add index'}),
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
name: __t('tableEditor.addIndex', { defaultMessage: 'Add index' }),
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
@@ -47,8 +47,8 @@
registerCommand({
id: 'tableEditor.addUnique',
category: __t('tableEditor', { defaultMessage: 'Table editor'}),
name: __t('tableEditor.addUnique', { defaultMessage: 'Add unique'}),
category: __t('tableEditor', { defaultMessage: 'Table editor' }),
name: __t('tableEditor.addUnique', { defaultMessage: 'Add unique' }),
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
@@ -188,7 +188,10 @@
<ObjectListControl
collection={columns?.map((x, index) => ({ ...x, ordinal: index + 1 }))}
title={_t('tableEditor.columns', { defaultMessage: 'Columns ({columnCount})', values: { columnCount: columns?.length || 0 } })}
title={_t('tableEditor.columnsCount', {
defaultMessage: 'Columns ({columnCount})',
values: { columnCount: columns?.length || 0 },
})}
emptyMessage={_t('tableEditor.nocolumnsdefined', { defaultMessage: 'No columns defined' })}
clickable
on:clickrow={e => showModal(ColumnEditorModal, { columnInfo: e.detail, tableInfo, setTableInfo, driver })}
@@ -217,9 +220,7 @@
text: _t('tableEditor.copydefinitions', { defaultMessage: 'Copy definitions' }),
icon: 'icon copy',
onClick: selected => {
const names = selected
.map(x => `${x.columnName} ${x.dataType}${x.notNull ? ' NOT NULL' : ''}`)
.join(',\n');
const names = selected.map(x => `${x.columnName} ${x.dataType}${x.notNull ? ' NOT NULL' : ''}`).join(',\n');
navigator.clipboard.writeText(names);
},
},
@@ -288,9 +289,21 @@
: null,
]}
>
<svelte:fragment slot="0" let:row>{row?.notNull ? _t('tableEditor.notnull', { defaultMessage: 'NOT NULL' }) : _t('tableEditor.null', { defaultMessage: 'NULL' })}</svelte:fragment>
<svelte:fragment slot="1" let:row>{row?.isSparse ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
<svelte:fragment slot="2" let:row>{row?.isPersisted ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
<svelte:fragment slot="0" let:row
>{row?.notNull
? _t('tableEditor.notnull', { defaultMessage: 'NOT NULL' })
: _t('tableEditor.null', { defaultMessage: 'NULL' })}</svelte:fragment
>
<svelte:fragment slot="1" let:row
>{row?.isSparse
? _t('tableEditor.yes', { defaultMessage: 'YES' })
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
>
<svelte:fragment slot="2" let:row
>{row?.isPersisted
? _t('tableEditor.yes', { defaultMessage: 'YES' })
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
>
<svelte:fragment slot="3" let:row
><Link
onClick={e => {
@@ -299,8 +312,16 @@
}}>{_t('tableEditor.remove', { defaultMessage: 'Remove' })}</Link
></svelte:fragment
>
<svelte:fragment slot="4" let:row>{row?.isUnsigned ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
<svelte:fragment slot="5" let:row>{row?.isZerofill ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
<svelte:fragment slot="4" let:row
>{row?.isUnsigned
? _t('tableEditor.yes', { defaultMessage: 'YES' })
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
>
<svelte:fragment slot="5" let:row
>{row?.isZerofill
? _t('tableEditor.yes', { defaultMessage: 'YES' })
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
>
<svelte:fragment slot="name" let:row><ColumnLabel {...row} forceIcon /></svelte:fragment>
</ObjectListControl>
@@ -321,7 +342,10 @@
<ObjectListControl
collection={indexes}
onAddNew={isWritable && columns?.length > 0 ? addIndex : null}
title={_t('tableEditor.indexes', { defaultMessage: 'Indexes ({indexCount})', values: { indexCount: indexes?.length || 0 } })}
title={_t('tableEditor.indexes', {
defaultMessage: 'Indexes ({indexCount})',
values: { indexCount: indexes?.length || 0 },
})}
emptyMessage={isWritable ? _t('tableEditor.noindexdefined', { defaultMessage: 'No index defined' }) : null}
clickable
on:clickrow={e => showModal(IndexEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo, driver })}
@@ -348,7 +372,11 @@
>
<svelte:fragment slot="name" let:row><ConstraintLabel {...row} /></svelte:fragment>
<svelte:fragment slot="0" let:row>{row?.columns.map(x => x.columnName).join(', ')}</svelte:fragment>
<svelte:fragment slot="1" let:row>{row?.isUnique ? _t('tableEditor.yes', { defaultMessage: 'YES' }) : _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment>
<svelte:fragment slot="1" let:row
>{row?.isUnique
? _t('tableEditor.yes', { defaultMessage: 'YES' })
: _t('tableEditor.no', { defaultMessage: 'NO' })}</svelte:fragment
>
<svelte:fragment slot="2" let:row
><Link
onClick={e => {
@@ -364,7 +392,10 @@
<ObjectListControl
collection={uniques}
onAddNew={isWritable && columns?.length > 0 ? addUnique : null}
title={_t('tableEditor.uniqueConstraints', { defaultMessage: 'Unique constraints ({constraintCount})', values: { constraintCount: uniques?.length || 0 } })}
title={_t('tableEditor.uniqueConstraints', {
defaultMessage: 'Unique constraints ({constraintCount})',
values: { constraintCount: uniques?.length || 0 },
})}
emptyMessage={isWritable ? _t('tableEditor.nouniquedefined', { defaultMessage: 'No unique defined' }) : null}
clickable
on:clickrow={e => showModal(UniqueEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo })}
@@ -401,13 +432,21 @@
<ForeignKeyObjectListControl
collection={foreignKeys}
onAddNew={isWritable && columns?.length > 0 ? addForeignKey : null}
title={_t('tableEditor.foreignKeys', { defaultMessage: 'Foreign keys ({foreignKeyCount})', values: { foreignKeyCount: foreignKeys?.length || 0 } })}
emptyMessage={isWritable ? _t('tableEditor.noforeignkeydefined', { defaultMessage: 'No foreign key defined' }) : null}
title={_t('tableEditor.foreignKeys', {
defaultMessage: 'Foreign keys ({foreignKeyCount})',
values: { foreignKeyCount: foreignKeys?.length || 0 },
})}
emptyMessage={isWritable
? _t('tableEditor.noforeignkeydefined', { defaultMessage: 'No foreign key defined' })
: null}
clickable
onRemove={row => setTableInfo(tbl => editorDeleteConstraint(tbl, row))}
on:clickrow={e => showModal(ForeignKeyEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo, dbInfo })}
/>
<ForeignKeyObjectListControl collection={dependencies} title={_t('tableEditor.dependencies', { defaultMessage: 'Dependencies' })} />
<ForeignKeyObjectListControl
collection={dependencies}
title={_t('tableEditor.dependencies', { defaultMessage: 'Dependencies' })}
/>
{/if}
</div>
@@ -12,6 +12,7 @@
import { onMount, tick } from 'svelte';
import TargetApplicationSelect from '../forms/TargetApplicationSelect.svelte';
import { apiCall } from '../utility/api';
import { _t } from '../translations';
// import { apiCall } from '../utility/api';
// import { saveDbToApp } from '../utility/appTools';
@@ -70,11 +71,11 @@
<FormProvider>
<ModalBase {...$$restProps}>
<svelte:fragment slot="header">Virtual foreign key</svelte:fragment>
<svelte:fragment slot="header">{_t('virtualForeignKey.virtualForeignKey', { defaultMessage: 'Virtual foreign key' })}</svelte:fragment>
<div class="largeFormMarker">
<div class="row">
<div class="label col-3">Referenced table</div>
<div class="label col-3">{_t('virtualForeignKey.referencedTable', { defaultMessage: 'Referenced table' })}</div>
<div class="col-9">
<SelectField
value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })}
@@ -105,10 +106,10 @@
<div class="row">
<div class="col-5 mr-1">
Base column - {$tableInfo?.pureName}
{_t('virtualForeignKey.baseColumn', { defaultMessage: 'Base column' })} - {$tableInfo?.pureName}
</div>
<div class="col-5 ml-1">
Ref column - {refTableName || '(table not set)'}
{_t('virtualForeignKey.refColumn', { defaultMessage: 'Ref column' })} - {refTableName || _t('virtualForeignKey.tableNotSet', { defaultMessage: '(table not set)' })}
</div>
</div>
@@ -152,7 +153,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);
@@ -165,14 +166,14 @@
<FormStyledButton
type="button"
value="Add column"
value={_t('virtualForeignKey.addColumn', { defaultMessage: 'Add column' })}
on:click={() => {
columns = [...columns, {}];
}}
/>
<div class="row">
<div class="label col-3">Target application</div>
<div class="label col-3">{_t('virtualForeignKey.targetApplication', { defaultMessage: 'Target application' })}</div>
<div class="col-9">
<TargetApplicationSelect bind:value={dstApp} {conid} {database} />
</div>
@@ -181,7 +182,7 @@
<svelte:fragment slot="footer">
<FormSubmit
value={'Save'}
value={_t('common.save', { defaultMessage: 'Save' })}
disabled={!dstApp}
on:click={async () => {
await apiCall('apps/save-virtual-reference', {
@@ -196,7 +197,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>
+2 -1
View File
@@ -169,6 +169,7 @@
import hasPermission from '../utility/hasPermission';
import QueryAiAssistant from '../ai/QueryAiAssistant.svelte';
import { getCurrentSettings } from '../stores';
import { Messages } from 'openai/resources/chat/completions';
export let tabid;
export let conid;
@@ -765,7 +766,7 @@
<svelte:fragment slot="2">
<ResultTabs
bind:this={domResultTabs}
tabs={[{ label: 'Messages', slot: 0 }]}
tabs={[{ label: _t('query.Messages', { defaultMessage: 'Messages' }), slot: 0 }]}
{sessionId}
{executeNumber}
bind:resultCount
+2 -10
View File
@@ -11,7 +11,6 @@
import DefaultActionsSettings from "../settings/DefaultActionsSettings.svelte";
import BehaviourSettings from "../settings/BehaviourSettings.svelte";
import ExternalToolsSettings from "../settings/ExternalToolsSettings.svelte";
import OtherSettings from "../settings/OtherSettings.svelte";
import LicenseSettings from "../settings/LicenseSettings.svelte";
import { isProApp } from "../utility/proTools";
import { _t } from "../translations";
@@ -20,6 +19,8 @@
import SQLEditorSettings from "../settings/SQLEditorSettings.svelte";
import AiSettingsTab from "../settings/AiSettingsTab.svelte";
export let selectedItem = 'general';
const menuItems = [
{
label: _t('settings.general', { defaultMessage: 'General' }),
@@ -98,16 +99,7 @@
props: {},
testid: 'settings-ai',
},
{
label: _t('settings.other', { defaultMessage: 'Other' }),
identifier: 'other',
component: OtherSettings,
props: {},
testid: 'settings-other',
},
];
let selectedItem = 'general';
</script>
<SettingsFormProvider>
+6 -6
View File
@@ -172,11 +172,11 @@
const resp = await apiCall('database-connections/run-script', { conid, database, sql, useTransaction: true });
const { errorMessage } = resp || {};
if (errorMessage) {
showModal(ErrorMessageModal, { title: 'Error when saving', message: errorMessage });
showModal(ErrorMessageModal, { title: _t('tableData.errorWhenSaving', { defaultMessage: 'Error when saving' }), message: errorMessage });
} else {
dispatchChangeSet({ type: 'reset', value: createChangeSet() });
cache.update(reloadDataCacheFunc);
showSnackbarSuccess('Saved to database');
showSnackbarSuccess(_t('tableData.savedToDatabase', { defaultMessage: 'Saved to database' }));
}
}
@@ -192,11 +192,11 @@
});
const { errorMessage } = resp || {};
if (errorMessage) {
showModal(ErrorMessageModal, { title: 'Error when saving', message: errorMessage });
showModal(ErrorMessageModal, { title: _t('tableData.errorWhenSaving', { defaultMessage: 'Error when saving' }), message: errorMessage });
} else {
dispatchChangeSet({ type: 'reset', value: createChangeSet() });
cache.update(reloadDataCacheFunc);
showSnackbarSuccess('Saved to database');
showSnackbarSuccess(_t('tableData.savedToDatabase', { defaultMessage: 'Saved to database' }));
}
} else {
const script = driver.createSaveChangeSetScript($changeSetStore?.value, $dbinfo, () =>
@@ -360,13 +360,13 @@
>
<ToolStripCommandSplitButton
buttonLabel={autoRefreshStarted ? `Refresh (every ${autoRefreshInterval}s)` : null}
buttonLabel={autoRefreshStarted ? _t('tableData.refreshEvery', { defaultMessage: 'Refresh (every {autoRefreshInterval}s)', values: { autoRefreshInterval } }) : null}
commands={['dataGrid.refresh', ...createAutoRefreshMenu()]}
hideDisabled
data-testid="TableDataTab_refreshGrid"
/>
<ToolStripCommandSplitButton
buttonLabel={autoRefreshStarted ? `Refresh (every ${autoRefreshInterval}s)` : null}
buttonLabel={autoRefreshStarted ? _t('tableData.refreshEvery', { defaultMessage: 'Refresh (every {autoRefreshInterval}s)', values: { autoRefreshInterval } }) : null}
commands={['dataForm.refresh', ...createAutoRefreshMenu()]}
hideDisabled
data-testid="TableDataTab_refreshForm"
@@ -6,6 +6,7 @@ import { getExtensions } from '../stores';
import { getConnectionInfo, getDatabaseInfo } from './metadataLoaders';
import ConfirmSqlModal, { saveScriptToDatabase } from '../modals/ConfirmSqlModal.svelte';
import { apiCall } from './api';
import { _t } from '../translations';
export async function alterDatabaseDialog(conid, database, updateFunc) {
const conn = await getConnectionInfo({ conid });
@@ -30,8 +31,8 @@ export async function alterDatabaseDialog(conid, database, updateFunc) {
export async function renameDatabaseObjectDialog(conid, database, oldName, updateFunc) {
showModal(InputTextModal, {
value: oldName,
label: 'New name',
header: 'Rename object',
label: _t('renameDatabaseObject.newName', { defaultMessage: 'New name' }),
header: _t('renameDatabaseObject.header', { defaultMessage: 'Rename object' }),
onConfirm: newName => {
alterDatabaseDialog(conid, database, db => updateFunc(db, newName));
},

Some files were not shown because too many files have changed in this diff Show More