Compare commits

..

3 Commits

Author SHA1 Message Date
SPRINX0\prochazka 0b9e8e4d23 upgraded ibm_db package 2025-11-11 10:40:27 +01:00
SPRINX0\prochazka 98c134afe2 v6.6.12-beta.4 2025-11-10 16:11:05 +01:00
SPRINX0\prochazka b15a051dd1 db2 plugin copy 2025-11-10 16:10:50 +01:00
312 changed files with 12668 additions and 15970 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- 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: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- 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: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- 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: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- 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: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- 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: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a
ref: f27a03d4aff5b00a009643df146a9c17bdbf7801
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+18
View File
@@ -42,6 +42,24 @@ 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
-1
View File
@@ -24,7 +24,6 @@ docker/plugins
.env.development.local
.env.test.local
.env.production.local
.env.translation
npm-debug.log*
yarn-debug.log*
-43
View File
@@ -8,49 +8,6 @@ Builds:
- linux - application for linux
- win - application for Windows
## 6.7.3
- FIXED: Fixed problem in analyser core - in PostgreSQL, after dropping table, dropped table still appeared in structure
- FIXED: PostgreSQL numeric columns do not align right #1254
- ADDED: Custom thousands separator #1213
## 6.7.2
- CHANGED: Settings modal redesign - now is settings opened in tab instead of modal, similarily as in VSCode
- FIXED: Fixed search in table shortcuts #1273
- CHANGED: Improved foreign key editor UX
- FIXED: Fixed incremental DB structure refresh for PostgreSQL, optimalized slow loading primary keys in PostgreSQL
- CHANGED: You could now choose, how to refresh structure, added ability to disconnect or reconnect
- ADDED: Better processing of table backups, generate table restore script #1274
- CHANGED: Improved storage of settings, especially for Team Premium edition
## 6.7.1
- ADDED: LANGUAGE environment variable for the web version. #1266
- ADDED: New localizations (Italian, Portugese (Brazil), Japanese)
- ADDED: Option to detect language from browser settings in web version
- FIXED: Check updates option no longer available in 6.7.0 #1263
- FIXED: A MERGE statement must be terminated by a semi-colon (;), but dbgate stripped it. #1257
- ADDED: Show table size #552
- ADDED: Sort tables by size and by row count
- ADDED: Connect to Legacy MongoDB (Premium) #540
- FIXED: Fixed problems in saving team files in Team Premium edition
- CHANGED: Files are by default saved to team folders in Team Premium edition
- ADDED: Other files types supported in Team Premium edition (diagrams, query design, perspectives, import/export jobs, shell scripts, database compare jobs)
## 6.7.0
- ADDED: Added localization support, now you can use DbGate in multiple languages (French, Spanish, German, Czech, Slovak, Simplified Chinese) #347 #705 #939 #1079
- CHANGED: Solved many issues with binary fields, huge performance improvements in binary fields processing
- FIXED: Export to CSV produces empty file #1247
- CHANGED: Upgraded electron to version 38 #1243
- FIXED: PostgreSQL export to SQL and XML doesn't include bytea field contents #1228
- FIXED: Export CSV broken #1080
- FIXED: Inconsistent handling of hex-like strings #680
- FIXED: Export mongodb binary cell as binary file #292
- CHANGED: SSL is used automatically for connections to Azure databases
- ADDED: New export formats CSV for Excel, TSV
- FIXED: Horizontal scrolling on macOS trackpad/Magic Mouse #1250
## 6.6.12
- FIXED: Cannot paste license key on Mac (special commands like copy/paste were disabled on license screen)
## 6.6.11
- FIXED: Fixed theming on application startup
- CHANGED: Improved licensing page
+6 -31
View File
@@ -31,16 +31,6 @@ let mainModule;
let appUpdateStatus = '';
let settingsJson = {};
function getTranslated(key) {
if (typeof key === 'string' && global.TRANSLATION_DATA?.[key]) {
return global.TRANSLATION_DATA?.[key];
}
if (typeof key?._transKey === 'string') {
return global.TRANSLATION_DATA?.[key._transKey] ?? key._transOptions?.defaultMessage;
}
return key;
}
process.on('uncaughtException', function (error) {
console.error('uncaughtException', error);
});
@@ -73,7 +63,6 @@ try {
let mainWindow;
let mainMenu;
let runCommandOnLoad = null;
let mainWindowMenuSet = false;
log.transports.file.level = 'debug';
autoUpdater.logger = log;
@@ -102,16 +91,11 @@ function commandItem(item, disableAll = false) {
if (item.skipInApp) {
return { skip: true };
}
if (!command) {
return { skip: true };
}
return {
id,
label: command
? getTranslated(command.menuName) || getTranslated(command.toolbarName) || getTranslated(command.name)
: id,
label: command ? command.menuName || command.toolbarName || command.name : id,
accelerator: formatKeyText(command ? command.keyText : undefined),
enabled: command ? command.enabled && (!disableAll || command.systemCommand) : false,
enabled: command ? command.enabled && !disableAll : false,
click() {
if (mainWindow) {
mainWindow.webContents.send('run-command', id);
@@ -171,14 +155,11 @@ ipcMain.on('update-commands', async (event, arg) => {
const command = commands[key];
// rebuild menu
if (global.TRANSLATION_DATA && (menu.label != command.text || menu.accelerator != command.keyText)) {
if (menu.label != command.text || menu.accelerator != command.keyText) {
mainMenu = buildMenu(isModalOpened || !!dbgatePage);
Menu.setApplicationMenu(mainMenu);
if (!mainWindowMenuSet) {
mainWindow.setMenu(mainMenu);
mainWindowMenuSet = true;
}
// mainWindow.setMenu(mainMenu);
return;
}
@@ -325,12 +306,6 @@ ipcMain.on('check-for-updates', async (event, url) => {
autoUpdater.autoDownload = false;
autoUpdater.checkForUpdates();
});
ipcMain.on('translation-data', async (event, arg) => {
global.TRANSLATION_DATA = JSON.parse(arg);
mainMenu = buildMenu();
Menu.setApplicationMenu(mainMenu);
mainWindow.setMenu(mainMenu);
});
function fillMissingSettings(value) {
const res = {
@@ -407,8 +382,8 @@ function createWindow() {
mainWindow.setFullScreen(true);
}
// mainMenu = buildMenu();
// mainWindow.setMenu(mainMenu);
mainMenu = buildMenu();
mainWindow.setMenu(mainMenu);
function loadMainWindow() {
const startUrl =
+7 -15
View File
@@ -1,10 +1,6 @@
function _t(key, { defaultMessage, currentTranslations } = {}) {
return (currentTranslations || global.TRANSLATION_DATA)?.[key] || defaultMessage;
}
module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
module.exports = ({ editMenu, isMac }) => [
{
label: _t('menu.file', { defaultMessage: 'File', currentTranslations }),
label: 'File',
submenu: [
{ command: 'new.connection', hideDisabled: true },
{ command: 'new.sqliteDatabase', hideDisabled: true },
@@ -32,7 +28,7 @@ module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
},
editMenu
? {
label: _t('menu.edit', { defaultMessage: 'Edit', currentTranslations }),
label: 'Edit',
submenu: [
{ command: 'edit.undo' },
{ command: 'edit.redo' },
@@ -57,7 +53,7 @@ module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
// ],
// },
{
label: _t('menu.view', { defaultMessage: 'View', currentTranslations }),
label: 'View',
submenu: [
{ command: 'app.reload', hideDisabled: true },
{ command: 'app.toggleDevTools', hideDisabled: true },
@@ -76,12 +72,10 @@ module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
{ command: 'app.zoomIn', hideDisabled: true },
{ command: 'app.zoomOut', hideDisabled: true },
{ command: 'app.zoomReset', hideDisabled: true },
{ divider: true },
{ command: 'app.showLogs', hideDisabled: true },
],
},
{
label: _t('menu.tools', { defaultMessage: 'Tools', currentTranslations }),
label: 'Tools',
submenu: [
{ command: 'database.search', hideDisabled: true },
{ command: 'commandPalette.show', hideDisabled: true },
@@ -97,8 +91,6 @@ module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
{ divider: true },
{ command: 'app.exportConnections', hideDisabled: true },
{ command: 'app.importConnections', hideDisabled: true },
{ divider: true },
{ command: 'app.managePlugins', hideDisabled: true },
],
},
...(isMac
@@ -110,7 +102,7 @@ module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
]
: []),
{
label: _t('menu.help', { defaultMessage: 'Help', currentTranslations }),
label: 'Help',
submenu: [
{ command: 'app.openDocs', hideDisabled: true },
{ command: 'app.openWeb', hideDisabled: true },
@@ -122,7 +114,7 @@ module.exports = ({ editMenu, isMac }, currentTranslations = null) => [
{ command: 'tabs.changelog', hideDisabled: true },
{ command: 'about.show', hideDisabled: true },
{ divider: true },
{ command: 'app.checkForUpdates', hideDisabled: true },
{ command: 'file.checkForUpdates', hideDisabled: true },
],
},
];
-27
View File
@@ -160,31 +160,4 @@ program
}
});
program
.command('sort')
.description('Sort translation files by keys')
.action(() => {
try {
const languages = getAllNonDefaultLanguages();
for (const language of languages) {
const filePath = `./translations/${language}.json`;
const content = fs.readFileSync(filePath, 'utf-8');
const translations = JSON.parse(content);
const sortedTranslations = {};
Object.keys(translations)
.sort()
.forEach(key => {
// @ts-ignore
sortedTranslations[key] = translations[key];
});
fs.writeFileSync(filePath, JSON.stringify(sortedTranslations, null, 2), 'utf-8');
console.log(`Sorted translations for language: ${language}`);
}
} catch (error) {
console.error(error);
console.error('Error during sort:', error.message);
process.exit(1);
}
});
module.exports = { program };
-132
View File
@@ -1,132 +0,0 @@
require('dotenv').config({ path: '.env.translation' });
const fs = require('fs');
const path = require('path');
const OpenAI = require('openai');
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const translationsDir = path.join(__dirname, '../../translations');
const enFilePath = path.join(translationsDir, 'en.json');
const languageNames = {
'cs.json': 'Czech',
'de.json': 'German',
'es.json': 'Spanish',
'fr.json': 'French',
'it.json': 'Italian',
'ja.json': 'Japanese',
'pt.json': 'Portuguese',
'sk.json': 'Slovak',
'zh.json': 'Chinese'
};
// Read source (english)
const enTranslations = JSON.parse(fs.readFileSync(enFilePath, 'utf8'));
const enKeys = Object.keys(enTranslations);
// Get all translation files
const translationFiles = fs.readdirSync(translationsDir)
.filter(file => file.endsWith('.json') && file !== 'en.json')
.sort();
console.log(`Found ${enKeys.length} keys in en.json\n`);
console.log('='.repeat(80));
async function translateMissingIds({file, translations, missingIds}){
const languageName = languageNames[file];
if (!languageName) {
console.log(`No language name mapping for file: ${file}`);
return;
}
// Build object with only missing translations
const needed = {};
missingIds.forEach(key => {
needed[key] = enTranslations[key];
});
// Get all existing translations as style examples
const existingTranslations = {};
Object.keys(translations).forEach(key => {
if (translations[key] && !translations[key].startsWith('***')) {
existingTranslations[key] = {
en: enTranslations[key],
translated: translations[key]
};
}
});
const prompt = `You are a professional translator for DbGate, a database management application.
Translate the following English UI strings to ${languageName}.
IMPORTANT RULES:
1. Preserve ALL placeholders exactly as they appear: {plugin}, {columnNumber}, {0}, {1}, etc.
2. Maintain technical terminology appropriately for database software
3. Match the translation style, tone, and formality of the existing translations shown below
4. Keep the same level of brevity or verbosity as the existing translations
5. Return ONLY valid JSON - no markdown, no explanations, no code blocks
6. Use the same keys as provided
EXISTING TRANSLATIONS (for style reference):
${JSON.stringify(existingTranslations, null, 2)}
STRINGS TO TRANSLATE:
${JSON.stringify(needed, null, 2)}
Return format: {"key": "translated value", ...}`;
const response = await client.chat.completions.create({
model: 'gpt-5.1',
messages: [
{ role: 'system', content: 'You are a professional translator specializing in software localization. Match the style and tone of existing translations. Return only valid JSON.' },
{ role: 'user', content: prompt }
],
temperature: 0.2
});
let translatedJson = response.choices[0].message.content.trim();
// Remove markdown code blocks if present
translatedJson = translatedJson.replace(/^```json\n?/, '').replace(/\n?```$/, '');
return JSON.parse(translatedJson);
}
(async () => {
for (const file of translationFiles) {
const filePath = path.join(translationsDir, file);
const translations = JSON.parse(fs.readFileSync(filePath, 'utf8'));
const missingIds = enKeys.filter(key => !translations.hasOwnProperty(key) || (typeof translations[key] === 'string' && translations[key].startsWith('***')));
console.log(`\n${file.toUpperCase()}`);
console.log('-'.repeat(80));
if (missingIds.length === 0) {
console.log('✓ All translations complete!');
continue;
} else {
console.log(`Found ${missingIds.length} untranslated IDs\n`);
}
const newTranslations = await translateMissingIds({file, translations, missingIds});
if (!newTranslations) {
console.log(`Skipping file due to translation error: ${file}`);
continue;
}
for (const [key, value] of Object.entries(newTranslations)) {
translations[key] = value;
console.log(`Translated: ${key} => ${value}`);
}
fs.writeFileSync(filePath, JSON.stringify(translations, null, 2) + '\n', 'utf8');
console.log(`\n✓ Updated translations written to ${file}`);
}
console.log('\n' + '='.repeat(80));
console.log('Translation complete!\n');
})();
+1 -1
View File
@@ -4,7 +4,6 @@ const volatilePackages = [
'@clickhouse/client',
'bson', // this package is already bundled and is used in mongodb
'mongodb',
'mongodb-old',
'mongodb-client-encryption',
'tedious',
'msnodesqlv8',
@@ -25,6 +24,7 @@ const volatilePackages = [
'@duckdb/node-api',
'@mongosh/browser-runtime-electron',
'@mongosh/service-provider-node-driver',
'ibm_db',
];
module.exports = volatilePackages;
-1
View File
@@ -10,7 +10,6 @@ module.exports = defineConfig({
// baseUrl: 'http://localhost:3000',
// trashAssetsBeforeRuns: false,
chromeWebSecurity: false,
reporter: process.env.CI ? 'mocha-reporter-gha' : 'spec',
setupNodeEvents(on, config) {
// implement node event listeners here
@@ -113,18 +113,6 @@ 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();
+12 -1
View File
@@ -60,7 +60,7 @@ describe('Data browser data', () => {
cy.contains('MyChinook').click();
cy.testid('SqlObjectList_search').clear().type('album');
cy.contains('Tables (1/11)');
cy.contains('347 rows, 65.5 KB, InnoDB');
cy.contains('347 rows, InnoDB');
cy.testid('SqlObjectList_searchMenuDropDown').click();
cy.contains('Column name').click();
cy.contains('Tables (2/11)');
@@ -310,6 +310,17 @@ 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
+13 -115
View File
@@ -110,7 +110,7 @@ describe('Charts', () => {
cy.themeshot('new-object-window');
});
it.skip('Database chat - charts', () => {
it.only('Database chat - charts', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click();
@@ -119,13 +119,11 @@ describe('Charts', () => {
cy.get('body').realType('show me chart of most popular genres');
cy.get('body').realPress('{enter}');
cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
cy.testid('chart-canvas', { timeout: 30000 }).should($c =>
expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/)
);
cy.testid('chart-canvas', { timeout: 30000 }).should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
cy.themeshot('database-chat-chart');
});
it.skip('Database chat', () => {
it('Database chat', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click();
@@ -146,7 +144,7 @@ describe('Charts', () => {
// cy.themeshot('database-chat');
});
it.skip('Explain query error', () => {
it('Explain query error', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('TabsPanel_buttonNewObject').click();
@@ -157,115 +155,15 @@ describe('Charts', () => {
cy.testid('MessageViewRow-explainErrorButton-1').click();
cy.testid('ChatCodeRenderer_useSqlButton', { timeout: 30000 });
cy.themeshot('explain-query-error');
});
it('Switch language', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.testid('WidgetIconPanel_settings').click();
cy.testid('SettingsModal_languageSelect').select('Deutsch');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings').click();
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('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('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('Jazyk');
cy.themeshot('switch-language-cs');
cy.testid('SettingsModal_languageSelect').select('中文');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings').click();
cy.contains('语言');
cy.themeshot('switch-language-zh');
cy.testid('SettingsModal_languageSelect').select('English');
cy.testid('ConfirmModal_okButton').click();
cy.testid('WidgetIconPanel_settings');
});
it('Settings', () => {
cy.testid('WidgetIconPanel_settings').click();
cy.themeshot('app-settings-general');
cy.contains('Behaviour').click();
cy.themeshot('app-settings-behaviour');
cy.get('[data-testid=BehaviourSettings_useTabPreviewMode]').uncheck();
// SQL Editor
cy.contains('SQL Editor').click();
cy.get('[data-testid=SQLEditorSettings_sqlCommandsCase]').select('lowerCase');
cy.contains('MySql-connection').click();
cy.contains('charts_sample').click();
cy.contains('employees').click();
cy.contains('MyChinook').click();
cy.contains('Customer').rightclick();
cy.contains('SQL template').click();
cy.contains('CREATE TABLE').click();
cy.contains('create table');
// Default Actions
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Default Actions').click();
cy.get('[data-testid=DefaultActionsSettings_useLastUsedAction]').uncheck();
// Themes
cy.contains('Themes').click();
cy.themeshot('app-settings-themes');
cy.contains('Dark').click();
cy.get('body').find('.theme-dark').should('exist');
cy.contains('Light').click();
cy.get('body').find('.theme-light').should('exist');
// General
cy.contains(/^General$/).click();
cy.contains('charts_sample');
cy.get('[data-testid=GeneralSettings_lockedDatabaseMode]').check();
cy.contains('Connections').click();
cy.contains('charts_sample').should('not.exist');
// Datagrid
cy.contains('Data grid').click();
cy.get('[data-testid=DataGridSettings_showHintColumns]').uncheck();
cy.wait(500);
cy.contains('Album').click();
cy.contains('AC/DC').should('not.exist');
cy.testid('WidgetIconPanel_settings').click();
cy.contains('Keyboard shortcuts').click();
cy.themeshot('app-settings-keyboard-shortcuts');
cy.contains('Chart').click();
cy.testid('CommandModal_keyboardButton').click();
cy.realPress(['Control', 'g']);
cy.realPress('Enter');
cy.contains('OK').click();
cy.contains('Ctrl+G');
cy.contains('AI').click();
cy.themeshot('app-settings-ai');
cy.get('[data-testid=AISettings_addProviderButton]').click();
cy.contains('Provider 1');
cy.get('[data-testid=AiProviderCard_removeButton]').click();
cy.contains('Are you sure you want to remove Provider 1 provider?');
cy.contains('OK').click();
cy.contains('Provider 1').should('not.exist');
// cy.testid('TabsPanel_buttonNewObject').click();
// cy.testid('NewObjectModal_databaseChat').click();
// cy.wait(1000);
// cy.get('body').realType('show me chart of most popular genres');
// cy.get('body').realPress('{enter}');
// cy.testid('DatabaseChatTab_executeAllQueries', { timeout: 30000 }).click();
// cy.wait(5000);
// cy.testid('chart-canvas').should($c => expect($c[0].toDataURL()).to.match(/^data:image\/png;base64/));
// cy.themeshot('database-chat-chart');
});
});
+5 -64
View File
@@ -103,70 +103,13 @@ 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('addresses').rightclick();
cy.contains('customers').rightclick();
cy.contains('Create table backup').click();
cy.testid('ConfirmSqlModal_okButton').click();
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');
cy.contains('_customers').click();
cy.contains('Rows: 8').should('be.visible');
});
});
@@ -203,15 +146,13 @@ describe('Import CSV', () => {
cy.contains('Import').click();
cy.get('input[type=file]').selectFile('cypress/fixtures/customers-20.csv', { force: true });
cy.testid('ImportExportConfigurator_tableMappingSection').contains('customers-20');
cy.contains('customers-20');
cy.testid('ImportExportTab_preview_content').contains('50ddd99fAdF48B3').should('be.visible');
cy.testid('ImportExportTab_executeButton').click();
cy.testid('ImportExportConfigurator_tableMappingSection').contains('20 rows written').should('be.visible');
cy.contains('20 rows written').should('be.visible');
cy.testid('SqlObjectList_refreshButton').click();
cy.testid('DatabasStatusMenu_refreshFull').click();
// cy.contains('Refresh DB structure (incremental)').click();
cy.testid('SqlObjectList_container').contains('customers-20').click();
cy.contains('Rows: 20').should('be.visible');
+4 -1
View File
@@ -10,11 +10,11 @@
"cypress-real-events": "^1.13.0",
"env-cmd": "^10.1.0",
"kill-port": "^2.0.1",
"mocha-reporter-gha": "^1.1.1",
"start-server-and-test": "^2.0.8"
},
"scripts": {
"cy:open": "cypress open --config experimentalInteractiveRunEvents=true",
"cy:run:add-connection": "cypress run --spec cypress/e2e/add-connection.cy.js",
"cy:run:portal": "cypress run --spec cypress/e2e/portal.cy.js",
"cy:run:oauth": "cypress run --spec cypress/e2e/oauth.cy.js",
@@ -23,6 +23,7 @@
"cy:run:multi-sql": "cypress run --spec cypress/e2e/multi-sql.cy.js",
"cy:run:cloud": "cypress run --spec cypress/e2e/cloud.cy.js",
"cy:run:charts": "cypress run --spec cypress/e2e/charts.cy.js",
"start:add-connection": "node clearTestingData && cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:portal": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/portal/.env node e2e-tests/init/portal.js && env-cmd -f e2e-tests/env/portal/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:oauth": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/oauth/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
@@ -31,6 +32,7 @@
"start:multi-sql": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/multi-sql/.env node e2e-tests/init/multi-sql.js && env-cmd -f e2e-tests/env/multi-sql/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:cloud": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/cloud/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:charts": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/charts/.env node e2e-tests/init/charts.js && env-cmd -f e2e-tests/env/charts/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"test:add-connection": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection",
"test:portal": "start-server-and-test start:portal http://localhost:3000 cy:run:portal",
"test:oauth": "start-server-and-test start:oauth http://localhost:3000 cy:run:oauth",
@@ -39,6 +41,7 @@
"test:multi-sql": "start-server-and-test start:multi-sql http://localhost:3000 cy:run:multi-sql",
"test:cloud": "start-server-and-test start:cloud http://localhost:3000 cy:run:cloud",
"test:charts": "start-server-and-test start:charts http://localhost:3000 cy:run:charts",
"test": "yarn test:add-connection && yarn test:portal && yarn test:oauth && yarn test:browse-data && yarn test:team && yarn test:multi-sql && yarn test:cloud && yarn test:charts",
"test:ci": "yarn test"
},
-52
View File
@@ -2,34 +2,6 @@
# yarn lockfile v1
"@actions/core@^1.10.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.11.1.tgz#ae683aac5112438021588030efb53b1adb86f172"
integrity sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==
dependencies:
"@actions/exec" "^1.1.1"
"@actions/http-client" "^2.0.1"
"@actions/exec@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@actions/exec/-/exec-1.1.1.tgz#2e43f28c54022537172819a7cf886c844221a611"
integrity sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==
dependencies:
"@actions/io" "^1.0.1"
"@actions/http-client@^2.0.1":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674"
integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==
dependencies:
tunnel "^0.0.6"
undici "^5.25.4"
"@actions/io@^1.0.1":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.1.3.tgz#4cdb6254da7962b07473ff5c335f3da485d94d71"
integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@@ -67,11 +39,6 @@
debug "^3.1.0"
lodash.once "^4.1.1"
"@fastify/busboy@^2.0.0":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
@@ -980,13 +947,6 @@ minimist@^1.2.8:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
mocha-reporter-gha@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/mocha-reporter-gha/-/mocha-reporter-gha-1.1.1.tgz#e1248abd0769f55b57b36ccd7db2b0b6573d5adf"
integrity sha512-CFbcgM56V4yWlbF91XuwrE6a5X/IqjVXTPefO7m8cY8Es8G1UhJ2KKOrk16AcSemRzVWXp2Fdy3bWJ7j45snWw==
dependencies:
"@actions/core" "^1.10.1"
ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -1332,11 +1292,6 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
tunnel@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
@@ -1352,13 +1307,6 @@ undici-types@~6.20.0:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
undici@^5.25.4:
version "5.29.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3"
integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==
dependencies:
"@fastify/busboy" "^2.0.0"
universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
@@ -12,7 +12,6 @@ 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');
@@ -26,13 +25,6 @@ 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 })),
// })),
};
}
@@ -41,7 +33,7 @@ function checkTableStructure(engine, t1, t2) {
expect(pickImportantTableInfo(engine, t1)).toEqual(pickImportantTableInfo(engine, t2));
}
async function testTableDiff(engine, conn, driver, mangle, changedTable = 't1') {
async function testTableDiff(engine, conn, driver, mangle) {
const initQuery = formatQueryWithoutParams(driver, `create table ~t0 (~id int not null primary key)`);
await driver.query(conn, transformSqlForEngine(engine, initQuery));
@@ -76,38 +68,17 @@ async function testTableDiff(engine, conn, driver, mangle, changedTable = 't1')
await driver.query(conn, transformSqlForEngine(engine, query));
}
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));
const tget = x => x.tables.find(y => y.pureName == 't1');
const structure1 = generateDbPairingId(extendDatabaseInfo(await driver.analyseFull(conn)));
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));
@@ -243,48 +214,6 @@ 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) => {
-73
View File
@@ -49,32 +49,6 @@ class StreamHandler {
}
}
class BinaryTestStreamHandler {
constructor(resolve, reject, expectedValue) {
this.resolve = resolve;
this.reject = reject;
this.expectedValue = expectedValue;
this.rowsReceived = [];
}
row(row) {
try {
this.rowsReceived.push(row);
if (this.expectedValue) {
expect(row).toEqual(this.expectedValue);
}
} catch (error) {
this.reject(error);
return;
}
}
recordset(columns) {}
done(result) {
this.resolve(this.rowsReceived);
}
info(msg) {}
}
function executeStreamItem(driver, conn, sql) {
return new Promise(resolve => {
const handler = new StreamHandler(resolve);
@@ -249,51 +223,4 @@ describe('Query', () => {
expect(row[keys[0]] == 1).toBeTruthy();
})
);
test.each(engines.filter(x => x.binaryDataType).map(engine => [engine.label, engine]))(
'Binary - %s',
testWrapper(async (dbhan, driver, engine) => {
await runCommandOnDriver(dbhan, driver, dmp =>
dmp.createTable({
pureName: 't1',
columns: [
{ columnName: 'id', dataType: 'int', notNull: true, autoIncrement: true },
{ columnName: 'val', dataType: engine.binaryDataType },
],
primaryKey: {
columns: [{ columnName: 'id' }],
},
})
);
const structure = await driver.analyseFull(dbhan);
const table = structure.tables.find(x => x.pureName == 't1');
const dmp = driver.createDumper();
dmp.putCmd("INSERT INTO ~t1 (~val) VALUES (%v)", {
$binary: { base64: 'iVBORw0KWgo=' },
});
await driver.query(dbhan, dmp.s, {discardResult: true});
const dmp2 = driver.createDumper();
dmp2.put('SELECT ~val FROM ~t1');
const res = await driver.query(dbhan, dmp2.s);
const row = res.rows[0];
const keys = Object.keys(row);
expect(keys.length).toEqual(1);
expect(row[keys[0]]).toEqual({$binary: {base64: 'iVBORw0KWgo='}});
const res2 = await driver.readQuery(dbhan, dmp2.s);
const rows = await Array.fromAsync(res2);
const rowsVal = rows.filter(r => r.val != null);
expect(rowsVal.length).toEqual(1);
expect(rowsVal[0].val).toEqual({$binary: {base64: 'iVBORw0KWgo='}});
const res3 = await new Promise((resolve, reject) => {
const handler = new BinaryTestStreamHandler(resolve, reject, {val: {$binary: {base64: 'iVBORw0KWgo='}}});
driver.stream(dbhan, dmp2.s, handler);
});
})
);
});
@@ -33,9 +33,7 @@ describe('Schema tests', () => {
expect(schemas2.find(x => x.schemaName == 'myschema')).toBeTruthy();
expect(schemas2.length).toEqual(count + 1);
expect(schemas2.find(x => x.isDefault).schemaName).toEqual(engine.defaultSchemaName);
if (!engine.skipIncrementalAnalysis) {
expect(structure2).toBeNull();
}
expect(structure2).toBeNull();
})
);
@@ -53,9 +51,7 @@ describe('Schema tests', () => {
const structure2 = await driver.analyseIncremental(conn, structure1);
const schemas2 = await driver.listSchemas(conn);
expect(schemas2.find(x => x.schemaName == 'myschema')).toBeFalsy();
if (!engine.skipIncrementalAnalysis) {
expect(structure2).toBeNull();
}
expect(structure2).toBeNull();
})
);
@@ -94,7 +94,7 @@ describe('Table analyse', () => {
})
);
test.each(engines.map(engine => [engine.label, engine]))(
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
'Table add - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
@@ -112,7 +112,7 @@ describe('Table analyse', () => {
})
);
test.each(engines.map(engine => [engine.label, engine]))(
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
'Table remove - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine)));
@@ -130,7 +130,7 @@ describe('Table analyse', () => {
})
);
test.each(engines.map(engine => [engine.label, engine]))(
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
'Table change - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine)));
+4 -10
View File
@@ -44,7 +44,6 @@ const mysqlEngine = {
supportRenameSqlObject: false,
dbSnapshotBySeconds: true,
dumpFile: 'data/chinook-mysql.sql',
binaryDataType: 'blob',
dumpChecks: [
{
sql: 'select count(*) as res from genre',
@@ -187,7 +186,6 @@ const mariaDbEngine = {
/** @type {import('dbgate-types').TestEngineInfo} */
const postgreSqlEngine = {
label: 'PostgreSQL',
skipIncrementalAnalysis: true,
connection: {
engine: 'postgres@dbgate-plugin-postgres',
password: 'Pwd2020Db',
@@ -218,7 +216,6 @@ const postgreSqlEngine = {
supportSchemas: true,
supportRenameSqlObject: true,
defaultSchemaName: 'public',
binaryDataType: 'bytea',
dumpFile: 'data/chinook-postgre.sql',
dumpChecks: [
{
@@ -449,7 +446,6 @@ const sqlServerEngine = {
supportTableComments: true,
supportColumnComments: true,
// skipSeparateSchemas: true,
binaryDataType: 'varbinary(100)',
triggers: [
{
testName: 'triggers before each row',
@@ -510,7 +506,6 @@ const sqliteEngine = {
},
},
],
binaryDataType: 'blob',
};
const libsqlFileEngine = {
@@ -624,7 +619,6 @@ const oracleEngine = {
},
},
],
binaryDataType: 'blob',
};
/** @type {import('dbgate-types').TestEngineInfo} */
@@ -760,16 +754,16 @@ const enginesOnLocal = [
// cassandraEngine,
// mysqlEngine,
// mariaDbEngine,
postgreSqlEngine,
//sqlServerEngine,
// postgreSqlEngine,
// sqlServerEngine,
// sqliteEngine,
// cockroachDbEngine,
// clickhouseEngine,
// libsqlFileEngine,
// libsqlWsEngine,
//oracleEngine,
// oracleEngine,
// duckdbEngine,
//firebirdEngine,
firebirdEngine,
];
/** @type {import('dbgate-types').TestEngineInfo[] & Record<string, import('dbgate-types').TestEngineInfo>} */
-1
View File
@@ -1,4 +1,3 @@
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": "^28.1.3",
"jest": "^27.0.1",
"pino-pretty": "^11.2.2",
"tmp": "^0.2.3"
}
+1 -3
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "6.7.3",
"version": "6.6.12-beta.4",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -74,8 +74,6 @@
"translations:add-missing": "node common/translations-cli/index.js add-missing",
"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 -1
View File
@@ -2,7 +2,7 @@ DEVMODE=1
SHELL_SCRIPTING=1
ALLOW_DBGATE_PRIVATE_CLOUD=1
DEVWEB=1
# LOCAL_AUTH_PROXY=1
LOCAL_AUTH_PROXY=1
# LOCAL_AI_GATEWAY=true
# REDIRECT_TO_DBGATE_CLOUD_LOGIN=1
+1 -1
View File
@@ -31,7 +31,7 @@
"cors": "^2.8.5",
"cross-env": "^6.0.3",
"dbgate-datalib": "^6.0.0-alpha.1",
"dbgate-query-splitter": "^4.11.9",
"dbgate-query-splitter": "^4.11.7",
"dbgate-sqltree": "^6.0.0-alpha.1",
"dbgate-tools": "^6.0.0-alpha.1",
"debug": "^4.3.4",
+2 -2
View File
@@ -35,8 +35,8 @@ module.exports = {
},
refreshPublicFiles_meta: true,
async refreshPublicFiles({ isRefresh }, req) {
await refreshPublicFiles(isRefresh, req?.headers?.['x-ui-language']);
async refreshPublicFiles({ isRefresh }) {
await refreshPublicFiles(isRefresh);
return {
status: 'ok',
};
+9 -6
View File
@@ -71,7 +71,6 @@ module.exports = {
const isLicenseValid = checkedLicense?.status == 'ok';
const logoutUrl = storageConnectionError ? null : await authProvider.getLogoutUrl();
const adminConfig = storageConnectionError ? null : await storage.readConfig({ group: 'admin' });
const settingsConfig = storageConnectionError ? null : await storage.readConfig({ group: 'settings' });
storage.startRefreshLicense();
@@ -122,7 +121,6 @@ module.exports = {
allowPrivateCloud: platformInfo.isElectron || !!process.env.ALLOW_DBGATE_PRIVATE_CLOUD,
...currentVersion,
redirectToDbGateCloudLogin: !!process.env.REDIRECT_TO_DBGATE_CLOUD_LOGIN,
preferrendLanguage: settingsConfig?.['storage.language'] || process.env.LANGUAGE || null,
};
return configResult;
@@ -289,11 +287,16 @@ module.exports = {
const res = await lock.acquire('settings', async () => {
const currentValue = await this.loadSettings();
try {
let updated = {
...currentValue,
...values,
};
let updated = currentValue;
if (process.env.STORAGE_DATABASE) {
updated = {
...currentValue,
..._.mapValues(values, v => {
if (v === true) return 'true';
if (v === false) return 'false';
return v;
}),
};
await storage.writeConfig({
group: 'settings',
config: updated,
+3 -14
View File
@@ -14,11 +14,7 @@ const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
const processArgs = require('../utility/processArgs');
const { safeJsonParse, getLogger, extractErrorLogData } = require('dbgate-tools');
const platformInfo = require('../utility/platformInfo');
const {
connectionHasPermission,
testConnectionPermission,
loadPermissionsFromRequest,
} = require('../utility/hasPermission');
const { connectionHasPermission, testConnectionPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
const pipeForkLogs = require('../utility/pipeForkLogs');
const requireEngineDriver = require('../utility/requireEngineDriver');
const { getAuthProviderById } = require('../auth/authProvider');
@@ -120,10 +116,7 @@ function getPortalCollections() {
}
}
logger.info(
{ connections: connections.map(pickSafeConnectionInfo) },
'DBGM-00005 Using connections from ENV variables'
);
logger.info({ connections: connections.map(pickSafeConnectionInfo) }, 'DBGM-00005 Using connections from ENV variables');
const noengine = connections.filter(x => !x.engine);
if (noengine.length > 0) {
logger.warn(
@@ -509,11 +502,7 @@ module.exports = {
state,
client: 'web',
});
if (authResp?.url) {
res.redirect(authResp.url);
return;
}
res.json({ error: 'No URL returned from auth provider' });
res.redirect(authResp.url);
},
dbloginApp_meta: true,
+1 -44
View File
@@ -360,12 +360,6 @@ module.exports = {
"columnName": "value",
"dataType": "varchar(1000)",
"notNull": false
},
{
"pureName": "config",
"columnName": "valueType",
"dataType": "varchar(50)",
"notNull": false
}
],
"foreignKeys": [],
@@ -1539,12 +1533,6 @@ module.exports = {
"columnName": "name",
"dataType": "varchar(250)",
"notNull": true
},
{
"pureName": "team_file_types",
"columnName": "format",
"dataType": "varchar(50)",
"notNull": false
}
],
"foreignKeys": [],
@@ -1561,38 +1549,7 @@ module.exports = {
"preloadedRows": [
{
"id": -1,
"name": "sql",
"format": "text"
},
{
"id": -2,
"name": "diagrams",
"format": "json"
},
{
"id": -3,
"name": "query",
"format": "json"
},
{
"id": -4,
"name": "perspectives",
"format": "json"
},
{
"id": -5,
"name": "impexp",
"format": "json"
},
{
"id": -6,
"name": "shell",
"format": "text"
},
{
"id": -7,
"name": "dbcompare",
"format": "json"
"name": "sql"
}
]
},
+6 -13
View File
@@ -193,7 +193,7 @@ async function getCloudSigninHeaders(holder = null) {
return null;
}
async function updateCloudFiles(isRefresh, language) {
async function updateCloudFiles(isRefresh) {
let lastCloudFilesTags;
try {
lastCloudFilesTags = await fs.readFile(path.join(datadir(), 'cloud-files-tags.txt'), 'utf-8');
@@ -218,7 +218,6 @@ async function updateCloudFiles(isRefresh, language) {
...getLicenseHttpHeaders(),
...(await getCloudInstanceHeaders()),
'x-app-version': currentVersion.version,
'x-app-language': language || 'en',
},
}
);
@@ -275,7 +274,7 @@ async function ensurePromoWidgetDataLoaded() {
promoWidgetDataLoaded = true;
}
async function updatePremiumPromoWidget(language) {
async function updatePremiumPromoWidget() {
await ensurePromoWidgetDataLoaded();
const tags = (await collectCloudFilesSearchTags()).join(',');
@@ -284,10 +283,8 @@ async function updatePremiumPromoWidget(language) {
`${DBGATE_CLOUD_URL}/premium-promo-widget?identifier=${promoWidgetData?.identifier ?? 'empty'}&tags=${tags}`,
{
headers: {
...getLicenseHttpHeaders(),
...(await getCloudInstanceHeaders()),
'x-app-version': currentVersion.version,
'x-app-language': language || 'en',
},
}
);
@@ -302,21 +299,17 @@ async function updatePremiumPromoWidget(language) {
socket.emitChanged(`promo-widget-changed`);
}
async function refreshPublicFiles(isRefresh, uiLanguage) {
const language = platformInfo.isElectron
? (await config.getCachedSettings())?.['localization.language'] || 'en'
: uiLanguage;
async function refreshPublicFiles(isRefresh) {
if (!cloudFiles) {
await loadCloudFiles();
}
try {
await updateCloudFiles(isRefresh, language);
await updateCloudFiles(isRefresh);
} catch (err) {
logger.error(extractErrorLogData(err), 'DBGM-00166 Error updating cloud files');
}
const configSettings = await config.get();
if (!isProApp() || configSettings?.trialDaysLeft != null) {
await updatePremiumPromoWidget(language);
if (!isProApp()) {
await updatePremiumPromoWidget();
}
}
@@ -17,7 +17,7 @@ class QueryStreamTableWriter {
this.started = new Date().getTime();
}
initializeFromQuery(structure, resultIndex, chartDefinition, autoDetectCharts = false, options = {}) {
initializeFromQuery(structure, resultIndex, chartDefinition, autoDetectCharts = false) {
this.jslid = crypto.randomUUID();
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
fs.writeFileSync(
@@ -25,7 +25,6 @@ class QueryStreamTableWriter {
JSON.stringify({
...structure,
__isStreamHeader: true,
...options
}) + '\n'
);
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
@@ -180,7 +179,7 @@ class StreamHandler {
process.send({ msgtype: 'changedCurrentDatabase', database, sesid: this.sesid });
}
recordset(columns, options) {
recordset(columns) {
if (this.rowsLimitOverflow) {
return;
}
@@ -190,8 +189,7 @@ class StreamHandler {
Array.isArray(columns) ? { columns } : columns,
this.queryStreamInfoHolder.resultIndex,
this.frontMatter?.[`chart-${this.queryStreamInfoHolder.resultIndex + 1}`],
this.autoDetectCharts,
options
this.autoDetectCharts
);
this.queryStreamInfoHolder.resultIndex += 1;
this.rowCounter = 0;
-1
View File
@@ -2,5 +2,4 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
reporters: ['default', 'github-actions'],
};
+1 -14
View File
@@ -35,12 +35,6 @@ program
.option('-u, --user <user>', 'user name')
.option('-p, --password <password>', 'password')
.option('-d, --database <database>', 'database name')
.option('--url <url>', 'database url')
.option('--file <file>', 'database file')
.option('--socket-path <socketPath>', 'socket path')
.option('--service-name <serviceName>', 'service name (for Oracle)')
.option('--auth-type <authType>', 'authentication type')
.option('--use-ssl', 'use SSL connection')
.option('--auto-index-foreign-keys', 'automatically adds indexes to all foreign keys')
.option(
'--load-data-condition <condition>',
@@ -54,7 +48,7 @@ program
.command('deploy <modelFolder>')
.description('Deploys model to database')
.action(modelFolder => {
const { engine, server, user, password, database, url, file, transaction } = program.opts();
const { engine, server, user, password, database, transaction } = program.opts();
// const hooks = [];
// if (program.autoIndexForeignKeys) hooks.push(dbmodel.hooks.autoIndexForeignKeys);
@@ -66,13 +60,6 @@ program
user,
password,
database,
databaseUrl: url,
useDatabaseUrl: !!url,
databaseFile: file,
socketPath: program.socketPath,
serviceName: program.serviceName,
authType: program.authType,
useSsl: program.useSsl,
},
modelFolder,
useTransaction: transaction,
-1
View File
@@ -2,5 +2,4 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
reporters: ['default', 'github-actions'],
};
+2 -5
View File
@@ -1,4 +1,4 @@
import { arrayToHexString, base64ToHex, evalFilterBehaviour, isTypeDateTime } from 'dbgate-tools';
import { arrayToHexString, evalFilterBehaviour, isTypeDateTime } from 'dbgate-tools';
import { format, toDate } from 'date-fns';
import _isString from 'lodash/isString';
import _cloneDeepWith from 'lodash/cloneDeepWith';
@@ -21,13 +21,10 @@ export function getFilterValueExpression(value, dataType?) {
if (value === false) return 'FALSE';
if (value.$oid) return `ObjectId("${value.$oid}")`;
if (value.$bigint) return value.$bigint;
if (value.$decimal) return value.$decimal;
if (value.type == 'Buffer' && Array.isArray(value.data)) {
return '0x' + arrayToHexString(value.data);
}
if (value?.$binary?.base64) {
return base64ToHex(value.$binary.base64);
}
return `="${value}"`;
}
+5 -2
View File
@@ -2,7 +2,7 @@ import P from 'parsimmon';
import moment from 'moment';
import { Condition } from 'dbgate-sqltree';
import { interpretEscapes, token, word, whitespace } from './common';
import { hexToBase64, parseNumberSafe } from 'dbgate-tools';
import { hexStringToArray, parseNumberSafe } from 'dbgate-tools';
import { FilterBehaviour, TransformType } from 'dbgate-types';
const binaryCondition =
@@ -385,7 +385,10 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
hexstring: () =>
token(P.regexp(/0x(([0-9a-fA-F][0-9a-fA-F])+)/, 1))
.map(x => ({ $binary: { base64: hexToBase64(x) } }))
.map(x => ({
type: 'Buffer',
data: hexStringToArray(x),
}))
.desc('hex string'),
noQuotedString: () => P.regexp(/[^\s^,^'^"]+/).desc('string unquoted'),
+2 -5
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, dumpSqlSourceDef, dumpSqlSourceRef } from './dumpSqlSource';
import { dumpSqlFromDefinition, dumpSqlSourceRef } from './dumpSqlSource';
import { dumpSqlCondition } from './dumpSqlCondition';
export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
@@ -115,10 +115,7 @@ export function dumpSqlInsert(dmp: SqlDumper, cmd: Insert) {
cmd.fields.map(x => x.targetColumn)
);
dmp.putCollection(',', cmd.fields, x => dumpSqlExpression(dmp, x));
if (cmd.whereNotExistsSource) {
dmp.put(' ^from ');
dumpSqlSourceDef(dmp, cmd.whereNotExistsSource);
} else if (dmp.dialect.requireFromDual) {
if (dmp.dialect.requireFromDual) {
dmp.put(' ^from ^dual ');
}
dmp.put(' ^where ^not ^exists (^select * ^from %f ^where ', cmd.targetTable);
@@ -2,7 +2,6 @@ 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) {
@@ -68,11 +67,5 @@ export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) {
});
dmp.put(')');
break;
case 'select':
dmp.put('(');
dumpSqlSelect(dmp, expr.select);
dmp.put(')');
break;
}
}
@@ -19,7 +19,6 @@ function isLike(value, test) {
function extractRawValue(value) {
if (value?.$bigint) return value.$bigint;
if (value?.$oid) return value.$oid;
if (value?.$decimal) return value.$decimal;
return value;
}
+1 -8
View File
@@ -44,7 +44,6 @@ export interface Insert {
fields: UpdateField[];
targetTable: NamedObjectInfo;
insertWhereNotExistsCondition?: Condition;
whereNotExistsSource?: Source;
}
export interface AllowIdentityInsert {
@@ -227,11 +226,6 @@ export interface RowNumberExpression {
orderBy: OrderByExpression[];
}
export interface SelectExpression {
exprType: 'select';
select: Select;
}
export type Expression =
| ColumnRefExpression
| ValueExpression
@@ -241,8 +235,7 @@ export type Expression =
| CallExpression
| MethodCallExpression
| TranformExpression
| RowNumberExpression
| SelectExpression;
| RowNumberExpression;
export type OrderByExpression = Expression & { direction: 'ASC' | 'DESC' };
export type ResultField = Expression & { alias?: string };
-1
View File
@@ -2,5 +2,4 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
reporters: ['default', 'github-actions'],
};
+1 -1
View File
@@ -33,7 +33,7 @@
},
"dependencies": {
"blueimp-md5": "^2.19.0",
"dbgate-query-splitter": "^4.11.9",
"dbgate-query-splitter": "^4.11.7",
"dbgate-sqltree": "^6.0.0-alpha.1",
"debug": "^4.3.4",
"json-stable-stringify": "^1.0.1",
-5
View File
@@ -164,11 +164,6 @@ export class DatabaseAnalyser<TClient = any> {
const res = {};
for (const field of STRUCTURE_FIELDS) {
const isAll = this.modifications.some(x => x.action == 'all' && x.objectTypeField == field);
if (isAll) {
res[field] = newlyAnalysed[field] || [];
continue;
}
const removedIds = this.modifications
.filter(x => x.action == 'remove' && x.objectTypeField == field)
.map(x => x.objectId);
-9
View File
@@ -78,16 +78,7 @@ export class SqlDumper implements AlterProcessor {
else if (_isNumber(value)) this.putRaw(value.toString());
else if (_isDate(value)) this.putStringValue(new Date(value).toISOString());
else if (value?.type == 'Buffer' && _isArray(value?.data)) this.putByteArrayValue(value?.data);
else if (value?.$binary?.base64) {
const binary = atob(value.$binary.base64);
const bytes = new Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
this.putByteArrayValue(bytes);
}
else if (value?.$bigint) this.putRaw(value?.$bigint);
else if (value?.$decimal) this.putRaw(value?.$decimal);
else if (_isPlainObject(value) || _isArray(value)) this.putStringValue(JSON.stringify(value));
else this.put('^null');
}
-1
View File
@@ -47,7 +47,6 @@ export const mongoFilterBehaviour: FilterBehaviour = {
allowStringToken: true,
allowNumberDualTesting: true,
allowObjectIdTesting: true,
allowHexString: true,
};
export const evalFilterBehaviour: FilterBehaviour = {
+15 -72
View File
@@ -43,20 +43,6 @@ export function hexStringToArray(inputString) {
return res;
}
export function base64ToHex(base64String) {
const binaryString = atob(base64String);
const hexString = Array.from(binaryString, c => c.charCodeAt(0).toString(16).padStart(2, '0')).join('');
return '0x' + hexString.toUpperCase();
}
export function hexToBase64(hexString) {
const binaryString = hexString
.match(/.{1,2}/g)
.map(byte => String.fromCharCode(parseInt(byte, 16)))
.join('');
return btoa(binaryString);
}
export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) {
if (!_isString(value)) return value;
@@ -68,9 +54,8 @@ export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) {
const mHex = value.match(/^0x([0-9a-fA-F][0-9a-fA-F])+$/);
if (mHex) {
return {
$binary: {
base64: hexToBase64(value.substring(2)),
},
type: 'Buffer',
data: hexStringToArray(value.substring(2)),
};
}
}
@@ -201,26 +186,6 @@ function stringifyJsonToGrid(value): ReturnType<typeof stringifyCellValue> {
return { value: '(JSON)', gridStyle: 'nullCellStyle' };
}
function formatNumberCustomSeparator(value, thousandsSeparator) {
const [intPart, decPart] = value.split('.');
const intPartWithSeparator = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
return decPart ? `${intPartWithSeparator}.${decPart}` : intPartWithSeparator;
}
function formatCellNumber(value, gridFormattingOptions?: { thousandsSeparator?: string }) {
const separator = gridFormattingOptions?.thousandsSeparator;
if (_isNumber(value)) {
if (separator === 'none' || (value < 1000 && value > -1000)) return value.toString();
if (separator === 'system') return value.toLocaleString();
}
// fallback for system locale
if (separator === 'space' || separator === 'system') return formatNumberCustomSeparator(value.toString(), ' ');
if (separator === 'narrowspace') return formatNumberCustomSeparator(value.toString(), '\u202F');
if (separator === 'comma') return formatNumberCustomSeparator(value.toString(), ',');
if (separator === 'dot') return formatNumberCustomSeparator(value.toString(), '.');
return value.toString();
}
export function stringifyCellValue(
value,
intent:
@@ -231,7 +196,7 @@ export function stringifyCellValue(
| 'exportIntent'
| 'clipboardIntent',
editorTypes?: DataEditorTypesBehaviour,
gridFormattingOptions?: { thousandsSeparator?: string },
gridFormattingOptions?: { useThousandsSeparator?: boolean },
jsonParsedValue?: any
): {
value: string;
@@ -265,26 +230,11 @@ export function stringifyCellValue(
if (value === true) return { value: 'true', gridStyle: 'valueCellStyle' };
if (value === false) return { value: 'false', gridStyle: 'valueCellStyle' };
if (value?.$binary?.base64) {
return {
value: base64ToHex(value.$binary.base64),
gridStyle: 'valueCellStyle',
};
}
if (value?.$decimal) {
return {
value: formatCellNumber(value.$decimal, gridFormattingOptions),
gridStyle: 'valueCellStyle',
};
}
if (editorTypes?.parseHexAsBuffer) {
// if (value?.type == 'Buffer' && _isArray(value.data)) {
// return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' };
// }
if (value?.type == 'Buffer' && _isArray(value.data)) {
return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' };
}
}
if (editorTypes?.parseObjectIdAsDollar) {
if (value?.$oid) {
switch (intent) {
@@ -298,13 +248,13 @@ export function stringifyCellValue(
}
if (value?.$bigint) {
return {
value: formatCellNumber(value.$bigint, gridFormattingOptions),
value: value.$bigint,
gridStyle: 'valueCellStyle',
};
}
if (typeof value === 'bigint') {
return {
value: formatCellNumber(value.toString(), gridFormattingOptions),
value: value.toString(),
gridStyle: 'valueCellStyle',
};
}
@@ -379,8 +329,13 @@ export function stringifyCellValue(
if (_isNumber(value)) {
switch (intent) {
case 'gridCellIntent':
const separator = gridFormattingOptions?.thousandsSeparator;
return { value: formatCellNumber(value, gridFormattingOptions), gridStyle: 'valueCellStyle' };
return {
value:
gridFormattingOptions?.useThousandsSeparator && (value >= 10000 || value <= -10000)
? value.toLocaleString()
: value.toString(),
gridStyle: 'valueCellStyle',
};
default:
return { value: value.toString() };
}
@@ -472,9 +427,6 @@ export function shouldOpenMultilineDialog(value) {
if (value?.$bigint) {
return false;
}
if (value?.$decimal) {
return false;
}
if (_isPlainObject(value) || _isArray(value)) {
return true;
}
@@ -530,9 +482,6 @@ export function getAsImageSrc(obj) {
if (obj?.type == 'Buffer' && _isArray(obj?.data)) {
return `data:image/png;base64, ${arrayBufferToBase64(obj?.data)}`;
}
if (obj?.$binary?.base64) {
return `data:image/png;base64, ${obj.$binary.base64}`;
}
if (_isString(obj) && (obj.startsWith('http://') || obj.startsWith('https://'))) {
return obj;
@@ -725,9 +674,6 @@ export function deserializeJsTypesFromJsonParse(obj) {
if (value?.$bigint) {
return BigInt(value.$bigint);
}
if (value?.$decimal) {
return value.$decimal;
}
});
}
@@ -742,9 +688,6 @@ export function deserializeJsTypesReviver(key, value) {
if (value?.$bigint) {
return BigInt(value.$bigint);
}
if (value?.$decimal) {
return value.$decimal;
}
return value;
}
-1
View File
@@ -16,7 +16,6 @@ export interface SqlDumper extends AlterProcessor {
transform(type: TransformType, dumpExpr: () => void);
createDatabase(name: string);
dropDatabase(name: string);
comment(value: string);
callableTemplate(func: CallableObjectInfo);
-1
View File
@@ -238,7 +238,6 @@ export interface EngineDriver<TClient = any, TDataBase = any> extends FilterBeha
supportsDatabaseRestore?: boolean;
supportsServerSummary?: boolean;
supportsDatabaseProfiler?: boolean;
supportsIncrementalAnalysis?: boolean;
requiresDefaultSortCriteria?: boolean;
profilerFormatterFunction?: string;
profilerTimestampFunction?: string;
-2
View File
@@ -96,6 +96,4 @@ export type TestEngineInfo = {
}>;
objects?: Array<TestObjectInfo>;
binaryDataType?: string;
};
+1 -1
View File
@@ -32,7 +32,7 @@
"chartjs-plugin-datalabels": "^2.2.0",
"cross-env": "^7.0.3",
"dbgate-datalib": "^6.0.0-alpha.1",
"dbgate-query-splitter": "^4.11.9",
"dbgate-query-splitter": "^4.11.7",
"dbgate-sqltree": "^6.0.0-alpha.1",
"dbgate-tools": "^6.0.0-alpha.1",
"dbgate-types": "^6.0.0-alpha.1",
+2 -8
View File
@@ -27,7 +27,7 @@
import SettingsListener from './utility/SettingsListener.svelte';
import { handleAuthOnStartup } from './clientAuth';
import { initializeAppUpdates } from './utility/appUpdate';
import { _t, getCurrentTranslations, saveSelectedLanguageToCache } from './translations';
import { _t, saveSelectedLanguageToCache } from './translations';
import { installCloudListeners } from './utility/cloudListeners';
export let isAdminPage = false;
@@ -61,13 +61,7 @@
initializeAppUpdates();
installCloudListeners();
refreshPublicCloudFiles();
saveSelectedLanguageToCache(config.preferrendLanguage);
const electron = getElectron();
if (electron) {
electron.send('translation-data', JSON.stringify(getCurrentTranslations()));
global.TRANSLATION_DATA = getCurrentTranslations();
}
saveSelectedLanguageToCache();
}
loadedApi = loadedApiValue;
@@ -53,15 +53,14 @@
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: _t('appFile.newFileName', { defaultMessage: 'New file name' }),
header: _t('appFile.renameFile', { defaultMessage: 'Rename file' }),
label: 'New file name',
header: 'Rename file',
onConfirm: newFile => {
apiCall('apps/rename-file', {
file: data.fileName,
@@ -75,7 +74,7 @@
const handleDelete = () => {
showModal(ConfirmModal, {
message: _t('appFile.deleteFileConfirm', { defaultMessage: 'Really delete file {fileName}?', values: { fileName: data.fileName } }),
message: `Really delete file ${data.fileName}?`,
onConfirm: () => {
apiCall('apps/delete-file', {
file: data.fileName,
@@ -102,10 +101,10 @@
function createMenu() {
return [
{ 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 },
{ 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 },
// data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile },
];
@@ -15,7 +15,6 @@
import InputTextModal from '../modals/InputTextModal.svelte';
import { apiCall } from '../utility/api';
import { useConnectionList } from '../utility/metadataLoaders';
import { _t } from '../translations';
export let data;
@@ -35,8 +34,8 @@
showModal(InputTextModal, {
value: name,
label: _t('appFolder.newApplicationName', { defaultMessage: 'New application name' }),
header: _t('appFolder.renameApplication', { defaultMessage: 'Rename application' }),
label: 'New application name',
header: 'Rename application',
onConfirm: async newFolder => {
await apiCall('apps/rename-folder', {
folder: data.name,
@@ -61,16 +60,16 @@
function createMenu() {
return [
{ text: _t('common.delete', { defaultMessage: 'Delete' }), onClick: handleDelete },
{ text: _t('common.rename', { defaultMessage: 'Rename' }), onClick: handleRename },
{ text: 'Delete', onClick: handleDelete },
{ text: 'Rename', onClick: handleRename },
$currentDatabase && [
!isOnCurrentDb($currentDatabase, $connections) && {
text: _t('appFolder.enableOnCurrentDatabase', { defaultMessage: 'Enable on current database' }),
text: 'Enable on current database',
onClick: () => setOnCurrentDb(true),
},
isOnCurrentDb($currentDatabase, $connections) && {
text: _t('appFolder.disableOnCurrentDatabase', { defaultMessage: 'Disable on current database' }),
text: 'Disable on current database',
onClick: () => setOnCurrentDb(false),
},
],
@@ -91,7 +90,7 @@
title={data.name}
icon={'img app'}
statusIcon={isOnCurrentDb($currentDatabase, $connections) ? 'icon check' : null}
statusTitle={_t('appFolder.applicationUsedForDatabase', { defaultMessage: 'Application {application} is used for database {database}', values: { application: data.name, database: $currentDatabase?.name } })}
statusTitle={`Application ${data.name} is used for database ${$currentDatabase?.name}`}
isBold={data.name == $currentApplication}
on:click={() => ($currentApplication = data.name)}
menu={createMenu}
@@ -77,7 +77,7 @@
</div>
{/if}
<div on:drop={handleDrop} data-testid={`app-object-group-items-${_.kebabCase(group)}`}>
<div on:drop={handleDrop}>
{#each items as item}
<AppObjectListItem
isHidden={!item.isMatched}
+4 -5
View File
@@ -8,7 +8,6 @@
import Link from '../elements/Link.svelte';
import { focusedConnectionOrDatabase } from '../stores';
import { tick } from 'svelte';
import { _tval } from '../translations';
export let list;
export let module;
@@ -41,12 +40,12 @@
$: listTranslated = (list || []).map(data => ({
...data,
group: data?.group && _tval(data.group),
title: data?.title && _tval(data.title),
description: data?.description && _tval(data.description),
group: data?.group && _.isFunction(data.group) ? data.group() : data.group,
title: data?.title && _.isFunction(data.title) ? data.title() : data.title,
description: data?.description && _.isFunction(data.description) ? data.description() : data.description,
args: (data?.args || []).map(x => ({
...x,
label: x?.label && _tval(x.label),
label: x?.label && _.isFunction(x.label) ? x.label() : x.label,
})),
}));
@@ -82,7 +82,6 @@
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');
@@ -90,8 +89,8 @@
const handleRename = () => {
showModal(InputTextModal, {
value: data.fileName,
label: _t('archiveFile.newFileName', { defaultMessage: 'New file name' }),
header: _t('archiveFile.renameFile', { defaultMessage: 'Rename file' }),
label: 'New file name',
header: 'Rename file',
onConfirm: newFile => {
apiCall('archive/rename-file', {
file: data.fileName,
@@ -105,7 +104,7 @@
const handleDelete = () => {
showModal(ConfirmModal, {
message: _t('archiveFile.deleteFileConfirm', { defaultMessage: 'Really delete file {fileName}?', values: { fileName: data.fileName } }),
message: `Really delete file ${data.fileName}?`,
onConfirm: () => {
apiCall('archive/delete-file', {
file: data.fileName,
@@ -148,10 +147,10 @@
}
return [
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' && { 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' &&
createQuickExportMenu(
fmt => async () => {
@@ -186,19 +185,19 @@
},
}
),
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 },
data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile },
data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile },
!isZipped &&
isProApp() &&
data.fileType == 'jsonl' && {
text: _t('common.openInProfiler', { defaultMessage: 'Open in profiler' }),
text: 'Open in profiler',
submenu: getExtensions()
.drivers.filter(eng => eng.profilerFormatterFunction)
.map(eng => ({
text: eng.title,
onClick: () => {
openNewTab({
title: _t('common.profiler', { defaultMessage: 'Profiler' }),
title: 'Profiler',
icon: 'img profiler',
tabComponent: 'ProfilerTab',
props: {
@@ -21,15 +21,14 @@
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')
? _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 } }),
? `Really delete link to folder ${data.name}? Folder content remains untouched.`
: `Really delete folder ${data.name}?`,
onConfirm: () => {
apiCall('archive/delete-folder', { folder: data.name });
},
@@ -43,8 +42,8 @@
showModal(InputTextModal, {
value: name,
label: _t('archiveFolder.newFolderName', { defaultMessage: 'New folder name' }),
header: _t('archiveFolder.renameFolder', { defaultMessage: 'Rename folder' }),
label: 'New folder name',
header: 'Rename folder',
onConfirm: async newFolder => {
await apiCall('archive/rename-folder', {
folder: data.name,
@@ -96,7 +95,7 @@ await dbgateApi.deployDb(${JSON.stringify(
const handleCompareWithCurrentDb = () => {
openNewTab(
{
title: _t('common.compare', { defaultMessage: 'Compare' }),
title: 'Compare',
icon: 'img compare',
tabComponent: 'CompareModelTab',
props: {
@@ -154,7 +153,7 @@ await dbgateApi.deployDb(${JSON.stringify(
});
},
{
formatLabel: _t('common.zipFiles', { defaultMessage: 'ZIP files' }),
formatLabel: 'ZIP files',
formatExtension: 'zip',
defaultFileName: data.name?.endsWith('.zip') ? data.name : data.name + '.zip',
}
@@ -163,28 +162,28 @@ await dbgateApi.deployDb(${JSON.stringify(
function createMenu() {
return [
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 },
data.name != 'default' && { text: 'Delete', onClick: handleDelete },
data.name != 'default' && { text: 'Rename', onClick: handleRename },
isProApp() && { text: 'Data deployer', onClick: handleOpenDataDeployTab },
$currentDatabase && [
{ 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 },
{ text: 'Generate deploy DB SQL', onClick: handleGenerateDeploySql },
hasPermission(`run-shell-script`) && { text: 'Shell: Deploy DB', onClick: handleGenerateDeployScript },
],
data.name != 'default' &&
isProApp() &&
data.name.endsWith('.zip') && { text: _t('archiveFolder.unpackZip', { defaultMessage: 'Unpack ZIP' }), onClick: () => handleZipUnzip('archive/unzip') },
data.name.endsWith('.zip') && { text: 'Unpack ZIP', onClick: () => handleZipUnzip('archive/unzip') },
data.name != 'default' &&
isProApp() &&
!data.name.endsWith('.zip') && { text: _t('archiveFolder.packZip', { defaultMessage: 'Pack (create ZIP)' }), onClick: () => handleZipUnzip('archive/zip') },
!data.name.endsWith('.zip') && { text: 'Pack (create ZIP)', onClick: () => handleZipUnzip('archive/zip') },
isProApp() && { text: _t('archiveFolder.downloadZip', { defaultMessage: 'Download ZIP' }), onClick: handleDownloadZip },
isProApp() && { text: 'Download ZIP', onClick: handleDownloadZip },
data.name != 'default' &&
hasPermission('dbops/model/compare') &&
isProApp() &&
_.get($currentDatabase, 'connection._id') && {
onClick: handleCompareWithCurrentDb,
text: _t('archiveFolder.compareWithCurrentDb', { defaultMessage: 'Compare with {name}', values: { name: _.get($currentDatabase, 'name') } }),
text: `Compare with ${_.get($currentDatabase, 'name')}`,
},
];
}
@@ -407,8 +407,8 @@ await dbgateApi.executeQuery(${JSON.stringify(
const handleCreateNewApp = () => {
showModal(InputTextModal, {
header: _t('database.newApplication', { defaultMessage: 'New application' }),
label: _t('database.applicationName', { defaultMessage: 'Application name' }),
header: 'New application',
label: 'Application name',
value: _.startCase(name),
onConfirm: async appName => {
const newAppId = await apiCall('apps/create-app-from-db', {
@@ -446,7 +446,8 @@ await dbgateApi.executeQuery(${JSON.stringify(
driver?.databaseEngineTypes?.includes('document') && {
onClick: handleNewCollection,
text: _t('database.newCollection', {
defaultMessage: 'New collection/container'
defaultMessage: 'New {collectionLabel}',
values: { collectionLabel: driver?.collectionSingularLabel ?? 'collection/container' },
}),
},
hasPermission(`dbops/query`) &&
@@ -1,7 +1,5 @@
<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 =
@@ -71,13 +69,13 @@
function createScriptTemplatesSubmenu(objectTypeField) {
return {
label: _t('dbObject.sqlTemplate', { defaultMessage: 'SQL template' }),
label: 'SQL template',
submenu: getSupportedScriptTemplates(objectTypeField),
};
}
interface DbObjMenuItem {
label?: string | DefferedTranslationResult;
label?: string;
tab?: string;
forceNewTab?: boolean;
initialData?: any;
@@ -89,8 +87,7 @@
isRename?: boolean;
isTruncate?: boolean;
isCopyTableName?: boolean;
isTableBackup?: boolean;
isTableRestore?: boolean;
isDuplicateTable?: boolean;
isDiagram?: boolean;
functionName?: string;
isExport?: boolean;
@@ -108,8 +105,6 @@
}
function createMenusCore(objectTypeField, driver, data): DbObjMenuItem[] {
const backupMatch = data.objectTypeField === 'tables' ? data.pureName.match(TABLE_BACKUP_REGEX) : null;
switch (objectTypeField) {
case 'tables':
return [
@@ -118,19 +113,19 @@
divider: true,
},
isProApp() && {
label: _t('dbObject.designQuery', { defaultMessage: 'Design query' }),
label: 'Design query',
isQueryDesigner: true,
requiresWriteAccess: true,
},
isProApp() && {
label: _t('dbObject.designPerspectiveQuery', { defaultMessage: 'Design perspective query' }),
label: 'Design perspective query',
tab: 'PerspectiveTab',
forceNewTab: true,
icon: 'img perspective',
},
createScriptTemplatesSubmenu('tables'),
{
label: _t('dbObject.sqlGenerator', { defaultMessage: 'SQL generator' }),
label: 'SQL generator',
submenu: [
{
label: 'CREATE TABLE',
@@ -159,52 +154,45 @@
divider: true,
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.dropTable', { defaultMessage: 'Drop table' }),
label: 'Drop table',
isDrop: true,
requiresWriteAccess: true,
},
hasPermission('dbops/table/rename') &&
!driver?.dialect.disableRenameTable && {
label: _t('dbObject.renameTable', { defaultMessage: 'Rename table' }),
label: 'Rename table',
isRename: true,
requiresWriteAccess: true,
},
hasPermission('dbops/table/truncate') && {
label: _t('dbObject.truncateTable', { defaultMessage: 'Truncate table' }),
label: 'Truncate table',
isTruncate: true,
requiresWriteAccess: true,
},
{
label: _t('dbObject.copyTableName', { defaultMessage: 'Copy table name' }),
label: 'Copy table name',
isCopyTableName: true,
requiresWriteAccess: false,
},
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/table/backup') && {
label: 'Create table backup',
isDuplicateTable: true,
requiresWriteAccess: true,
},
hasPermission('dbops/model/view') && {
label: _t('dbObject.showDiagram', { defaultMessage: 'Show diagram' }),
label: 'Show diagram',
isDiagram: true,
},
{
divider: true,
},
hasPermission('dbops/export') && {
label: _t('common.export', { defaultMessage: 'Export' }),
label: 'Export',
functionName: 'tableReader',
isExport: true,
},
hasPermission('dbops/import') && {
label: _t('common.import', { defaultMessage: 'Import' }),
label: 'Import',
isImport: true,
requiresWriteAccess: true,
},
@@ -216,18 +204,18 @@
divider: true,
},
isProApp() && {
label: _t('dbObject.designQuery', { defaultMessage: 'Design query' }),
label: 'Design query',
isQueryDesigner: true,
},
isProApp() && {
label: _t('dbObject.designPerspectiveQuery', { defaultMessage: 'Design perspective query' }),
label: 'Design perspective query',
tab: 'PerspectiveTab',
forceNewTab: true,
icon: 'img perspective',
},
createScriptTemplatesSubmenu('views'),
{
label: _t('dbObject.sqlGenerator', { defaultMessage: 'SQL generator' }),
label: 'SQL generator',
submenu: [
{
label: 'CREATE VIEW',
@@ -247,12 +235,12 @@
divider: true,
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.dropView', { defaultMessage: 'Drop view' }),
label: 'Drop view',
isDrop: true,
requiresWriteAccess: true,
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.renameView', { defaultMessage: 'Rename view' }),
label: 'Rename view',
isRename: true,
requiresWriteAccess: true,
},
@@ -260,7 +248,7 @@
divider: true,
},
{
label: _t('common.export', { defaultMessage: 'Export' }),
label: 'Export',
isExport: true,
functionName: 'tableReader',
},
@@ -272,12 +260,12 @@
divider: true,
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.dropView', { defaultMessage: 'Drop view' }),
label: 'Drop view',
isDrop: true,
requiresWriteAccess: true,
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.renameView', { defaultMessage: 'Rename view' }),
label: 'Rename view',
isRename: true,
requiresWriteAccess: true,
},
@@ -285,12 +273,12 @@
divider: true,
},
{
label: _t('dbObject.queryDesigner', { defaultMessage: 'Query designer' }),
label: 'Query designer',
isQueryDesigner: true,
},
createScriptTemplatesSubmenu('matviews'),
{
label: _t('dbObject.sqlGenerator', { defaultMessage: 'SQL generator' }),
label: 'SQL generator',
submenu: [
{
label: 'CREATE MATERIALIZED VIEW',
@@ -310,7 +298,7 @@
divider: true,
},
{
label: _t('common.export', { defaultMessage: 'Export' }),
label: 'Export',
isExport: true,
functionName: 'tableReader',
},
@@ -318,7 +306,7 @@
case 'queries':
return [
{
label: _t('dbObject.openData', { defaultMessage: 'Open data' }),
label: 'Open data',
tab: 'QueryDataTab',
forceNewTab: true,
},
@@ -330,18 +318,18 @@
divider: true,
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.dropProcedure', { defaultMessage: 'Drop procedure' }),
label: 'Drop procedure',
isDrop: true,
requiresWriteAccess: true,
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.renameProcedure', { defaultMessage: 'Rename procedure' }),
label: 'Rename procedure',
isRename: true,
requiresWriteAccess: true,
},
createScriptTemplatesSubmenu('procedures'),
{
label: _t('dbObject.sqlGenerator', { defaultMessage: 'SQL generator' }),
label: 'SQL generator',
submenu: [
{
label: 'CREATE PROCEDURE',
@@ -364,7 +352,7 @@
return [
...defaultDatabaseObjectAppObjectActions['triggers'],
hasPermission('dbops/model/edit') && {
label: _t('dbObject.dropTrigger', { defaultMessage: 'Drop trigger' }),
label: 'Drop trigger',
isDrop: true,
requiresWriteAccess: true,
},
@@ -372,7 +360,7 @@
divider: true,
},
{
label: _t('dbObject.sqlGenerator', { defaultMessage: 'SQL generator' }),
label: 'SQL generator',
submenu: [
{
label: 'CREATE TRIGGER',
@@ -396,28 +384,28 @@
divider: true,
},
isProApp() && {
label: _t('dbObject.designPerspectiveQuery', { defaultMessage: 'Design perspective query' }),
label: 'Design perspective query',
tab: 'PerspectiveTab',
forceNewTab: true,
icon: 'img perspective',
},
hasPermission('dbops/export') && {
label: _t('common.export', { defaultMessage: 'Export' }),
label: 'Export',
isExport: true,
functionName: 'tableReader',
},
hasPermission('dbops/model/edit') && {
label: _t('dbObject.dropCollection', { defaultMessage: 'Drop collection/container' }),
label: `Drop ${driver?.collectionSingularLabel ?? 'collection/container'}`,
isDropCollection: true,
requiresWriteAccess: true,
},
hasPermission('dbops/table/rename') && {
label: _t('dbObject.renameCollection', { defaultMessage: 'Rename collection/container' }),
label: `Rename ${driver?.collectionSingularLabel ?? 'collection/container'}`,
isRenameCollection: true,
requiresWriteAccess: true,
},
hasPermission('dbops/table/backup') && {
label: _t('dbObject.createCollectionBackup', { defaultMessage: 'Create collection/container backup' }),
label: `Create ${driver?.collectionSingularLabel ?? 'collection/container'} backup`,
isDuplicateCollection: true,
requiresWriteAccess: true,
},
@@ -430,7 +418,7 @@
const menu: DbObjMenuItem[] = [
...defaultDatabaseObjectAppObjectActions['schedulerEvents'],
hasPermission('dbops/model/edit') && {
label: _t('dbObject.dropEvent', { defaultMessage: 'Drop event' }),
label: 'Drop event',
isDrop: true,
requiresWriteAccess: true,
},
@@ -438,12 +426,12 @@
if (data?.status === 'ENABLED') {
menu.push({
label: _t('dbObject.disable', { defaultMessage: 'Disable' }),
label: 'Disable',
isDisableEvent: true,
});
} else {
menu.push({
label: _t('dbObject.enable', { defaultMessage: 'Enable' }),
label: 'Enable',
isEnableEvent: true,
});
}
@@ -453,7 +441,7 @@
divider: true,
},
{
label: _t('dbObject.sqlGenerator', { defaultMessage: 'SQL generator' }),
label: 'SQL generator',
submenu: [
{
label: 'CREATE SCHEDULER EVENT',
@@ -486,7 +474,7 @@
if (menu.isQueryDesigner) {
openNewTab(
{
title: _t('dbObject.query', { defaultMessage: 'Query #' }),
title: 'Query #',
icon: 'img query-design',
tabComponent: 'QueryDesignTab',
focused: true,
@@ -511,7 +499,7 @@
} else if (menu.isDiagram) {
openNewTab(
{
title: _t('dbObject.diagram', { defaultMessage: 'Diagram #' }),
title: 'Diagram #',
icon: 'img diagram',
tabComponent: 'DiagramTab',
props: {
@@ -601,10 +589,7 @@
});
} else if (menu.isDropCollection) {
showModal(ConfirmModal, {
message: _t('dbObject.confirmDropCollection', {
defaultMessage: 'Really drop collection {name}?',
values: { name: data.pureName },
}),
message: `Really drop collection ${data.pureName}?`,
onConfirm: async () => {
const dbid = _.pick(data, ['conid', 'database']);
runOperationOnDatabase(dbid, {
@@ -618,8 +603,8 @@
} else if (menu.isRenameCollection) {
const driver = await getDriver();
showModal(InputTextModal, {
label: _t('dbObject.newCollectionName', { defaultMessage: 'New collection/container name' }),
header: _t('dbObject.renameCollection', { defaultMessage: 'Rename collection/container' }),
label: `New ${driver?.collectionSingularLabel ?? 'collection/container'} name`,
header: `Rename ${driver?.collectionSingularLabel ?? 'collection/container'}`,
value: data.pureName,
onConfirm: async newName => {
const dbid = _.pick(data, ['conid', 'database']);
@@ -635,10 +620,7 @@
const driver = await getDriver();
showModal(ConfirmModal, {
message: _t('dbObject.confirmCloneCollection', {
defaultMessage: 'Really create collection/container copy named {name}?',
values: { name: newName },
}),
message: `Really create ${driver?.collectionSingularLabel ?? 'collection/container'} copy named ${newName}?`,
onConfirm: async () => {
const dbid = _.pick(data, ['conid', 'database']);
runOperationOnDatabase(dbid, {
@@ -648,7 +630,7 @@
});
},
});
} else if (menu.isTableBackup) {
} else if (menu.isDuplicateTable) {
const driver = await getDriver();
const dmp = driver.createDumper();
const newTable = _.cloneDeep(data);
@@ -682,25 +664,6 @@
},
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({
@@ -754,9 +717,8 @@
const coreMenus = createMenusCore(objectTypeField, driver, data);
const filteredSumenus = coreMenus.map(item => {
if (!item) return item;
if (!item.submenu) {
return { ...item, label: _tval(item.label) };
return item;
}
return {
...item,
@@ -772,7 +734,7 @@
};
});
const filteredNoEmptySubmenus = _.compact(filteredSumenus).filter(x => !x.submenu || x.submenu.length > 0);
const filteredNoEmptySubmenus = filteredSumenus.filter(x => !x.submenu || x.submenu.length > 0);
return filteredNoEmptySubmenus;
}
@@ -806,9 +768,7 @@
openNewTab(
{
// title: getObjectTitle(connection, schemaName, pureName),
title: tabComponent
? getObjectTitle(connection, schemaName, pureName)
: _t('dbObject.query', { defaultMessage: 'Query #' }),
title: tabComponent ? getObjectTitle(connection, schemaName, pureName) : 'Query #',
focused: tabComponent == null,
tooltip,
icon:
@@ -1037,8 +997,6 @@
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">
@@ -1056,7 +1014,7 @@
} from '../stores';
import openNewTab from '../utility/openNewTab';
import { extractDbNameFromComposite, filterNameCompoud, getConnectionLabel } from 'dbgate-tools';
import { getConnectionInfo, getDatabaseInfo } from '../utility/metadataLoaders';
import { getConnectionInfo } from '../utility/metadataLoaders';
import fullDisplayName from '../utility/fullDisplayName';
import { showModal } from '../modals/modalTools';
import { findEngineDriver } from 'dbgate-tools';
@@ -1077,9 +1035,6 @@
import { getSupportedScriptTemplates } from '../utility/applyScriptTemplate';
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;
@@ -1108,9 +1063,6 @@
if (data.tableRowCount != null) {
res.push(`${formatRowCount(data.tableRowCount)} rows`);
}
if (data.sizeBytes) {
res.push(formatFileSize(data.sizeBytes));
}
if (data.tableEngine) {
res.push(data.tableEngine);
}
@@ -1119,21 +1071,14 @@
}
$: 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={backupTitle ??
(data.schemaName && !passProps?.hideSchemaName ? `${data.schemaName}.${data.pureName}` : data.pureName)}
icon={backupParsed ? 'img table-backup' : databaseObjectIcons[data.objectTypeField]}
title={data.schemaName && !passProps?.hideSchemaName ? `${data.schemaName}.${data.pureName}` : data.pureName}
icon={databaseObjectIcons[data.objectTypeField]}
menu={createMenu}
showPinnedInsteadOfUnpin={passProps?.showPinnedInsteadOfUnpin}
onPin={passProps?.ingorePin ? null : isPinned ? null : () => pinnedTables.update(list => [...list, data])}
@@ -193,7 +193,6 @@
import { saveFileToDisk } from '../utility/exportFileTools';
import { getConnectionInfo } from '../utility/metadataLoaders';
import { showSnackbarError } from '../utility/snackbar';
import { _t } from '../translations';
export let data;
@@ -215,26 +214,27 @@
function createMenu() {
return [
handler?.tabComponent && { text: _t('common.open', { defaultMessage: 'Open' }), onClick: openTab },
handler?.tabComponent && { text: 'Open', onClick: openTab },
!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 && 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 &&
data.allowRead &&
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 },
hasPermission('all-team-files/create') && { text: 'Create copy', onClick: handleCopy },
data.teamFileId && data.allowWrite && { text: 'Delete', onClick: handleDelete },
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 },
folder == 'markdown' && { text: 'Show page', onClick: showMarkdownPage },
!data.teamFileId && { text: 'Download', onClick: handleDownload },
data.teamFileId && data.allowRead && { text: 'Download', onClick: handleDownload },
];
}
const handleDelete = () => {
showModal(ConfirmModal, {
message: _t('common.reallyDeleteFile', { defaultMessage: 'Really delete file {file}?', values: { file: data.file } }),
message: `Really delete 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: _t('common.newFileName', { defaultMessage: 'New file name' }),
header: _t('common.renameFile', { defaultMessage: 'Rename file' }),
label: 'New file name',
header: '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: _t('savedFile.newFileName', { defaultMessage: 'New file name' }),
header: _t('savedFile.copyFile', { defaultMessage: 'Copy file' }),
label: 'New file name',
header: '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(_t('savedFile.noPermissionUseTeamFile', { defaultMessage: 'You do not have permission to use this team file' }));
showSnackbarError('You do not have permission to use this team file');
return;
}
} else {
if (!data.allowRead) {
showSnackbarError(_t('savedFile.noPermissionReadTeamFile', { defaultMessage: 'You do not have permission to read this team file' }));
showSnackbarError('You do not have permission to read this team file');
return;
}
}
+11 -12
View File
@@ -1,4 +1,3 @@
import { __t } from '../translations';
export function matchDatabaseObjectAppObject(obj1, obj2) {
return (
obj1?.objectTypeField == obj2?.objectTypeField &&
@@ -12,12 +11,12 @@ export function matchDatabaseObjectAppObject(obj1, obj2) {
function getTableLikeActions(dataTab) {
return [
{
label: __t('dbObject.openData', { defaultMessage: 'Open data' }),
label: 'Open data',
tab: dataTab,
defaultActionId: 'openTable',
},
{
label: __t('dbObject.openRawData', { defaultMessage: 'Open raw data' }),
label: 'Open raw data',
tab: dataTab,
defaultActionId: 'openRawTable',
isRawMode: true,
@@ -34,13 +33,13 @@ function getTableLikeActions(dataTab) {
// defaultActionId: 'openForm',
// },
{
label: __t('dbObject.openStructure', { defaultMessage: 'Open structure' }),
label: 'Open structure',
tab: 'TableStructureTab',
icon: 'img table-structure',
defaultActionId: 'openStructure',
},
{
label: __t('dbObject.showSql', { defaultMessage: 'Show SQL' }),
label: 'Show SQL',
tab: 'SqlObjectTab',
defaultActionId: 'showSql',
icon: 'img sql-file',
@@ -54,7 +53,7 @@ export const defaultDatabaseObjectAppObjectActions = {
matviews: getTableLikeActions('ViewDataTab'),
procedures: [
{
label: __t('dbObject.showSql', { defaultMessage: 'Show SQL' }),
label: 'Show SQL',
tab: 'SqlObjectTab',
defaultActionId: 'showSql',
icon: 'img sql-file',
@@ -62,7 +61,7 @@ export const defaultDatabaseObjectAppObjectActions = {
],
functions: [
{
label: __t('dbObject.showSql', { defaultMessage: 'Show SQL' }),
label: 'Show SQL',
tab: 'SqlObjectTab',
defaultActionId: 'showSql',
icon: 'img sql-file',
@@ -70,7 +69,7 @@ export const defaultDatabaseObjectAppObjectActions = {
],
triggers: [
{
label: __t('dbObject.showSql', { defaultMessage: 'Show SQL' }),
label: 'Show SQL',
tab: 'SqlObjectTab',
defaultActionId: 'showSql',
icon: 'img sql-file',
@@ -78,12 +77,12 @@ export const defaultDatabaseObjectAppObjectActions = {
],
collections: [
{
label: __t('dbObject.openData', { defaultMessage: 'Open data' }),
label: 'Open data',
tab: 'CollectionDataTab',
defaultActionId: 'openTable',
},
{
label: __t('dbObject.openJson', { defaultMessage: 'Open JSON' }),
label: 'Open JSON',
tab: 'CollectionDataTab',
defaultActionId: 'openJson',
initialData: {
@@ -95,7 +94,7 @@ export const defaultDatabaseObjectAppObjectActions = {
],
schedulerEvents: [
{
label: __t('dbObject.showSql', { defaultMessage: 'Show SQL' }),
label: 'Show SQL',
tab: 'SqlObjectTab',
defaultActionId: 'showSql',
icon: 'img sql-file',
@@ -103,7 +102,7 @@ export const defaultDatabaseObjectAppObjectActions = {
],
queries: [
{
label: __t('dbObject.showQuery', { defaultMessage: 'Show query' }),
label: 'Show query',
tab: 'QueryDataTab',
defaultActionId: 'showAppQuery',
icon: 'img app-query',
@@ -5,7 +5,6 @@
import getElectron from '../utility/getElectron';
import InlineButtonLabel from '../buttons/InlineButtonLabel.svelte';
import resolveApi, { resolveApiHeaders } from '../utility/resolveApi';
import { _t } from '../translations';
import uuidv1 from 'uuid/v1';
@@ -50,11 +49,11 @@
</script>
{#if electron}
<InlineButton on:click={handleOpenElectronFile} title={_t('files.openFile', { defaultMessage: "Open file" })} data-testid={$$props['data-testid']}>
<InlineButton on:click={handleOpenElectronFile} title="Open file" data-testid={$$props['data-testid']}>
<FontIcon {icon} />
</InlineButton>
{:else}
<InlineButtonLabel on:click={() => {}} title={_t('files.uploadFile', { defaultMessage: "Upload file" })} data-testid={$$props['data-testid']} htmlFor={inputId}>
<InlineButtonLabel on:click={() => {}} title="Upload file" data-testid={$$props['data-testid']} htmlFor={inputId}>
<FontIcon {icon} />
</InlineButtonLabel>
{/if}
@@ -1,6 +1,6 @@
<script context="module">
function getCommandTitle(command) {
let res = _tval(command.text);
let res = _.isFunction(command.text) ? command.text() : command.text;
if (command.keyText || command.keyTextFromGroup) {
res += ` (${formatKeyText(command.keyText || command.keyTextFromGroup)})`;
}
@@ -13,7 +13,6 @@
import { formatKeyText } from '../utility/common';
import ToolStripButton from './ToolStripButton.svelte';
import _ from 'lodash';
import { _tval } from '../translations';
export let command;
export let component = ToolStripButton;
@@ -34,6 +33,6 @@
{iconAfter}
{...$$restProps}
>
{(_tval(buttonLabel) || _tval(cmd?.toolbarName) || _tval(cmd?.name))}
{(_.isFunction(buttonLabel) ? buttonLabel() : buttonLabel) || (_.isFunction(cmd?.toolbarName) ? cmd.toolbarName() : cmd.toolbarName) || (_.isFunction(cmd?.name) ? cmd.name() : cmd.name)}
</svelte:component>
{/if}
@@ -24,7 +24,6 @@
import ToolStripCommandButton from './ToolStripCommandButton.svelte';
import ToolStripDropDownButton from './ToolStripDropDownButton.svelte';
import _ from 'lodash';
import { _tval } from '../translations';
export let quickExportHandlerRef = null;
export let command = 'sqlDataGrid.export';
export let label = 'Export';
@@ -40,7 +39,7 @@
{#if hasPermission('dbops/export')}
{#if quickExportHandlerRef}
<ToolStripDropDownButton menu={getExportMenu} label={_tval(label)} icon="icon export" />
<ToolStripDropDownButton menu={getExportMenu} label={_.isFunction(label) ? label() : label} icon="icon export" />
{:else}
<ToolStripCommandButton {command} />
{/if}
+1 -2
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import FormStyledButtonLikeLabel from '../buttons/FormStyledButtonLikeLabel.svelte';
import uploadFiles from '../utility/uploadFiles';
import { _t } from '../translations';
const handleChange = e => {
const files = [...e.target.files];
@@ -10,6 +9,6 @@
</script>
<div class="m-1">
<FormStyledButtonLikeLabel htmlFor="uploadFileButton">{_t('files.uploadFile', { defaultMessage: "Upload file" })}</FormStyledButtonLikeLabel>
<FormStyledButtonLikeLabel htmlFor="uploadFileButton">Upload file</FormStyledButtonLikeLabel>
<input type="file" id="uploadFileButton" hidden on:change={handleChange} />
</div>
@@ -10,9 +10,6 @@
if (value?.type == 'Buffer' && _.isArray(value?.data)) {
return 'data:image/png;base64, ' + btoa(String.fromCharCode.apply(null, value?.data));
}
if (value?.$binary?.base64) {
return 'data:image/png;base64, ' + value.$binary.base64;
}
return null;
} catch (err) {
console.log('Error showing picture', err);
+17 -17
View File
@@ -1,9 +1,9 @@
<script context="module">
registerCommand({
id: 'commandPalette.show',
category: __t('command.commandPalette', { defaultMessage: 'Command palette' }),
name: __t('command.commandPalette.show', { defaultMessage: 'Show' }),
toolbarName: __t('command.commandPalette', { defaultMessage: 'Command palette' }),
category: 'Command palette',
name: 'Show',
toolbarName: 'Command palette',
toolbarOrder: 0,
keyText: 'F1',
toolbar: true,
@@ -15,9 +15,9 @@
registerCommand({
id: 'database.search',
category: __t('command.database', { defaultMessage: 'Database' }),
toolbarName: __t('command.database.databaseSearch', { defaultMessage: 'Database search' }),
name: __t('command.database.search', { defaultMessage: 'Search' }),
category: 'Database',
toolbarName: 'Database search',
name: 'Search',
keyText: isElectronAvailable() ? 'CtrlOrCommand+P' : 'F3',
onClick: () => visibleCommandPalette.set('database'),
testEnabled: () => getVisibleCommandPalette() != 'database',
@@ -81,7 +81,7 @@
import { getLocalStorage } from '../utility/storageCache';
import registerCommand from './registerCommand';
import { formatKeyText, switchCurrentDatabase } from '../utility/common';
import { _tval, __t, _t } from '../translations';
import { _val } from '../translations';
let domInput;
let filter = '';
@@ -114,11 +114,11 @@
($visibleCommandPalette == 'database'
? extractDbItems($databaseInfo, { conid, database }, $connectionList)
: parentCommand
? parentCommand.getSubCommands()
: sortedComands
? parentCommand.getSubCommands()
: sortedComands
).filter(x => !x.isGroupCommand),
{
extract: x => _tval(x.text),
extract: x => _val(x.text),
pre: '<b>',
post: '</b>',
}
@@ -163,10 +163,10 @@
on:clickOutside={() => {
$visibleCommandPalette = null;
}}
data-testid="CommandPalette_main"
data-testid='CommandPalette_main'
>
<div
class="overlay"
<div
class="overlay"
on:click={() => {
$visibleCommandPalette = null;
}}
@@ -181,7 +181,7 @@
domInput.focus();
}}
>
<FontIcon icon="icon menu" /> {_t('commandPalette.commands', { defaultMessage: 'Commands' })}
<FontIcon icon="icon menu" /> Commands
</div>
<div
class="page"
@@ -191,7 +191,7 @@
domInput.focus();
}}
>
<FontIcon icon="icon database" /> {_t('common.database', { defaultMessage: 'Database' })}
<FontIcon icon="icon database" /> Database
</div>
</div>
<div class="mainInner">
@@ -201,8 +201,8 @@
bind:this={domInput}
bind:value={filter}
on:keydown={handleKeyDown}
placeholder={_tval(parentCommand?.text) ||
($visibleCommandPalette == 'database' ? _t('commandPalette.searchInDatabase', { defaultMessage: 'Search in database' }) : _t('commandPalette.searchInCommands', { defaultMessage: 'Search in commands' }))}
placeholder={parentCommand?.text ||
($visibleCommandPalette == 'database' ? 'Search in database' : 'Search in commands')}
/>
</div>
<div class="content">
@@ -1,16 +1,14 @@
import _ from 'lodash';
import { currentDatabase, getCurrentDatabase, getExtensions } from '../stores';
import { currentDatabase, getCurrentDatabase } from '../stores';
import getElectron from '../utility/getElectron';
import registerCommand from './registerCommand';
import { apiCall } from '../utility/api';
import { getDatabasStatusMenu, switchCurrentDatabase } from '../utility/common';
import { __t } from '../translations';
import { findEngineDriver } from 'dbgate-tools';
import { switchCurrentDatabase } from '../utility/common';
registerCommand({
id: 'database.changeState',
category: __t('command.database', { defaultMessage: 'Database' }),
name: __t('command.database.changeStatus', { defaultMessage: 'Change status' }),
category: 'Database',
name: 'Change status',
getSubCommands: () => {
const current = getCurrentDatabase();
if (!current) return [];
@@ -19,8 +17,33 @@ registerCommand({
conid: connection._id,
database: name,
};
const driver = findEngineDriver(connection, getExtensions());
return getDatabasStatusMenu(dbid, driver);
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);
},
},
];
},
});
@@ -3,7 +3,6 @@ import { recentDatabases, currentDatabase, getRecentDatabases } from '../stores'
import registerCommand from './registerCommand';
import { getConnectionLabel } from 'dbgate-tools';
import { switchCurrentDatabase } from '../utility/common';
import { __t } from '../translations';
currentDatabase.subscribe(value => {
if (!value) return;
@@ -25,9 +24,9 @@ function switchDatabaseCommand(db) {
registerCommand({
id: 'database.switch',
category: __t('command.database', { defaultMessage: 'Database' }),
name: __t('command.database.changeRecent', { defaultMessage: 'Change to recent' }),
menuName: __t('command.database.switchRecent', { defaultMessage: 'Switch recent database' }),
category: 'Database',
name: 'Change to recent',
menuName: 'Switch recent database',
keyText: 'CtrlOrCommand+D',
getSubCommands: () => getRecentDatabases().map(switchDatabaseCommand),
});
+10 -10
View File
@@ -1,7 +1,6 @@
import { commands } from '../stores';
import { invalidateCommandDefinitions } from './invalidateCommands';
import _ from 'lodash';
import { _tval, DefferedTranslationResult, isDefferedTranslationResult } from '../translations';
export interface SubCommand {
text: string;
@@ -10,10 +9,10 @@ export interface SubCommand {
export interface GlobalCommand {
id: string;
category: string | DefferedTranslationResult; // null for group commands
category: string | (() => string); // null for group commands
isGroupCommand?: boolean;
name: string | DefferedTranslationResult;
text?: string | DefferedTranslationResult;
name: string | (() => string);
text?: string | (() => string);
keyText?: string;
keyTextFromGroup?: string; // automatically filled from group
group?: string;
@@ -25,8 +24,8 @@ export interface GlobalCommand {
toolbar?: boolean;
enabled?: boolean;
showDisabled?: boolean;
toolbarName?: string | DefferedTranslationResult;
menuName?: string | DefferedTranslationResult;
toolbarName?: string | (() => string);
menuName?: string;
toolbarOrder?: number;
disableHandleKeyText?: string;
isRelatedToTab?: boolean;
@@ -44,10 +43,11 @@ export default function registerCommand(command: GlobalCommand) {
...x,
[command.id]: {
text:
isDefferedTranslationResult(command.category) || isDefferedTranslationResult(command.name)
? {
_transCallback: () => `${_tval(command.category)}: ${_tval(command.name)}`,
}
_.isFunction(command.category) || _.isFunction(command.name)
? () =>
`${_.isFunction(command.category) ? command.category() : command.category}: ${
_.isFunction(command.name) ? command.name() : command.name
}`
: `${command.category}: ${command.name}`,
...command,
enabled: !testEnabled,
+185 -240
View File
@@ -11,11 +11,11 @@ import {
promoWidgetPreview,
visibleToolbar,
visibleWidgetSideBar,
selectedWidget,
} from '../stores';
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';
@@ -40,6 +40,8 @@ import { getSettings } from '../utility/metadataLoaders';
import { isMac, switchCurrentDatabase } from '../utility/common';
import { doLogout } from '../clientAuth';
import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte';
import UploadErrorModal from '../modals/UploadErrorModal.svelte';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import NewCollectionModal from '../modals/NewCollectionModal.svelte';
import ConfirmModal from '../modals/ConfirmModal.svelte';
import localforage from 'localforage';
@@ -50,7 +52,6 @@ import { openWebLink } from '../utility/simpleTools';
import { _t } from '../translations';
import ExportImportConnectionsModal from '../modals/ExportImportConnectionsModal.svelte';
import { getBoolSettingsValue } from '../settings/settingsTools';
import { __t } from '../translations';
// function themeCommand(theme: ThemeDefinition) {
// return {
@@ -68,50 +69,42 @@ import { __t } from '../translations';
registerCommand({
id: 'theme.changeTheme',
category: __t('command.theme', { defaultMessage: 'Theme' }),
name: __t('command.theme.change', { defaultMessage: 'Change' }),
toolbarName: __t('command.theme.changeToolbar', { defaultMessage: 'Change theme' }),
onClick: () =>
openNewTab({
title: 'Settings',
icon: 'icon settings',
tabComponent: 'SettingsTab',
props: {
selectedItem: 'theme',
},
}),
category: 'Theme',
name: 'Change',
toolbarName: 'Change theme',
onClick: () => showModal(SettingsModal, { selectedTab: 'theme' }),
// getSubCommands: () => get(extensions).themes.map(themeCommand),
});
registerCommand({
id: 'toolbar.show',
category: __t('command.toolbar', { defaultMessage: 'Toolbar' }),
name: __t('command.toolbar.show', { defaultMessage: 'Show' }),
category: 'Toolbar',
name: 'Show',
onClick: () => visibleToolbar.set(true),
testEnabled: () => !getVisibleToolbar(),
});
registerCommand({
id: 'toolbar.hide',
category: __t('command.toolbar', { defaultMessage: 'Toolbar' }),
name: __t('command.toolbar.hide', { defaultMessage: 'Hide' }),
category: 'Toolbar',
name: 'Hide',
onClick: () => visibleToolbar.set(false),
testEnabled: () => getVisibleToolbar(),
});
registerCommand({
id: 'about.show',
category: __t('command.about', { defaultMessage: 'About' }),
name: __t('command.about.show', { defaultMessage: 'Show' }),
toolbarName: __t('command.about.toolbar', { defaultMessage: 'About' }),
category: 'About',
name: 'Show',
toolbarName: 'About',
onClick: () => showModal(AboutModal),
});
registerCommand({
id: 'toggle.sidebar',
category: __t('command.sidebar', { defaultMessage: 'Sidebar' }),
name: __t('command.sidebar.show', { defaultMessage: 'Show' }),
toolbarName: __t('command.sidebar.toggleToolbar', { defaultMessage: 'Toggle sidebar' }),
category: 'Sidebar',
name: 'Show',
toolbarName: 'Toggle sidebar',
keyText: 'CtrlOrCommand+B',
onClick: () => visibleWidgetSideBar.update(x => !x),
});
@@ -120,14 +113,14 @@ registerCommand({
id: 'new.connection',
toolbar: true,
icon: 'icon new-connection',
toolbarName: __t('command.new.connection', { defaultMessage: 'Add connection' }),
category: __t('command.new', { defaultMessage: 'New' }),
toolbarName: 'Add connection',
category: 'New',
toolbarOrder: 1,
name: __t('command.new.connection', { defaultMessage: 'Connection' }),
name: 'Connection',
testEnabled: () => !getCurrentConfig()?.runAsPortal && !getCurrentConfig()?.storageDatabase,
onClick: () => {
openNewTab({
title: _t('common.newConnection', { defaultMessage: 'New Connection' }),
title: 'New Connection',
icon: 'img connection',
tabComponent: 'ConnectionTab',
});
@@ -138,15 +131,15 @@ registerCommand({
id: 'new.connectionOnCloud',
toolbar: true,
icon: 'img cloud-connection',
toolbarName: __t('command.new.connection', { defaultMessage: 'Add connection' }),
category: __t('command.new', { defaultMessage: 'New' }),
toolbarName: 'Add connection',
category: 'New',
toolbarOrder: 1,
name: __t('command.new.connectionCloud', { defaultMessage: 'Connection on Cloud' }),
name: 'Connection on Cloud',
testEnabled: () =>
!getCurrentConfig()?.runAsPortal && !getCurrentConfig()?.storageDatabase && !!getCloudSigninTokenHolder(),
onClick: () => {
openNewTab({
title: _t('common.newConnectionCloud', { defaultMessage: 'New Connection on Cloud' }),
title: 'New Connection on Cloud',
icon: 'img cloud-connection',
tabComponent: 'ConnectionTab',
props: {
@@ -160,10 +153,10 @@ registerCommand({
id: 'new.connection.folder',
toolbar: true,
icon: 'icon add-folder',
toolbarName: __t('command.new.connectionFolderToolbar', { defaultMessage: 'Add connection folder' }),
category: __t('command.new', { defaultMessage: 'New' }),
toolbarName: 'Add connection folder',
category: 'New',
toolbarOrder: 1,
name: __t('command.new.connectionFolder', { defaultMessage: 'Connection folder' }),
name: 'Connection folder',
testEnabled: () => !getCurrentConfig()?.runAsPortal,
onClick: () => {
showModal(InputTextModal, {
@@ -183,22 +176,22 @@ registerCommand({
registerCommand({
id: 'new.query',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'icon file',
toolbar: true,
toolbarOrder: 2,
name: __t('command.new.query', { defaultMessage: 'Query' }),
toolbarName: __t('command.new.queryToolbar', { defaultMessage: 'New query' }),
name: 'Query',
toolbarName: 'New query',
keyText: 'CtrlOrCommand+T',
onClick: () => newQuery(),
});
registerCommand({
id: 'new.shell',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img shell',
name: __t('command.new.shell', { defaultMessage: 'JavaScript Shell' }),
menuName: __t('command.new.JSShell', { defaultMessage: 'New JavaScript shell' }),
name: 'JavaScript Shell',
menuName: 'New JavaScript shell',
onClick: () => {
openNewTab({
title: 'Shell #',
@@ -211,10 +204,10 @@ registerCommand({
if (isProApp()) {
registerCommand({
id: 'new.queryDesign',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img query-design',
name: __t('command.new.queryDesign', { defaultMessage: 'Query design' }),
menuName: __t('command.new.newQueryDesign', { defaultMessage: 'New query design' }),
name: 'Query design',
menuName: 'New query design',
onClick: () => newQueryDesign(),
testEnabled: () =>
getCurrentDatabase() &&
@@ -225,10 +218,10 @@ if (isProApp()) {
if (isProApp()) {
registerCommand({
id: 'new.modelTransform',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img transform',
name: __t('command.new.modelTransform', { defaultMessage: 'Model transform' }),
menuName: __t('command.new.newModelTransform', { defaultMessage: 'New model transform' }),
name: 'Model transform',
menuName: 'New model transform',
onClick: () => {
openNewTab(
{
@@ -269,10 +262,10 @@ if (isProApp()) {
if (isProApp()) {
registerCommand({
id: 'new.perspective',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img perspective',
name: __t('command.new.perspective', { defaultMessage: 'Perspective' }),
menuName: __t('command.new.newPerspective', { defaultMessage: 'New perspective' }),
name: 'Perspective',
menuName: 'New perspective',
onClick: () => newPerspective(),
});
}
@@ -280,10 +273,10 @@ if (isProApp()) {
if (isProApp()) {
registerCommand({
id: 'new.application',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img app',
name: __t('command.new.application', { defaultMessage: 'Application' }),
menuName: __t('command.new.newApplication', { defaultMessage: 'New application' }),
name: 'Application',
menuName: 'New application',
onClick: () => {
openNewTab({
title: 'Application #',
@@ -296,10 +289,10 @@ if (isProApp()) {
registerCommand({
id: 'new.diagram',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img diagram',
name: __t('command.new.diagram', { defaultMessage: 'ER Diagram' }),
menuName: __t('command.new.newDiagram', { defaultMessage: 'New ER diagram' }),
name: 'ER Diagram',
menuName: 'New ER diagram',
testEnabled: () =>
getCurrentDatabase() &&
findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('sql'),
@@ -308,9 +301,9 @@ registerCommand({
registerCommand({
id: 'new.archiveFolder',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img archive',
name: __t('command.new.archiveFolder', { defaultMessage: 'Archive folder' }),
name: 'Archive folder',
onClick: () => {
showModal(InputTextModal, {
value: '',
@@ -342,11 +335,11 @@ registerCommand({
registerCommand({
id: 'new.table',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'icon table',
name: __t('command.new.table', { defaultMessage: 'Table' }),
name: 'Table',
toolbar: true,
toolbarName: __t('command.new.tableToolbar', { defaultMessage: 'New table' }),
toolbarName: 'New table',
testEnabled: () => {
if (!hasPermission('dbops/model/edit')) return false;
const driver = findEngineDriver(get(currentDatabase)?.connection, getExtensions());
@@ -362,11 +355,11 @@ registerCommand({
registerCommand({
id: 'new.collection',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'icon table',
name: __t('command.new.collection', { defaultMessage: 'Collection' }),
name: 'Collection',
toolbar: true,
toolbarName: __t('command.new.collectionToolbar', { defaultMessage: 'New collection/container' }),
toolbarName: 'New collection/container',
testEnabled: () => {
const driver = findEngineDriver(get(currentDatabase)?.connection, getExtensions());
return !!get(currentDatabase) && driver?.databaseEngineTypes?.includes('document');
@@ -388,9 +381,9 @@ registerCommand({
registerCommand({
id: 'new.markdown',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img markdown',
name: __t('command.new.markdown', { defaultMessage: 'Markdown page' }),
name: 'Markdown page',
onClick: () => {
openNewTab({
title: 'Page #',
@@ -403,9 +396,9 @@ registerCommand({
if (isProApp()) {
registerCommand({
id: 'new.modelCompare',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'icon compare',
name: __t('command.new.modelCompare', { defaultMessage: 'Compare DB' }),
name: 'Compare DB',
toolbar: true,
onClick: () => {
openNewTab({
@@ -419,10 +412,10 @@ if (isProApp()) {
registerCommand({
id: 'new.jsonl',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img archive',
name: __t('command.new.jsonl', { defaultMessage: 'JSON Lines' }),
menuName: __t('command.new.newJsonl', { defaultMessage: 'New JSON lines file' }),
name: 'JSON Lines',
menuName: 'New JSON lines file',
onClick: () => {
openNewTab(
{
@@ -439,10 +432,10 @@ registerCommand({
registerCommand({
id: 'new.sqliteDatabase',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img sqlite-database',
name: __t('command.new.sqliteDatabase', { defaultMessage: 'SQLite database' }),
menuName: __t('command.new.sqliteDatabase', { defaultMessage: 'New SQLite database' }),
name: 'SQLite database',
menuName: _t('command.new.sqliteDatabase', { defaultMessage: 'New SQLite database' }),
onClick: () => {
showModal(InputTextModal, {
value: 'newdb',
@@ -459,10 +452,10 @@ registerCommand({
registerCommand({
id: 'new.duckdbDatabase',
category: __t('command.new', { defaultMessage: 'New' }),
category: 'New',
icon: 'img sqlite-database',
name: __t('command.new.duckdbDatabase', { defaultMessage: 'DuckDB database' }),
menuName: __t('command.new.duckdbDatabase', { defaultMessage: 'New DuckDB database' }),
name: 'DuckDB database',
menuName: _t('command.new.duckdbDatabase', { defaultMessage: 'New DuckDB database' }),
onClick: () => {
showModal(InputTextModal, {
value: 'newdb',
@@ -479,8 +472,8 @@ registerCommand({
registerCommand({
id: 'tabs.changelog',
category: __t('command.tabs', { defaultMessage: 'Tabs' }),
name: __t('command.tabs.changelog', { defaultMessage: 'Changelog' }),
category: 'Tabs',
name: 'Changelog',
onClick: () => {
openNewTab({
title: 'ChangeLog',
@@ -495,7 +488,7 @@ registerCommand({
id: 'group.save',
category: null,
isGroupCommand: true,
name: __t('command.save', { defaultMessage: 'Save' }),
name: 'Save',
keyText: 'CtrlOrCommand+S',
group: 'save',
});
@@ -504,7 +497,7 @@ registerCommand({
id: 'group.saveAs',
category: null,
isGroupCommand: true,
name: __t('command.saveAs', { defaultMessage: 'Save As' }),
name: 'Save As',
keyText: 'CtrlOrCommand+Shift+S',
group: 'saveAs',
});
@@ -513,7 +506,7 @@ registerCommand({
id: 'group.undo',
category: null,
isGroupCommand: true,
name: __t('command.undo', { defaultMessage: 'Undo' }),
name: 'Undo',
keyText: 'CtrlOrCommand+Z',
group: 'undo',
});
@@ -522,15 +515,15 @@ registerCommand({
id: 'group.redo',
category: null,
isGroupCommand: true,
name: __t('command.redo', { defaultMessage: 'Redo' }),
name: 'Redo',
keyText: 'CtrlOrCommand+Y',
group: 'redo',
});
registerCommand({
id: 'file.open',
category: __t('command.file', { defaultMessage: 'File' }),
name: __t('command.file.open', { defaultMessage: 'Open' }),
category: 'File',
name: 'Open',
keyText: 'CtrlOrCommand+O',
testEnabled: () => getElectron() != null,
onClick: openElectronFile,
@@ -538,39 +531,36 @@ registerCommand({
registerCommand({
id: 'file.openArchive',
category: __t('command.file', { defaultMessage: 'File' }),
name: __t('command.file.openArchive', { defaultMessage: 'Open DB Model/Archive' }),
category: 'File',
name: 'Open DB Model/Archive',
testEnabled: () => getElectron() != null,
onClick: openArchiveFolder,
});
registerCommand({
id: 'folder.showLogs',
category: __t('command.folder', { defaultMessage: 'Folder' }),
name: __t('command.folder.openLogs', { defaultMessage: 'Open logs' }),
category: 'Folder',
name: 'Open logs',
testEnabled: () => getElectron() != null,
onClick: () => electron.showItemInFolder(getCurrentConfig().logsFilePath),
});
registerCommand({
id: 'folder.showData',
category: __t('command.folder', { defaultMessage: 'Folder' }),
name: __t('command.folder.openData', { defaultMessage: 'Open data folder' }),
category: 'Folder',
name: 'Open data folder',
testEnabled: () => getElectron() != null,
onClick: () => electron.showItemInFolder(getCurrentConfig().connectionsFilePath),
});
registerCommand({
id: 'app.resetSettings',
category: __t('command.file', { defaultMessage: 'File' }),
name: __t('command.file.resetLayout', { defaultMessage: 'Reset layout data & settings' }),
category: 'File',
name: 'Reset layout data & settings',
testEnabled: () => true,
onClick: () => {
showModal(ConfirmModal, {
message: _t('command.file.resetLayoutConfirm', {
defaultMessage:
'Really reset layout data? All opened tabs, settings and layout data will be lost. Connections and saved files will be preserved. After this, restart DbGate for applying changes.',
}),
message: `Really reset layout data? All opened tabs, settings and layout data will be lost. Connections and saved files will be preserved. After this, restart DbGate for applying changes.`,
onConfirm: async () => {
await apiCall('config/delete-settings');
localStorage.clear();
@@ -587,8 +577,8 @@ registerCommand({
registerCommand({
id: 'app.exportConnections',
category: __t('command.settings', { defaultMessage: 'Settings' }),
name: __t('command.settings.exportConnections', { defaultMessage: 'Export connections' }),
category: 'Settings',
name: 'Export connections',
testEnabled: () => !getCurrentConfig()?.runAsPortal && !getCurrentConfig()?.storageDatabase,
onClick: () => {
showModal(ExportImportConnectionsModal, {
@@ -599,8 +589,8 @@ registerCommand({
registerCommand({
id: 'app.importConnections',
category: __t('command.settings', { defaultMessage: 'Settings' }),
name: __t('command.settings.importConnections', { defaultMessage: 'Import connections' }),
category: 'Settings',
name: 'Import connections',
testEnabled: () => !getCurrentConfig()?.runAsPortal && !getCurrentConfig()?.storageDatabase,
onClick: async () => {
const files = await electron.showOpenDialog({
@@ -625,8 +615,8 @@ registerCommand({
registerCommand({
id: 'file.import',
category: __t('command.file', { defaultMessage: 'File' }),
name: __t('command.file.import', { defaultMessage: 'Import data' }),
category: 'File',
name: 'Import data',
toolbar: true,
icon: 'icon import',
onClick: () =>
@@ -646,8 +636,8 @@ registerCommand({
registerCommand({
id: 'view.reset',
category: __t('command.view', { defaultMessage: 'View' }),
name: __t('command.view.reset', { defaultMessage: 'Reset view' }),
category: 'View',
name: 'Reset view',
onClick: () => {
const keys = [
'leftPanelWidth',
@@ -674,16 +664,14 @@ registerCommand({
'currentArchive',
];
for (const key of keys) removeLocalStorage(key);
showSnackbarSuccess(
_t('command.view.restart', { defaultMessage: 'Restart DbGate (or reload on web) for applying changes' })
);
showSnackbarSuccess('Restart DbGate (or reload on web) for applying changes');
},
});
registerCommand({
id: 'sql.generator',
category: __t('command.sql', { defaultMessage: 'SQL' }),
name: __t('command.sql.generator', { defaultMessage: 'SQL Generator' }),
category: 'SQL',
name: 'SQL Generator',
toolbar: true,
icon: 'icon sql-generator',
testEnabled: () =>
@@ -699,8 +687,8 @@ registerCommand({
registerCommand({
id: 'database.export',
category: __t('command.database', { defaultMessage: 'Database' }),
name: __t('command.database.export', { defaultMessage: 'Export database' }),
category: 'Database',
name: 'Export database',
toolbar: true,
icon: 'icon export',
testEnabled: () => getCurrentDatabase() != null && hasPermission(`dbops/export`) && isProApp(),
@@ -717,8 +705,8 @@ registerCommand({
if (isProApp()) {
registerCommand({
id: 'database.compare',
category: __t('command.database', { defaultMessage: 'Database' }),
name: __t('command.database.compare', { defaultMessage: 'Compare databases' }),
category: 'Database',
name: 'Compare databases',
toolbar: true,
icon: 'icon compare',
testEnabled: () =>
@@ -750,8 +738,8 @@ if (isProApp()) {
registerCommand({
id: 'database.chat',
category: __t('command.database', { defaultMessage: 'Database' }),
name: __t('command.database.chat', { defaultMessage: 'Database chat' }),
category: 'Database',
name: 'Database chat',
toolbar: true,
icon: 'icon ai',
testEnabled: () =>
@@ -773,26 +761,13 @@ if (isProApp()) {
}
if (hasPermission('settings/change')) {
registerCommand({
id: 'settings.settingsTab',
category: __t('command.settings', { defaultMessage: 'Settings' }),
name: __t('command.settings.settingsTab', { defaultMessage: 'Settings tab' }),
onClick: () => {
openNewTab({
title: _t('command.settings.settingsTab', { defaultMessage: 'Settings tab' }),
icon: 'icon settings',
tabComponent: 'SettingsTab',
props: {},
});
},
});
registerCommand({
id: 'settings.commands',
category: __t('command.settings', { defaultMessage: 'Settings' }),
name: __t('command.settings.shortcuts', { defaultMessage: 'Keyboard shortcuts' }),
category: 'Settings',
name: 'Keyboard shortcuts',
onClick: () => {
openNewTab({
title: _t('command.settings.shortcuts', { defaultMessage: 'Keyboard shortcuts' }),
title: 'Keyboard Shortcuts',
icon: 'icon keyboard',
tabComponent: 'CommandListTab',
props: {},
@@ -801,20 +776,20 @@ if (hasPermission('settings/change')) {
testEnabled: () => hasPermission('settings/change'),
});
// registerCommand({
// id: 'settings.show',
// category: __t('command.settings', { defaultMessage: 'Settings' }),
// name: __t('command.settings.change', { defaultMessage: 'Change' }),
// toolbarName: __t('command.settings', { defaultMessage: 'Settings' }),
// onClick: () => showModal(SettingsModal),
// testEnabled: () => hasPermission('settings/change'),
// });
registerCommand({
id: 'settings.show',
category: 'Settings',
name: 'Change',
toolbarName: 'Settings',
onClick: () => showModal(SettingsModal),
testEnabled: () => hasPermission('settings/change'),
});
}
registerCommand({
id: 'cloud.logout',
category: __t('command.cloud', { defaultMessage: 'Cloud' }),
name: __t('command.cloud.logout', { defaultMessage: 'Logout' }),
category: 'Cloud',
name: 'Logout',
onClick: () => {
cloudSigninTokenHolder.set(null);
},
@@ -822,10 +797,8 @@ registerCommand({
registerCommand({
id: 'file.exit',
category: __t('command.file', { defaultMessage: 'File' }),
name: isMac()
? __t('command.file.quit', { defaultMessage: 'Quit' })
: __t('command.file.exit', { defaultMessage: 'Exit' }),
category: 'File',
name: isMac() ? 'Quit' : 'Exit',
// keyText: isMac() ? 'Command+Q' : null,
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('quit-app'),
@@ -833,16 +806,16 @@ registerCommand({
registerCommand({
id: 'app.logout',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.app.logout', { defaultMessage: 'Logout' }),
category: 'App',
name: 'Logout',
testEnabled: () => getCurrentConfig()?.isUserLoggedIn,
onClick: doLogout,
});
registerCommand({
id: 'app.loggedUserCommands',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.app.loggedUser', { defaultMessage: 'Logged user' }),
category: 'App',
name: 'Logged user',
getSubCommands: () => {
const config = getCurrentConfig();
if (!config) return [];
@@ -859,16 +832,16 @@ registerCommand({
registerCommand({
id: 'app.disconnect',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.app.disconnect', { defaultMessage: 'Disconnect' }),
category: 'App',
name: 'Disconnect',
testEnabled: () => getCurrentConfig()?.singleConnection != null && !getCurrentConfig()?.isUserLoggedIn,
onClick: () => disconnectServerConnection(getCurrentConfig()?.singleConnection?._id),
});
registerCommand({
id: 'app.checkForUpdates',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.app.checkForUpdates', { defaultMessage: 'Check for updates' }),
id: 'file.checkForUpdates',
category: 'App',
name: 'Check for updates',
// testEnabled: () => true,
testEnabled: () => getAppUpdaterActive(),
onClick: () => getElectron().send('check-for-updates'),
@@ -888,35 +861,34 @@ export function registerFileCommands({
undoRedo = false,
executeAdditionalCondition = null,
copyPaste = false,
defaultTeamFolder = false,
}) {
if (save) {
registerCommand({
id: idPrefix + '.save',
group: 'save',
category,
name: __t('command.save', { defaultMessage: 'Save' }),
name: 'Save',
// keyText: 'CtrlOrCommand+S',
icon: 'icon save',
toolbar: true,
isRelatedToTab: true,
testEnabled: () => getCurrentEditor() != null,
onClick: () => saveTabFile(getCurrentEditor(), 'save', folder, format, fileExtension, defaultTeamFolder),
onClick: () => saveTabFile(getCurrentEditor(), 'save', folder, format, fileExtension),
});
registerCommand({
id: idPrefix + '.saveAs',
group: 'saveAs',
category,
name: __t('command.saveAs', { defaultMessage: 'Save As' }),
name: 'Save As',
testEnabled: () => getCurrentEditor() != null,
onClick: () => saveTabFile(getCurrentEditor(), 'save-as', folder, format, fileExtension, defaultTeamFolder),
onClick: () => saveTabFile(getCurrentEditor(), 'save-as', folder, format, fileExtension),
});
registerCommand({
id: idPrefix + '.saveToDisk',
category,
name: __t('command.saveToDisk', { defaultMessage: 'Save to disk' }),
name: 'Save to disk',
testEnabled: () => getCurrentEditor() != null && getElectron() != null,
onClick: () => saveTabFile(getCurrentEditor(), 'save-to-disk', folder, format, fileExtension, defaultTeamFolder),
onClick: () => saveTabFile(getCurrentEditor(), 'save-to-disk', folder, format, fileExtension),
});
}
@@ -924,7 +896,7 @@ export function registerFileCommands({
registerCommand({
id: idPrefix + '.execute',
category,
name: __t('command.execute', { defaultMessage: 'Execute' }),
name: 'Execute',
icon: 'icon run',
toolbar: true,
isRelatedToTab: true,
@@ -938,7 +910,7 @@ export function registerFileCommands({
registerCommand({
id: idPrefix + '.kill',
category,
name: __t('command.kill', { defaultMessage: 'Kill' }),
name: 'Kill',
icon: 'icon close',
toolbar: true,
isRelatedToTab: true,
@@ -951,7 +923,7 @@ export function registerFileCommands({
registerCommand({
id: idPrefix + '.toggleComment',
category,
name: __t('command.toggleComment', { defaultMessage: 'Toggle comment' }),
name: 'Toggle comment',
keyText: 'CtrlOrCommand+/',
disableHandleKeyText: 'CtrlOrCommand+/',
testEnabled: () => getCurrentEditor() != null,
@@ -963,7 +935,7 @@ export function registerFileCommands({
registerCommand({
id: idPrefix + '.copy',
category,
name: __t('command.copy', { defaultMessage: 'Copy' }),
name: 'Copy',
disableHandleKeyText: 'CtrlOrCommand+C',
testEnabled: () => getCurrentEditor() != null,
onClick: () => getCurrentEditor().copy(),
@@ -971,7 +943,7 @@ export function registerFileCommands({
registerCommand({
id: idPrefix + '.paste',
category,
name: __t('command.paste', { defaultMessage: 'Paste' }),
name: 'Paste',
disableHandleKeyText: 'CtrlOrCommand+V',
testEnabled: () => getCurrentEditor() != null,
onClick: () => getCurrentEditor().paste(),
@@ -982,7 +954,7 @@ export function registerFileCommands({
registerCommand({
id: idPrefix + '.find',
category,
name: __t('command.find', { defaultMessage: 'Find' }),
name: 'Find',
keyText: 'CtrlOrCommand+F',
testEnabled: () => getCurrentEditor() != null,
onClick: () => getCurrentEditor().find(),
@@ -991,7 +963,7 @@ export function registerFileCommands({
id: idPrefix + '.replace',
category,
keyText: isMac() ? 'Alt+Command+F' : 'CtrlOrCommand+H',
name: __t('command.replace', { defaultMessage: 'Replace' }),
name: 'Replace',
testEnabled: () => getCurrentEditor() != null,
onClick: () => getCurrentEditor().replace(),
});
@@ -1000,7 +972,7 @@ export function registerFileCommands({
registerCommand({
id: idPrefix + '.undo',
category,
name: __t('command.undo', { defaultMessage: 'Undo' }),
name: 'Undo',
group: 'undo',
icon: 'icon undo',
testEnabled: () => getCurrentEditor()?.canUndo(),
@@ -1010,7 +982,7 @@ export function registerFileCommands({
id: idPrefix + '.redo',
category,
group: 'redo',
name: __t('command.redo', { defaultMessage: 'Redo' }),
name: 'Redo',
icon: 'icon redo',
testEnabled: () => getCurrentEditor()?.canRedo(),
onClick: () => getCurrentEditor().redo(),
@@ -1020,24 +992,24 @@ export function registerFileCommands({
registerCommand({
id: 'app.minimize',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.minimize', { defaultMessage: 'Minimize' }),
category: 'Application',
name: 'Minimize',
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'minimize'),
});
registerCommand({
id: 'app.maximize',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.maximize', { defaultMessage: 'Maximize' }),
category: 'Application',
name: 'Maximize',
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'maximize'),
});
registerCommand({
id: 'app.toggleFullScreen',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.toggleFullScreen', { defaultMessage: 'Toggle full screen' }),
category: 'Application',
name: 'Toggle full screen',
keyText: 'F11',
testEnabled: () => getElectron() != null,
onClick: async () => {
@@ -1051,45 +1023,45 @@ registerCommand({
registerCommand({
id: 'app.toggleDevTools',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.toggleDevTools', { defaultMessage: 'Toggle Dev Tools' }),
category: 'Application',
name: 'Toggle Dev Tools',
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'devtools'),
});
registerCommand({
id: 'app.reload',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.reload', { defaultMessage: 'Reload' }),
category: 'Application',
name: 'Reload',
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'reload'),
});
registerCommand({
id: 'app.openDocs',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.documentation', { defaultMessage: 'Documentation' }),
category: 'Application',
name: 'Documentation',
onClick: () => openWebLink('https://docs.dbgate.io/'),
});
registerCommand({
id: 'app.openWeb',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.web', { defaultMessage: 'DbGate web' }),
category: 'Application',
name: 'DbGate web',
onClick: () => openWebLink('https://www.dbgate.io/'),
});
registerCommand({
id: 'app.openIssue',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.openIssue', { defaultMessage: 'Report problem or feature request' }),
category: 'Application',
name: 'Report problem or feature request',
onClick: () => openWebLink('https://github.com/dbgate/dbgate/issues/new'),
});
registerCommand({
id: 'app.openSponsoring',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.becomeSponsor', { defaultMessage: 'Become sponsor' }),
category: 'Application',
name: 'Become sponsor',
testEnabled: () => !isProApp(),
onClick: () => openWebLink('https://opencollective.com/dbgate'),
});
@@ -1103,8 +1075,8 @@ registerCommand({
registerCommand({
id: 'app.zoomIn',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.zoomIn', { defaultMessage: 'Zoom in' }),
category: 'Application',
name: 'Zoom in',
keyText: 'CtrlOrCommand+=',
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'zoomin'),
@@ -1112,8 +1084,8 @@ registerCommand({
registerCommand({
id: 'app.zoomOut',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.zoomOut', { defaultMessage: 'Zoom out' }),
category: 'Application',
name: 'Zoom out',
keyText: 'CtrlOrCommand+-',
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'zoomout'),
@@ -1121,16 +1093,16 @@ registerCommand({
registerCommand({
id: 'app.zoomReset',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.zoomReset', { defaultMessage: 'Reset zoom' }),
category: 'Application',
name: 'Reset zoom',
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'zoomreset'),
});
registerCommand({
id: 'edit.undo',
category: __t('command.edit', { defaultMessage: 'Edit' }),
name: __t('command.edit.undo', { defaultMessage: 'Undo' }),
category: 'Edit',
name: 'Undo',
keyText: 'CtrlOrCommand+Z',
systemCommand: true,
testEnabled: () => getElectron() != null,
@@ -1139,8 +1111,8 @@ registerCommand({
registerCommand({
id: 'edit.redo',
category: __t('command.edit', { defaultMessage: 'Edit' }),
name: __t('command.edit.redo', { defaultMessage: 'Redo' }),
category: 'Edit',
name: 'Redo',
systemCommand: true,
testEnabled: () => getElectron() != null,
onClick: () => getElectron().send('window-action', 'redo'),
@@ -1148,8 +1120,8 @@ registerCommand({
registerCommand({
id: 'edit.cut',
category: __t('command.edit', { defaultMessage: 'Edit' }),
name: __t('command.edit.cut', { defaultMessage: 'Cut' }),
category: 'Edit',
name: 'Cut',
keyText: 'CtrlOrCommand+X',
systemCommand: true,
testEnabled: () => getElectron() != null,
@@ -1158,8 +1130,8 @@ registerCommand({
registerCommand({
id: 'edit.copy',
category: __t('command.edit', { defaultMessage: 'Edit' }),
name: __t('command.edit.copy', { defaultMessage: 'Copy' }),
category: 'Edit',
name: 'Copy',
keyText: 'CtrlOrCommand+C',
systemCommand: true,
testEnabled: () => getElectron() != null,
@@ -1168,8 +1140,8 @@ registerCommand({
registerCommand({
id: 'edit.paste',
category: __t('command.edit', { defaultMessage: 'Edit' }),
name: __t('command.edit.paste', { defaultMessage: 'Paste' }),
category: 'Edit',
name: 'Paste',
keyText: 'CtrlOrCommand+V',
systemCommand: true,
testEnabled: () => getElectron() != null,
@@ -1178,8 +1150,8 @@ registerCommand({
registerCommand({
id: 'edit.selectAll',
category: __t('command.edit', { defaultMessage: 'Edit' }),
name: __t('command.edit.selectAll', { defaultMessage: 'Select All' }),
category: 'Edit',
name: 'Select All',
keyText: 'CtrlOrCommand+A',
systemCommand: true,
testEnabled: () => getElectron() != null,
@@ -1188,8 +1160,8 @@ registerCommand({
registerCommand({
id: 'app.unsetCurrentDatabase',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.unsetCurrentDatabase', { defaultMessage: 'Unset current database' }),
category: 'Application',
name: 'Unset current database',
testEnabled: () => getCurrentDatabase() != null,
onClick: () => currentDatabase.set(null),
});
@@ -1198,8 +1170,8 @@ let loadedCampaignList = [];
registerCommand({
id: 'internal.loadCampaigns',
category: __t('command.internal', { defaultMessage: 'Internal' }),
name: __t('command.internal.loadCampaigns', { defaultMessage: 'Load campaign list' }),
category: 'Internal',
name: 'Load campaign list',
testEnabled: () => getBoolSettingsValue('internal.showCampaigns', false),
onClick: async () => {
const resp = await apiCall('cloud/promo-widget-list', {});
@@ -1209,8 +1181,8 @@ registerCommand({
registerCommand({
id: 'internal.showCampaigns',
category: __t('command.internal', { defaultMessage: 'Internal' }),
name: __t('command.internal.showCampaigns', { defaultMessage: 'Show campaigns' }),
category: 'Internal',
name: 'Show campaigns',
testEnabled: () => getBoolSettingsValue('internal.showCampaigns', false) && loadedCampaignList?.length > 0,
getSubCommands: () => {
return loadedCampaignList.map(campaign => ({
@@ -1229,33 +1201,6 @@ registerCommand({
},
});
if (hasPermission('application-log')) {
registerCommand({
id: 'app.showLogs',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.showLogs', { defaultMessage: 'View application logs' }),
onClick: () => {
openNewTab({
title: 'Application log',
icon: 'img applog',
tabComponent: 'AppLogTab',
});
},
});
}
if (hasPermission('widgets/plugins')) {
registerCommand({
id: 'app.managePlugins',
category: __t('command.application', { defaultMessage: 'Application' }),
name: __t('command.application.managePlugins', { defaultMessage: 'Manage plugins' }),
onClick: () => {
selectedWidget.set('plugins');
visibleWidgetSideBar.set(true);
},
});
}
const electron = getElectron();
if (electron) {
electron.addEventListener('run-command', (e, commandId) => runCommand(commandId));
+2 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import _ from 'lodash';
import { getStringSettingsValue } from '../settings/settingsTools';
import { getBoolSettingsValue } from '../settings/settingsTools';
import { stringifyCellValue } from 'dbgate-tools';
export let rowData;
@@ -13,7 +13,7 @@
value,
'gridCellIntent',
editorTypes,
{ thousandsSeparator: getStringSettingsValue('dataGrid.thousandsSeparatorChar', 'none') },
{ useThousandsSeparator: getBoolSettingsValue('dataGrid.thousandsSeparator', false) },
jsonParsedValue
);
@@ -3,16 +3,16 @@
registerCommand({
id: 'collectionDataGrid.openQuery',
category: __t('command.dataGrid', { defaultMessage: 'Data grid' }),
name: __t('command.dataGrid.openQuery', { defaultMessage: 'Open query' }),
category: 'Data grid',
name: 'Open query',
testEnabled: () => getCurrentEditor() != null,
onClick: () => getCurrentEditor().openQuery(),
});
registerCommand({
id: 'collectionDataGrid.export',
category: __t('command.dataGrid', { defaultMessage: 'Data grid' }),
name: __t('command.dataGrid.export', { defaultMessage: 'Export' }),
category: 'Data grid',
name: 'Export',
keyText: 'CtrlOrCommand+E',
icon: 'icon export',
testEnabled: () => getCurrentEditor() != null,
@@ -140,7 +140,6 @@
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
import { mongoFilterBehaviour, standardFilterBehaviours } from 'dbgate-tools';
import { openImportExportTab } from '../utility/importExportTools';
import { __t } from '../translations';
export let conid;
export let display;
@@ -1,7 +1,7 @@
<script lang="ts">
import _, { isPlainObject } from 'lodash';
import ShowFormButton from '../formview/ShowFormButton.svelte';
import { detectTypeIcon, getConvertValueMenu, isJsonLikeLongString, safeJsonParse, isTypeNumber } from 'dbgate-tools';
import { detectTypeIcon, getConvertValueMenu, isJsonLikeLongString, safeJsonParse } from 'dbgate-tools';
import { openJsonDocument } from '../tabs/JsonTab.svelte';
import CellValue from './CellValue.svelte';
import { openJsonLinesData } from '../utility/openJsonLinesData';
@@ -57,7 +57,7 @@
$: style = computeStyle(maxWidth, col);
$: isJson =
_.isPlainObject(value) && !(value?.type == 'Buffer' && _.isArray(value.data)) && !value.$oid && !value.$bigint && !value.$decimal;
_.isPlainObject(value) && !(value?.type == 'Buffer' && _.isArray(value.data)) && !value.$oid && !value.$bigint;
// don't parse JSON for explicit data types
$: jsonParsedValue = !editorTypes?.explicitDataType && isJsonLikeLongString(value) ? safeJsonParse(value) : null;
@@ -80,7 +80,7 @@
class:isFocusedColumn
class:hasOverlayValue
class:isMissingOverlayField
class:alignRight={ (_.isNumber(value) || isTypeNumber(col.dataType)) && !showHint}
class:alignRight={_.isNumber(value) && !showHint}
{style}
>
{#if hasOverlayValue}
+7 -11
View File
@@ -361,7 +361,6 @@
detectSqlFilterBehaviour,
stringifyCellValue,
shouldOpenMultilineDialog,
base64ToHex,
} from 'dbgate-tools';
import { getContext, onDestroy } from 'svelte';
import _, { map } from 'lodash';
@@ -422,7 +421,7 @@
import { openJsonLinesData } from '../utility/openJsonLinesData';
import contextMenuActivator from '../utility/contextMenuActivator';
import InputTextModal from '../modals/InputTextModal.svelte';
import { __t, _t, _tval } from '../translations';
import { __t, _t } from '../translations';
import { isProApp } from '../utility/proTools';
import SaveArchiveModal from '../modals/SaveArchiveModal.svelte';
import hasPermission from '../utility/hasPermission';
@@ -759,7 +758,7 @@
export function saveCellToFileEnabled() {
const value = getSelectedExportableCell();
return _.isString(value) || (value?.type == 'Buffer' && _.isArray(value?.data)) || (value?.$binary?.base64);
return _.isString(value) || (value?.type == 'Buffer' && _.isArray(value?.data));
}
export async function saveCellToFile() {
@@ -772,8 +771,6 @@
fs.promises.writeFile(file, value);
} else if (value?.type == 'Buffer' && _.isArray(value?.data)) {
fs.promises.writeFile(file, window['Buffer'].from(value.data));
} else if (value?.$binary?.base64) {
fs.promises.writeFile(file, window['Buffer'].from(value.$binary.base64, 'base64'));
}
}
}
@@ -799,9 +796,8 @@
isText
? data
: {
$binary: {
base64: data.toString('base64'),
},
type: 'Buffer',
data: [...data],
}
);
}
@@ -1799,12 +1795,12 @@
text: _t('datagrid.copyAdvanced', { defaultMessage: 'Copy advanced'}),
submenu: [
_.keys(copyRowsFormatDefs).map(format => ({
text: _tval(copyRowsFormatDefs[format].label),
text: _.isFunction(copyRowsFormatDefs[format].label) ? copyRowsFormatDefs[format].label() : copyRowsFormatDefs[format].label,
onClick: () => copyToClipboardCore(format),
})),
{ divider: true },
_.keys(copyRowsFormatDefs).map(format => ({
text: _t('datagrid.setFormat', { defaultMessage: 'Set format: ' }) + (_tval(copyRowsFormatDefs[format].name)),
text: _t('datagrid.setFormat', { defaultMessage: 'Set format: ' }) + (_.isFunction(copyRowsFormatDefs[format].name) ? copyRowsFormatDefs[format].name() : copyRowsFormatDefs[format].name),
onClick: () => ($copyRowsFormat = format),
})),
@@ -1874,7 +1870,7 @@
return [
menu,
{
text: _tval(copyRowsFormatDefs[$copyRowsFormat].label),
text: _.isFunction(copyRowsFormatDefs[$copyRowsFormat].label) ? copyRowsFormatDefs[$copyRowsFormat].label() : copyRowsFormatDefs[$copyRowsFormat].label,
onClick: () => copyToClipboardCore($copyRowsFormat),
keyText: 'CtrlOrCommand+C',
tag: 'copy',
@@ -3,8 +3,8 @@
registerCommand({
id: 'jslTableGrid.export',
category: __t('command.dataGrid', { defaultMessage: 'Data grid' }),
name: __t('command.dataGrid.export', { defaultMessage: 'Export' }),
category: 'Data grid',
name: 'Export',
icon: 'icon export',
keyText: 'CtrlOrCommand+E',
testEnabled: () => getCurrentEditor() != null,
@@ -56,7 +56,6 @@
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
import { openImportExportTab } from '../utility/importExportTools';
import { __t } from '../translations';
export let jslid;
export let display;
@@ -55,7 +55,7 @@
{/if}
{#if dependencies.length > 0}
<div class="bold nowrap ml-1">{_t('dataGrid.dependentTables', { defaultMessage: 'Dependent tables' })} ({dependencies.length})</div>
<div class="bold nowrap ml-1">Dependent tables ({dependencies.length})</div>
{#each dependencies.filter(fk => filterName(filter, fk.pureName)) as fk}
<div
class="link"
@@ -1,5 +1,5 @@
<script context="module" lang="ts">
import { __t, _t } from '../translations'
import { __t } from '../translations'
const getCurrentEditor = () => getActiveComponent('SqlDataGridCore');
registerCommand({
@@ -127,7 +127,7 @@
export function openQuery(sql?) {
openNewTab(
{
title: _t('common.queryNumber', { defaultMessage: 'Query #' }),
title: 'Query #',
icon: 'img sql-file',
tabComponent: 'QueryTab',
focused: true,
-1
View File
@@ -73,7 +73,6 @@ export function countColumnSizes(grider: Grider, columns, containerWidth, displa
if (_.isArray(value)) text = `[${value.length} items]`;
else if (value?.$oid) text = `ObjectId("${value.$oid}")`;
else if (value?.$bigint) text = value.$bigint;
else if (value?.$decimal) text = value.$decimal;
else if (isJsonLikeLongString(value) && safeJsonParse(value)) text = '(JSON)';
const width = context.measureText(typeof text == 'string' ? text.slice(0, MAX_GRID_TEXT_LENGTH) : text).width + 8;
// console.log('colName', colName, text, width);
+21 -22
View File
@@ -3,9 +3,9 @@
registerCommand({
id: 'designer.arrange',
category: __t('command.designer', { defaultMessage: 'Designer' }),
category: 'Designer',
icon: 'icon arrange',
name: __t('command.designer.arrange', { defaultMessage: 'Arrange' }),
name: 'Arrange',
toolbar: true,
isRelatedToTab: true,
testEnabled: () => getCurrentEditor()?.canArrange(),
@@ -15,9 +15,9 @@
registerCommand({
id: 'diagram.export',
category: __t('command.designer', { defaultMessage: 'Designer' }),
toolbarName: __t('command.designer.exportDiagram', { defaultMessage: 'Export diagram' }),
name: __t('command.designer.exportDiagram', { defaultMessage: 'Export diagram' }),
category: 'Designer',
toolbarName: 'Export diagram',
name: 'Export diagram',
icon: 'icon report',
toolbar: true,
isRelatedToTab: true,
@@ -27,9 +27,9 @@
registerCommand({
id: 'diagram.deleteSelectedTables',
category: __t('command.designer', { defaultMessage: 'Designer' }),
toolbarName: __t('command.designer.remove', { defaultMessage: 'Remove' }),
name: __t('command.designer.removeSelectedTables', { defaultMessage: 'Remove selected tables' }),
category: 'Designer',
toolbarName: 'Remove',
name: 'Remove selected tables',
icon: 'icon delete',
toolbar: true,
isRelatedToTab: true,
@@ -67,7 +67,6 @@
import { isProApp } from '../utility/proTools';
import dragScroll from '../utility/dragScroll';
import FormStyledButton from '../buttons/FormStyledButton.svelte';
import { __t, _t } from '../translations';
export let value;
export let onChange;
@@ -849,45 +848,45 @@
settings?.customizeStyle && [
{ divider: true },
isProApp() && {
text: _t('designer.columnProperties', { defaultMessage: 'Column properties' }),
text: 'Column properties',
submenu: [
{
text: _t('designer.nullabilityYesNo', { defaultMessage: 'Nullability: {show}', values: { show: value?.style?.showNullability ? 'YES' : 'NO' } }),
text: `Nullability: ${value?.style?.showNullability ? 'YES' : 'NO'}`,
onClick: changeStyleFunc('showNullability', !value?.style?.showNullability),
},
{
text: _t('designer.dataTypeYesNo', { defaultMessage: 'Data type: {show}', values: { show: value?.style?.showDataType ? 'YES' : 'NO' } }),
text: `Data type: ${value?.style?.showDataType ? 'YES' : 'NO'}`,
onClick: changeStyleFunc('showDataType', !value?.style?.showDataType),
},
],
},
isProApp() && {
text: _t('designer.columns', { defaultMessage: 'Columns - { filterColumns }', values: { filterColumns: _.startCase(value?.style?.filterColumns || 'all') } }),
text: `Columns - ${_.startCase(value?.style?.filterColumns || 'all')}`,
submenu: [
{
text: _t('designer.all', { defaultMessage: 'All' }),
text: 'All',
onClick: changeStyleFunc('filterColumns', ''),
},
{
text: _t('designer.primaryKey', { defaultMessage: 'Primary Key' }),
text: 'Primary Key',
onClick: changeStyleFunc('filterColumns', 'primaryKey'),
},
{
text: _t('designer.allKeys', { defaultMessage: 'All Keys' }),
text: 'All Keys',
onClick: changeStyleFunc('filterColumns', 'allKeys'),
},
{
text: _t('designer.notNull', { defaultMessage: 'Not Null' }),
text: 'Not Null',
onClick: changeStyleFunc('filterColumns', 'notNull'),
},
{
text: _t('designer.keysAndNotNull', { defaultMessage: 'Keys And Not Null' }),
text: 'Keys And Not Null',
onClick: changeStyleFunc('filterColumns', 'keysAndNotNull'),
},
],
},
{
text: _t('designer.zoom', { defaultMessage: 'Zoom - {zoom}%', values: { zoom: ((value?.style?.zoomKoef || 1) * 100) } }),
text: `Zoom - ${(value?.style?.zoomKoef || 1) * 100}%`,
submenu: DIAGRAM_ZOOMS.map(koef => ({
text: `${koef * 100} %`,
onClick: changeStyleFunc('zoomKoef', koef.toString()),
@@ -1016,11 +1015,11 @@
use:dragScroll={handleDragScroll}
>
{#if !(tables?.length > 0)}
<div class="empty">{_t('designer.dragDropTables', { defaultMessage: 'Drag & drop tables or views from left panel here' })}</div>
<div class="empty">Drag &amp; drop tables or views from left panel here</div>
{#if allowAddTablesButton}
<div class="addAllTables">
<FormStyledButton value={_t('designer.addAllTables', { defaultMessage: 'Add all tables' })} on:click={handleAddAllTables} />
<FormStyledButton value="Add all tables" on:click={handleAddAllTables} />
</div>
{/if}
{/if}
@@ -1119,7 +1118,7 @@
<div class="panel">
<DragColumnMemory {settings} {sourceDragColumn$} {targetDragColumn$} />
<div class="searchbox">
<SearchInput bind:value={columnFilter} placeholder={_t('designer.filterColumns', { defaultMessage: 'Filter columns' })} />
<SearchInput bind:value={columnFilter} placeholder="Filter columns" />
<CloseSearchButton bind:filter={columnFilter} />
</div>
</div>
@@ -17,7 +17,6 @@
import moveDrag from '../utility/moveDrag';
import ColumnLine from './ColumnLine.svelte';
import DomTableRef from './DomTableRef';
import { _t } from '../translations';
export let conid;
export let database;
@@ -186,8 +185,8 @@
const handleSetTableAlias = () => {
showModal(InputTextModal, {
value: alias || '',
label: _t('designerTable.newAlias', { defaultMessage: 'New alias' }),
header: _t('designerTable.setTableAlias', { defaultMessage: 'Set table alias' }),
label: 'New alias',
header: 'Set table alias',
onConfirm: newAlias => {
onChangeTable({
...table,
@@ -211,13 +210,13 @@
return settings?.tableMenu({ designer, designerId, onRemoveTable });
}
return [
{ text: _t('common.remove', { defaultMessage: 'Remove' }), onClick: () => onRemoveTable({ designerId }) },
{ text: 'Remove', onClick: () => onRemoveTable({ designerId }) },
{ divider: true },
settings?.allowTableAlias &&
!isMultipleTableSelection && [
{ text: _t('designerTable.setTableAlias', { defaultMessage: 'Set table alias' }), onClick: handleSetTableAlias },
{ text: 'Set table alias', onClick: handleSetTableAlias },
alias && {
text: _t('designerTable.removeTableAlias', { defaultMessage: 'Remove table alias' }),
text: 'Remove table alias',
onClick: () =>
onChangeTable({
...table,
@@ -226,11 +225,11 @@
},
],
settings?.allowAddAllReferences &&
!isMultipleTableSelection && { text: _t('designerTable.addReferences', { defaultMessage: 'Add references' }), onClick: () => onAddAllReferences(table) },
settings?.allowChangeColor && { text: _t('designerTable.changeColor', { defaultMessage: 'Change color' }), onClick: () => onChangeTableColor(table) },
!isMultipleTableSelection && { text: 'Add references', onClick: () => onAddAllReferences(table) },
settings?.allowChangeColor && { text: 'Change color', onClick: () => onChangeTableColor(table) },
settings?.allowDefineVirtualReferences &&
!isMultipleTableSelection && {
text: _t('designerTable.defineVirtualForeignKey', { defaultMessage: 'Define virtual foreign key' }),
text: 'Define virtual foreign key',
onClick: () => handleDefineVirtualForeignKey(table),
},
settings?.appendTableSystemMenu &&
@@ -1,170 +0,0 @@
<script lang="ts">
import _ from 'lodash';
import HorizontalSplitter from './HorizontalSplitter.svelte';
interface MenuItemDef {
label: string;
slot?: number;
component?: any;
props?: any;
testid?: string;
identifier?: string;
}
export let items: MenuItemDef[];
export let value: string | number = 0;
export let containerMaxWidth = undefined;
export let containerMaxHeight = undefined;
export let flex1 = true;
export let flexColContainer = false;
export let maxHeight100 = false;
export let scrollableContentContainer = false;
export let contentTestId = undefined;
export let onUserChange = null;
export function setValue(index) {
value = index;
}
export function getValue() {
return value;
}
</script>
<div class="main" class:maxHeight100 class:flex1>
<HorizontalSplitter initialValue="170px">
<svelte:fragment slot="1">
<div class="menu">
{#each _.compact(items) as item, index}
<div
class="menu-item"
class:selected={value == (item.identifier ?? index)}
on:click={() => {
value = item.identifier ?? index;
onUserChange?.(item.identifier ?? index);
}}
data-testid={item.testid}
>
<span class="ml-2 noselect">
{item.label}
</span>
</div>
{/each}
</div>
</svelte:fragment>
<svelte:fragment slot="2">
<div
class="content-container"
class:scrollableContentContainer
style:max-height={containerMaxHeight}
data-testid={contentTestId}
>
{#each _.compact(items) as item, index}
<div
class="container"
class:flexColContainer
class:maxHeight100
class:itemVisible={(item.identifier ?? index) == value}
style:max-width={containerMaxWidth}
>
<svelte:component
this={item.component}
{...item.props}
itemVisible={(item.identifier ?? index) == value}
menuControlHiddenItem={(item.identifier ?? index) != value}
/>
</div>
{/each}
</div>
</svelte:fragment>
</HorizontalSplitter>
</div>
<style>
.main {
display: flex;
flex: 1;
}
.main.flex1 {
flex: 1;
max-height: 100%;
}
.main.maxHeight100 {
max-height: 100%;
}
.menu {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
background-color: var(--theme-bg-2);
overflow-x: auto;
}
.menu::-webkit-scrollbar {
width: 7px;
}
.menu-item {
white-space: nowrap;
padding: 12px 20px;
display: flex;
align-items: center;
cursor: pointer;
border-bottom: 1px solid var(--theme-border);
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);
}
.menu-item.selected {
background-color: var(--theme-bg-1);
font-weight: 600;
border-left: 3px solid var(--theme-font-link);
}
.content-container {
flex: 1;
position: relative;
overflow: hidden;
height: 100%;
background-color: var(--theme-bg-0);
}
.scrollableContentContainer {
overflow-y: auto;
}
.container.maxHeight100 {
max-height: 100%;
}
.container.flexColContainer {
display: flex;
flex-direction: column;
}
.container {
position: absolute;
display: flex;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow-y: auto;
padding: 20px;
}
.container:not(.itemVisible) {
visibility: hidden;
}
</style>
+1 -2
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import _ from 'lodash';
import DropDownButton from '../buttons/DropDownButton.svelte';
import { _tval } from '../translations';
interface TabDef {
label: string;
@@ -47,7 +46,7 @@
data-testid={tab.testid}
>
<span class="ml-2 noselect">
{_tval(tab.label)}
{tab.label}
</span>
</div>
{/each}
@@ -27,7 +27,6 @@
import { evaluateCondition } from 'dbgate-sqltree';
import { compileCompoudEvalCondition } from 'dbgate-filterparser';
import { chevronExpandIcon } from '../icons/expandIcons';
import { _tval } from '../translations';
export let columns: (TableControlColumn | false)[];
export let rows = null;
@@ -369,7 +368,7 @@
{/if}
{/key}
{:else}
{ _tval(row[col.fieldName]) || '' }
{ _.isFunction(row[col.fieldName]) ? row[col.fieldName]() : row[col.fieldName] || ''}
{/if}
</td>
{/each}
@@ -7,7 +7,6 @@
import { getFormContext } from './FormProviderCore.svelte';
import FormSelectField from './FormSelectField.svelte';
import { _t } from '../translations';
export let folderName;
export let name;
@@ -29,10 +28,10 @@
<div>
<FormStyledButton
type="button"
value={_t('common.allFiles', { defaultMessage: "All files" })}
value="All files"
on:click={() => setFieldValue(name, _.uniq([...($values[name] || []), ...($files && $files.map(x => x.name))]))}
/>
<FormStyledButton type="button" value={_t('common.removeAll', { defaultMessage: "Remove all" })} on:click={() => setFieldValue(name, [])} />
<FormStyledButton type="button" value="Remove all" on:click={() => setFieldValue(name, [])} />
</div>
</div>
@@ -8,7 +8,6 @@
import { getFormContext } from './FormProviderCore.svelte';
import FormSelectField from './FormSelectField.svelte';
import { _t } from '../translations';
export let additionalFolders = [];
export let name;
@@ -36,7 +35,7 @@
label: folder,
})),
allowCreateNew && {
label: _t('archiveFolder.createNew', { defaultMessage: '(Create new)' }),
label: '(Create new)',
value: '@create',
},
];
@@ -49,8 +48,8 @@
function handleChange(e) {
if (e.detail == '@create') {
showModal(InputTextModal, {
header: _t('archiveFolder.archive', { defaultMessage: 'Archive' }),
label: _t('archiveFolder.nameOfNewArchiveFolder', { defaultMessage: 'Name of new archive folder' }),
header: 'Archive',
label: 'Name of new archive folder',
onConfirm: createOption,
});
}
@@ -1,12 +1,9 @@
<script lang="ts">
import FontIcon from "../icons/FontIcon.svelte";
export let type;
export let label;
export let noMargin = false;
export let disabled = false;
export let labelProps: any = {};
export let labelIcon = null;
</script>
<div class="largeFormMarker" class:noMargin>
@@ -15,9 +12,6 @@
<span {...labelProps} on:click={labelProps.onClick} class:disabled class='checkLabel'>{label}</span>
{:else}
<div class="label" {...labelProps} on:click={labelProps.onClick}>
{#if labelIcon}
<FontIcon icon={labelIcon} padRight />
{/if}
<span {...labelProps} on:click={labelProps.onClick} class:disabled>{label}</span>
</div>
<slot />
@@ -5,7 +5,6 @@
import { getFormContext } from './FormProviderCore.svelte';
import TextField from './TextField.svelte';
import { _t } from '../translations';
export let name;
export let disabled = false;
@@ -30,7 +29,7 @@
setFieldValue(name, e.target['value']);
}
}}
placeholder={isCrypted ? _t('common.passwordEncrypted', { defaultMessage: 'Password is encrypted' }) : undefined}
placeholder={isCrypted ? '(Password is encrypted)' : undefined}
type={isCrypted || showPassword ? 'text' : 'password'}
/>
{#if !isCrypted}
@@ -1,7 +1,6 @@
<script lang="ts">
import { getFormContext } from './FormProviderCore.svelte';
import TextField from './TextField.svelte';
import { _tval } from '../translations';
export let name;
export let defaultValue;
@@ -12,7 +11,7 @@
<TextField
{...$$restProps}
value={$values?.[name] ? _tval($values[name]) : defaultValue}
value={$values?.[name] ?? defaultValue}
on:input={e => setFieldValue(name, e.target['value'])}
on:input={e => {
if (saveOnInput) {
@@ -11,7 +11,6 @@
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;
@@ -34,8 +33,8 @@
async function handleAddNewApplication() {
showModal(InputTextModal, {
header: _t('database.newApplication', { defaultMessage: 'New application' }),
label: _t('database.applicationName', { defaultMessage: 'Application name' }),
header: 'New application',
label: 'Application name',
value: _.startCase(database),
onConfirm: async appName => {
const newAppId = await apiCall('apps/create-app-from-db', {
@@ -3,8 +3,8 @@
registerCommand({
id: 'collectionJsonView.expandAll',
category: __t('command.collectionData', { defaultMessage: 'Collection data' }),
name: __t('command.collectionData.expandAll', { defaultMessage: 'Expand all' }),
category: 'Collection data',
name: 'Expand all',
isRelatedToTab: true,
icon: 'icon expand-all',
onClick: () => getCurrentEditor().handleExpandAll(),
@@ -12,8 +12,8 @@
});
registerCommand({
id: 'collectionJsonView.collapseAll',
category: __t('command.collectionData', { defaultMessage: 'Collection data' }),
name: __t('command.collectionData.collapseAll', { defaultMessage: 'Collapse all' }),
category: 'Collection data',
name: 'Collapse all',
isRelatedToTab: true,
icon: 'icon collapse-all',
onClick: () => getCurrentEditor().handleCollapseAll(),
@@ -37,7 +37,6 @@
import CollectionJsonRow from './CollectionJsonRow.svelte';
import { getIntSettingsValue } from '../settings/settingsTools';
import invalidateCommands from '../commands/invalidateCommands';
import { __t } from '../translations';
export let conid;
export let database;
+39 -35
View File
@@ -14,8 +14,8 @@
registerCommand({
id: 'dataForm.refresh',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('common.refresh', { defaultMessage: 'Refresh' }),
category: 'Data form',
name: _t('common.refresh', { defaultMessage: 'Refresh' }),
keyText: 'F5 | CtrlOrCommand+R',
toolbar: true,
isRelatedToTab: true,
@@ -26,8 +26,8 @@
registerCommand({
id: 'dataForm.copyToClipboard',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.copyToClipboard', { defaultMessage: 'Copy to clipboard' }),
category: 'Data form',
name: 'Copy to clipboard',
keyText: 'CtrlOrCommand+C',
disableHandleKeyText: 'CtrlOrCommand+C',
testEnabled: () => getCurrentDataForm() != null,
@@ -36,8 +36,8 @@
registerCommand({
id: 'dataForm.revertRowChanges',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.revertRowChanges', { defaultMessage: 'Revert row changes' }),
category: 'Data form',
name: 'Revert row changes',
keyText: 'CtrlOrCommand+U',
testEnabled: () => getCurrentDataForm()?.getGrider()?.containsChanges,
onClick: () => getCurrentDataForm().getGrider().revertRowChanges(0),
@@ -45,8 +45,8 @@
registerCommand({
id: 'dataForm.setNull',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.setNull', { defaultMessage: 'Set NULL' }),
category: 'Data form',
name: 'Set NULL',
keyText: 'CtrlOrCommand+0',
testEnabled: () => getCurrentDataForm() != null && !getCurrentDataForm()?.getEditorTypes()?.supportFieldRemoval,
onClick: () => getCurrentDataForm().setFixedValue(null),
@@ -54,8 +54,8 @@
registerCommand({
id: 'dataForm.removeField',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.removeField', { defaultMessage: 'Remove field' }),
category: 'Data form',
name: 'Remove field',
keyText: 'CtrlOrCommand+0',
testEnabled: () => getCurrentDataForm() != null && getCurrentDataForm()?.getEditorTypes()?.supportFieldRemoval,
onClick: () => getCurrentDataForm().setFixedValue(undefined),
@@ -63,8 +63,8 @@
registerCommand({
id: 'dataForm.undo',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.undo', { defaultMessage: 'Undo' }),
category: 'Data form',
name: 'Undo',
group: 'undo',
icon: 'icon undo',
toolbar: true,
@@ -75,8 +75,8 @@
registerCommand({
id: 'dataForm.redo',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.redo', { defaultMessage: 'Redo' }),
category: 'Data form',
name: 'Redo',
group: 'redo',
icon: 'icon redo',
toolbar: true,
@@ -87,16 +87,16 @@
registerCommand({
id: 'dataForm.reconnect',
category: __t('command.dataGrid', { defaultMessage: 'Data grid' }),
name: __t('command.dataGrid.reconnect', { defaultMessage: 'Reconnect' }),
category: 'Data grid',
name: 'Reconnect',
testEnabled: () => getCurrentDataForm() != null,
onClick: () => getCurrentDataForm().reconnect(),
});
registerCommand({
id: 'dataForm.filterSelected',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.filterSelected', { defaultMessage: 'Filter this value' }),
category: 'Data form',
name: 'Filter this value',
keyText: 'CtrlOrCommand+Shift+F',
testEnabled: () => getCurrentDataForm() != null,
onClick: () => getCurrentDataForm().filterSelectedValue(),
@@ -104,16 +104,16 @@
registerCommand({
id: 'dataForm.addToFilter',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.addToFilter', { defaultMessage: 'Add to filter' }),
category: 'Data form',
name: 'Add to filter',
testEnabled: () => getCurrentDataForm() != null,
onClick: () => getCurrentDataForm().addToFilter(),
});
registerCommand({
id: 'dataForm.goToFirst',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.goToFirst', { defaultMessage: 'First' }),
category: 'Data form',
name: 'First',
keyText: 'CtrlOrCommand+Home',
toolbar: true,
isRelatedToTab: true,
@@ -124,8 +124,8 @@
registerCommand({
id: 'dataForm.goToPrevious',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.goToPrevious', { defaultMessage: 'Previous' }),
category: 'Data form',
name: 'Previous',
keyText: 'CtrlOrCommand+ArrowUp',
toolbar: true,
isRelatedToTab: true,
@@ -136,8 +136,8 @@
registerCommand({
id: 'dataForm.goToNext',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.goToNext', { defaultMessage: 'Next' }),
category: 'Data form',
name: 'Next',
keyText: 'CtrlOrCommand+ArrowDown',
toolbar: true,
isRelatedToTab: true,
@@ -148,8 +148,8 @@
registerCommand({
id: 'dataForm.goToLast',
category: __t('command.dataForm', { defaultMessage: 'Data form' }),
name: __t('command.dataForm.goToLast', { defaultMessage: 'Last' }),
category: 'Data form',
name: 'Last',
keyText: 'CtrlOrCommand+End',
toolbar: true,
isRelatedToTab: true,
@@ -197,7 +197,7 @@
import resizeObserver from '../utility/resizeObserver';
import openReferenceForm from './openReferenceForm';
import { useSettings } from '../utility/metadataLoaders';
import { _t, __t } from '../translations';
import { _t } from '../translations';
export let conid;
export let database;
@@ -243,16 +243,20 @@
function getRowCountInfo(allRowCount) {
if (rowCountNotAvailable) {
return _t('dataForm.rowCount', { defaultMessage: 'Row: {rowCount} / ???', values: { rowCount: ((display.config.formViewRecordNumber || 0) + 1).toLocaleString() } });
return `Row: ${((display.config.formViewRecordNumber || 0) + 1).toLocaleString()} / ???`;
}
if (rowData == null) {
if (allRowCount != null) {
return _t('dataForm.outOfBounds', { defaultMessage: 'Out of bounds: {current} / {total}', values: { current: ((display.config.formViewRecordNumber || 0) + 1).toLocaleString(), total: allRowCount.toLocaleString() } });
return `Out of bounds: ${(
(display.config.formViewRecordNumber || 0) + 1
).toLocaleString()} / ${allRowCount.toLocaleString()}`;
}
return _t('dataForm.noData', { defaultMessage: 'No data' });
return 'No data';
}
if (allRowCount == null || display == null) return _t('dataForm.loadingRowCount', { defaultMessage: 'Loading row count...' });
return _t('dataForm.rowCount', { defaultMessage: 'Row: {current} / {total}', values: { current: ((display.config.formViewRecordNumber || 0) + 1).toLocaleString(), total: allRowCount.toLocaleString() } });
if (allRowCount == null || display == null) return 'Loading row count...';
return `Row: ${(
(display.config.formViewRecordNumber || 0) + 1
).toLocaleString()} / ${allRowCount.toLocaleString()}`;
}
export function getGrider() {
@@ -716,7 +720,7 @@
</div>
{#if isLoading}
<LoadingInfo wrapper message={_t('common.loadingData', { defaultMessage: 'Loading data' })} />
<LoadingInfo wrapper message="Loading data" />
{/if}
<style>
-1
View File
@@ -353,7 +353,6 @@
'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>
+2 -3
View File
@@ -23,7 +23,6 @@
import ElectronFilesInput from './ElectronFilesInput.svelte';
import { addFilesToSourceList } from './ImportExportConfigurator.svelte';
import UploadButton from '../buttons/UploadButton.svelte';
import { _t } from '../translations';
export let setPreviewSource = undefined;
@@ -56,10 +55,10 @@
{:else}
<UploadButton />
{/if}
<FormStyledButton value={_t('importExport.addWebUrl', { defaultMessage: "Add web URL" })} on:click={handleAddUrl} />
<FormStyledButton value="Add web URL" on:click={handleAddUrl} />
</div>
<div class="wrapper">{_t('importExport.dragDropImportedFilesHere', { defaultMessage: "Drag & drop imported files here" })}</div>
<div class="wrapper">Drag &amp; drop imported files here</div>
</div>
<style>

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