Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b9e8e4d23 | |||
| 98c134afe2 | |||
| b15a051dd1 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,7 +24,6 @@ docker/plugins
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.translation
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
||||
@@ -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
@@ -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 =
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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');
|
||||
})();
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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,4 +1,3 @@
|
||||
module.exports = {
|
||||
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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',
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,5 +2,4 @@ module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,5 +2,4 @@ module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -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}"`;
|
||||
}
|
||||
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -2,5 +2,4 @@ module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js'],
|
||||
reporters: ['default', 'github-actions'],
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ export const mongoFilterBehaviour: FilterBehaviour = {
|
||||
allowStringToken: true,
|
||||
allowNumberDualTesting: true,
|
||||
allowObjectIdTesting: true,
|
||||
allowHexString: true,
|
||||
};
|
||||
|
||||
export const evalFilterBehaviour: FilterBehaviour = {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Vendored
-1
@@ -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);
|
||||
|
||||
|
||||
Vendored
-1
@@ -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;
|
||||
|
||||
Vendored
-2
@@ -96,6 +96,4 @@ export type TestEngineInfo = {
|
||||
}>;
|
||||
|
||||
objects?: Array<TestObjectInfo>;
|
||||
|
||||
binaryDataType?: string;
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 & 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,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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 & drop imported files here</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user