1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

...

48 Commits

Author SHA1 Message Date
420255cdd4 chore(release): 0.3.1 2021-07-27 10:27:02 +02:00
a8a47ed5f7 fix(UI): tabs or explorebar elements selected with mouse wheel or right button 2021-07-23 22:41:53 +02:00
425f0663f9 chore(release): 0.3.0 2021-07-23 17:38:31 +02:00
e106d100b5 refactor: mousedown instead click on tabs and explorebar elements 2021-07-23 15:45:06 +02:00
d7fdf53932 fix: wrong editor height with some conditions 2021-07-23 10:56:41 +02:00
62f7e57d0c fix: manual page input not disabled when only one page is available 2021-07-23 09:01:50 +02:00
14577d14bb fix: sort order of tables is lost switching pages 2021-07-22 18:22:47 +02:00
f5f2a697e8 ci: update snap store config 2021-07-22 17:30:07 +02:00
a0105cf1c3 fix(UI): not disabled buttons during save table setting tabs 2021-07-22 13:13:26 +02:00
77c5d28032 fix: new field default value unknown instead 'noval' 2021-07-22 13:06:54 +02:00
d1d8592f79 build: downgrade to ss2-promise 0.2.0 due build problems 2021-07-22 11:34:21 +02:00
1f828f69a0 Merge pull request #87 from Fabio286/new-tab-system
New tab system
2021-07-22 10:57:43 +02:00
adc5477673 feat: option to restore session on startup 2021-07-22 10:41:06 +02:00
1e543aa6b0 fix: reload twice after element rename 2021-07-21 18:50:22 +02:00
c41e059b0b fix: wrong loaded schema change 2021-07-21 17:56:55 +02:00
e6ef5ffa56 refactor: improved the way how schema is passed to client classes 2021-07-21 14:40:29 +02:00
d67d122270 Merge branch 'master' of https://github.com/Fabio286/antares into new-tab-system 2021-07-21 09:11:06 +02:00
b837e2fc68 Merge pull request #86 from Fabio286/dependabot/npm_and_yarn/ssh2-promise-1.0.0
build(deps): bump ssh2-promise from 0.1.9 to 1.0.0
2021-07-21 09:10:35 +02:00
a73a2f483e feat: option to select schema in query tabs 2021-07-20 19:19:54 +02:00
0a9983d30d feat: new function, procedure and scheduler tabs 2021-07-20 16:59:59 +02:00
58b91ebfe0 feat: new trigger function tabs 2021-07-19 22:38:56 +02:00
e78ca2417e fix(UI): multiple trigger tabs open on single click on explore bar 2021-07-19 11:28:11 +02:00
e1855a262d feat(UI): empty workspace view 2021-07-18 16:10:36 +02:00
6b725b1d40 fix: issues with trigger temp tabs 2021-07-17 16:09:57 +02:00
f6faad98f8 feat: new trigger setting tabs 2021-07-17 13:10:54 +02:00
320aa8ba04 feat: close tabs if element deleted 2021-07-17 10:46:24 +02:00
9f0280b991 Merge branch 'master' of https://github.com/Fabio286/antares into new-tab-system 2021-07-17 10:00:46 +02:00
04fa320820 fix: clear empty indexes and foreign keys on confirm respective modals 2021-07-17 09:59:45 +02:00
f7c3aa883d fix: tab won't open after table or view creation 2021-07-17 09:57:49 +02:00
f7a74df009 feat: new unsaved change reminder 2021-07-16 23:24:55 +02:00
003c02b1fb feat: new view setting tabs 2021-07-16 18:52:18 +02:00
ef21ea7448 feat: rename tabs if element is renamed 2021-07-16 17:27:37 +02:00
525c964c62 fix: enabled copy context on non editable rows 2021-07-16 17:09:02 +02:00
7845e3e501 feat(UI): new table settings tabs 2021-07-15 19:51:18 +02:00
0c29e0d566 perf(UI): improvements in setting bar connections sort 2021-07-15 18:31:44 +02:00
d38097d056 feat(UI): sortable tabs 2021-07-14 20:30:54 +02:00
c87b8dc738 refactor: passing schema from table context options 2021-07-14 18:15:13 +02:00
ed6e7fa72d refactor(UI): improved breadcrumbs and tabs 2021-07-14 16:10:34 +02:00
f0fa7c81b7 fix(UI): table icon in view data tabs 2021-07-14 12:33:26 +02:00
5bb4e496f2 feat(MySQL): improved schema detection for queries 2021-07-14 12:31:37 +02:00
01057332b0 feat(UI): display schema in data tabs 2021-07-14 12:31:12 +02:00
ab382dfbcd feat: new data tabs 2021-07-13 19:23:02 +02:00
88c4cdc8e2 feat(UI): close temp data tabs 2021-07-13 16:53:47 +02:00
15ff211a41 Merge branch 'master' of https://github.com/Fabio286/antares into new-tab-system 2021-07-13 09:19:26 +02:00
5c855a520a fix: solved a vulnerability in table names 2021-07-13 09:17:22 +02:00
7488bc7a17 refactor(core): better way to obtain schema 2021-07-13 09:09:25 +02:00
dependabot[bot]
fd85cf43a2 build(deps): bump ssh2-promise from 0.1.9 to 1.0.0
Bumps [ssh2-promise](https://github.com/sanketbajoria/ssh2-promise) from 0.1.9 to 1.0.0.
- [Release notes](https://github.com/sanketbajoria/ssh2-promise/releases)
- [Commits](https://github.com/sanketbajoria/ssh2-promise/commits/1.0.0)

---
updated-dependencies:
- dependency-name: ssh2-promise
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-12 19:05:27 +00:00
a87079cd17 feat(UI): temporary table data tabs 2021-07-12 19:18:29 +02:00
52 changed files with 2306 additions and 1809 deletions

View File

@@ -2,6 +2,59 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.3.1](https://github.com/Fabio286/antares/compare/v0.3.0...v0.3.1) (2021-07-27)
### Bug Fixes
* **UI:** tabs or explorebar elements selected with mouse wheel or right button ([a8a47ed](https://github.com/Fabio286/antares/commit/a8a47ed5f7d5d8cbbdd6c33ef8307d9dce5b193b))
## [0.3.0](https://github.com/Fabio286/antares/compare/v0.2.1...v0.3.0) (2021-07-23)
### Features
* close tabs if element deleted ([320aa8b](https://github.com/Fabio286/antares/commit/320aa8ba04d464f628b938b812165cd1ec786492))
* new data tabs ([ab382df](https://github.com/Fabio286/antares/commit/ab382dfbcd89f8ab38a8cbdb4d87e1705618fd0d))
* new function, procedure and scheduler tabs ([0a9983d](https://github.com/Fabio286/antares/commit/0a9983d30d6aeb9aee1c68b31a31f8c605deea45))
* new trigger function tabs ([58b91eb](https://github.com/Fabio286/antares/commit/58b91ebfe0ab200d76d4cfd442b9f2970ae97384))
* new trigger setting tabs ([f6faad9](https://github.com/Fabio286/antares/commit/f6faad98f88222500dfb7265b74c1be914b894cd))
* new unsaved change reminder ([f7a74df](https://github.com/Fabio286/antares/commit/f7a74df0097867a82a2a2d8b3c278a81897d7898))
* new view setting tabs ([003c02b](https://github.com/Fabio286/antares/commit/003c02b1fbe5aba7f53f3faa5610b0c2f7706793))
* option to restore session on startup ([adc5477](https://github.com/Fabio286/antares/commit/adc5477673603cd63fabf77a53db5397e3774e0e))
* option to select schema in query tabs ([a73a2f4](https://github.com/Fabio286/antares/commit/a73a2f483ef6927def4ba635e306277c34ae4b53))
* **UI:** empty workspace view ([e1855a2](https://github.com/Fabio286/antares/commit/e1855a262dc24363fc143a38a70154938308bd71))
* rename tabs if element is renamed ([ef21ea7](https://github.com/Fabio286/antares/commit/ef21ea74481839ba67ca79d40d5f47e1c12aebc0))
* **MySQL:** improved schema detection for queries ([5bb4e49](https://github.com/Fabio286/antares/commit/5bb4e496f289f3ae8a46f24d1a2fbb896d9da86e))
* **UI:** close temp data tabs ([88c4cdc](https://github.com/Fabio286/antares/commit/88c4cdc8e2a60ffbe26b0f831184c3a6dd9a1637))
* **UI:** display schema in data tabs ([0105733](https://github.com/Fabio286/antares/commit/01057332b090997c107bf395bb1fc3b9195e8218))
* **UI:** new table settings tabs ([7845e3e](https://github.com/Fabio286/antares/commit/7845e3e501bb7f890d07b42d4f3eb5f9f4bc8586))
* **UI:** sortable tabs ([d38097d](https://github.com/Fabio286/antares/commit/d38097d0567020b5265b5a0b347f5e1f38e0b1d4))
* **UI:** temporary table data tabs ([a87079c](https://github.com/Fabio286/antares/commit/a87079cd179033cebb6fd228ad7f1b991f3b6c46))
### Bug Fixes
* clear empty indexes and foreign keys on confirm respective modals ([04fa320](https://github.com/Fabio286/antares/commit/04fa320820df1f70a4ef05e4a6e4cbcd4081d047))
* enabled copy context on non editable rows ([525c964](https://github.com/Fabio286/antares/commit/525c964c623e7574f6abe732a2b315b8805d5eee))
* issues with trigger temp tabs ([6b725b1](https://github.com/Fabio286/antares/commit/6b725b1d40753ad89bfad6f1df57c6fb737e5262))
* manual page input not disabled when only one page is available ([62f7e57](https://github.com/Fabio286/antares/commit/62f7e57d0ccf6041b5469bec6a1864aa5b045609))
* new field default value unknown instead 'noval' ([77c5d28](https://github.com/Fabio286/antares/commit/77c5d280325792e20befc845f3a6834837131e39))
* reload twice after element rename ([1e543aa](https://github.com/Fabio286/antares/commit/1e543aa6b0b63e9134bf544682a26bd98573b794))
* solved a vulnerability in table names ([5c855a5](https://github.com/Fabio286/antares/commit/5c855a520a6bc66cc00b0b8afc6d2c03c75c0fab))
* sort order of tables is lost switching pages ([14577d1](https://github.com/Fabio286/antares/commit/14577d14bb337898a1fd0fdee1c3760812b9d21f))
* wrong editor height with some conditions ([d7fdf53](https://github.com/Fabio286/antares/commit/d7fdf53932a4d0609a88641c40d451c71b12c242))
* **UI:** multiple trigger tabs open on single click on explore bar ([e78ca24](https://github.com/Fabio286/antares/commit/e78ca2417e8f996fa0507c5a5586a75393dfe8ee))
* **UI:** not disabled buttons during save table setting tabs ([a0105cf](https://github.com/Fabio286/antares/commit/a0105cf1c37144ea27b212029d39275918f4f95e))
* tab won't open after table or view creation ([f7c3aa8](https://github.com/Fabio286/antares/commit/f7c3aa883dfcad0a28222c53cf22ab49d381559c))
* wrong loaded schema change ([c41e059](https://github.com/Fabio286/antares/commit/c41e059b0ba6d8c6d545c3b86a21a9a2abb6f537))
* **UI:** table icon in view data tabs ([f0fa7c8](https://github.com/Fabio286/antares/commit/f0fa7c81b7aa2a05833ce0b243afed39db98d66b))
### Improvements
* **UI:** improvements in setting bar connections sort ([0c29e0d](https://github.com/Fabio286/antares/commit/0c29e0d566c792ffd1b2b7045124e170b9c51985))
### [0.2.1](https://github.com/Fabio286/antares/compare/v0.2.0...v0.2.1) (2021-07-09) ### [0.2.1](https://github.com/Fabio286/antares/compare/v0.2.0...v0.2.1) (2021-07-09)

View File

@@ -1,7 +1,7 @@
{ {
"name": "antares", "name": "antares",
"productName": "Antares", "productName": "Antares",
"version": "0.2.1", "version": "0.3.1",
"description": "A cross-platform easy to use SQL client.", "description": "A cross-platform easy to use SQL client.",
"license": "MIT", "license": "MIT",
"repository": "https://github.com/Fabio286/antares.git", "repository": "https://github.com/Fabio286/antares.git",
@@ -102,7 +102,7 @@
"source-map-support": "^0.5.16", "source-map-support": "^0.5.16",
"spectre.css": "^0.5.9", "spectre.css": "^0.5.9",
"sql-formatter": "^4.0.2", "sql-formatter": "^4.0.2",
"ssh2-promise": "^0.1.7", "ssh2-promise": "^0.2.0",
"v-mask": "^2.2.4", "v-mask": "^2.2.4",
"vue-i18n": "^8.24.4", "vue-i18n": "^8.24.4",
"vuedraggable": "^2.24.3", "vuedraggable": "^2.24.3",
@@ -127,7 +127,7 @@
"standard-version": "^9.3.0", "standard-version": "^9.3.0",
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"stylelint-config-standard": "^22.0.0", "stylelint-config-standard": "^22.0.0",
"stylelint-scss": "^3.19.0", "stylelint-scss": "^3.20.1",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-template-compiler": "^2.6.14", "vue-template-compiler": "^2.6.14",
"webpack": "^4.46.0" "webpack": "^4.46.0"

View File

@@ -4,9 +4,7 @@ summary: Open source SQL client made to be simple and complete.
description: | description: |
Antares is an SQL client that aims to become an useful and complete tool, especially for developers. Antares is an SQL client that aims to become an useful and complete tool, especially for developers.
The target is to support as many databases as possible, and all major operating systems, including the ARM versions. The target is to support as many databases as possible, and all major operating systems, including the ARM versions.
At the moment this application is an alpha and supports only MySQL and x86 architecture. base: core20
Most of its current features might be enough for MySQL management, so give it a chance and send us your feedback, we would really appreciate it.
base: core18
grade: stable grade: stable
confinement: strict confinement: strict
@@ -60,6 +58,7 @@ parts:
- fcitx-frontend-gtk3 - fcitx-frontend-gtk3
- libappindicator3-1 - libappindicator3-1
- libasound2 - libasound2
- libcurl4
- libgconf-2-4 - libgconf-2-4
- libgtk-3-0 - libgtk-3-0
- libnotify4 - libnotify4
@@ -71,13 +70,59 @@ parts:
- libsecret-1-0 - libsecret-1-0
- libxtst6 - libxtst6
- libxkbfile1 - libxkbfile1
- gcc-10-base
- libapparmor1
- libblkid1
- libbsd0
- libcom-err2
- libcrypt1
- libdb5.3
- libdbus-1-3
- libexpat1
- libffi7
- libgcc-s1
- libgcrypt20
- libglib2.0-0
- libgmp10
- libgnutls30
- libgpg-error0
- libgssapi-krb5-2
- libhogweed5
- libidn2-0
- libjson-c4
- libk5crypto3
- libkeyutils1
- libkrb5-3
- libkrb5support0
- liblz4-1
- liblzma5
- libmount1
- libnettle7
- libp11-kit0
- libpcre2-8-0
- libselinux1
- libsqlite3-0
- libssl1.1
- libstdc++6
- libsystemd0
- libtasn1-6
- libudev1
- libunistring2
- libuuid1
- libwrap0
- libzstd1
- zlib1g
- libx11-xcb1
- libdrm2
- libgbm1
- libxcb-dri3-0
cleanup: cleanup:
after: [antares] after: [antares]
plugin: nil plugin: nil
build-snaps: [gnome-3-28-1804] build-snaps: [gnome-3-38-2004]
override-prime: | override-prime: |
set -eux set -eux
cd /snap/gnome-3-28-1804/current cd /snap/gnome-3-38-2004/current
find . -type f,l -exec rm -f $SNAPCRAFT_PRIME/{} \; find . -type f,l -exec rm -f $SNAPCRAFT_PRIME/{} \;
mdns-lookup: mdns-lookup:
@@ -91,7 +136,7 @@ parts:
- libnss-mdns - libnss-mdns
override-prime: | override-prime: |
set -eux set -eux
sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core18/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core20/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf
snapcraftctl prime snapcraftctl prime
prime: prime:
- lib/$SNAPCRAFT_ARCH_TRIPLET/libnss_mdns4_minimal* - lib/$SNAPCRAFT_ARCH_TRIPLET/libnss_mdns4_minimal*
@@ -101,7 +146,7 @@ apps:
antares: antares:
command: opt/Antares/antares --no-sandbox command: opt/Antares/antares --no-sandbox
desktop: usr/share/applications/antares.desktop desktop: usr/share/applications/antares.desktop
extensions: [gnome-3-28] extensions: [gnome-3-38]
environment: environment:
# Fallback to XWayland if running in a Wayland session. # Fallback to XWayland if running in a Wayland session.
DISABLE_WAYLAND: 1 DISABLE_WAYLAND: 1

View File

@@ -128,7 +128,12 @@ export default connections => {
if (!query) return; if (!query) return;
try { try {
const result = await connections[uid].raw(query, { nest: true, details: true, schema }); const result = await connections[uid].raw(query, {
nest: true,
details: true,
schema,
comments: false
});
return { status: 'success', response: result }; return { status: 'success', response: result };
} }

View File

@@ -29,7 +29,7 @@ export default (connections) => {
if (sortParams && sortParams.field && sortParams.dir) if (sortParams && sortParams.field && sortParams.dir)
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() }); query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
const result = await query.run({ details: true }); const result = await query.run({ details: true, schema });
return { status: 'success', response: result }; return { status: 'success', response: result };
} }

View File

@@ -335,11 +335,11 @@ export class MySQLClient extends AntaresCore {
.select('*') .select('*')
.schema('information_schema') .schema('information_schema')
.from('COLUMNS') .from('COLUMNS')
.where({ TABLE_SCHEMA: `= '${this._schema || schema}'`, TABLE_NAME: `= '${table}'` }) .where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` })
.orderBy({ ORDINAL_POSITION: 'ASC' }) .orderBy({ ORDINAL_POSITION: 'ASC' })
.run(); .run();
const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${this._schema || schema}\`.\`${table}\``); const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${schema}\`.\`${table}\``);
const remappedFields = fields.map(row => { const remappedFields = fields.map(row => {
if (!row['Create Table']) return false; if (!row['Create Table']) return false;
@@ -363,15 +363,14 @@ export class MySQLClient extends AntaresCore {
const details = fieldArr.slice(2).join(' '); const details = fieldArr.slice(2).join(' ');
let defaultValue = null; let defaultValue = null;
if (details.includes('DEFAULT')) { if (details.includes('DEFAULT'))
defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0]; defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0];
const defaultValueArr = defaultValue.split(''); // const defaultValueArr = defaultValue.split('');
if (defaultValueArr[0] === '\'') { // if (defaultValueArr[0] === '\'') {
defaultValueArr.shift(); // defaultValueArr.shift();
defaultValueArr.pop(); // defaultValueArr.pop();
defaultValue = defaultValueArr.join(''); // defaultValue = defaultValueArr.join('');
} // }
}
const typeAndLength = nameAndType[1].replace(')', '').split('('); const typeAndLength = nameAndType[1].replace(')', '').split('(');
@@ -574,7 +573,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropView (params) { async dropView (params) {
const sql = `DROP VIEW \`${this._schema}\`.\`${params.view}\``; const sql = `DROP VIEW \`${params.schema}\`.\`${params.view}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -586,10 +585,10 @@ export class MySQLClient extends AntaresCore {
*/ */
async alterView (params) { async alterView (params) {
const { view } = params; const { view } = params;
let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${this._schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`; let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${view.schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
if (view.name !== view.oldName) if (view.name !== view.oldName)
sql += `; RENAME TABLE \`${this._schema}\`.\`${view.oldName}\` TO \`${this._schema}\`.\`${view.name}\``; sql += `; RENAME TABLE \`${view.schema}\`.\`${view.oldName}\` TO \`${view.schema}\`.\`${view.name}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -600,8 +599,8 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createView (view) { async createView (params) {
const sql = `CREATE ALGORITHM = ${view.algorithm} ${view.definer ? `DEFINER=${view.definer} ` : ''}SQL SECURITY ${view.security} VIEW \`${this._schema}\`.\`${view.name}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`; const sql = `CREATE ALGORITHM = ${params.algorithm} ${params.definer ? `DEFINER=${params.definer} ` : ''}SQL SECURITY ${params.security} VIEW \`${params.schema}\`.\`${params.name}\` AS ${params.sql} ${params.updateOption ? `WITH ${params.updateOption} CHECK OPTION` : ''}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -634,7 +633,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropTrigger (params) { async dropTrigger (params) {
const sql = `DROP TRIGGER \`${this._schema}\`.\`${params.trigger}\``; const sql = `DROP TRIGGER \`${params.schema}\`.\`${params.trigger}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -651,8 +650,8 @@ export class MySQLClient extends AntaresCore {
try { try {
await this.createTrigger(tempTrigger); await this.createTrigger(tempTrigger);
await this.dropTrigger({ trigger: tempTrigger.name }); await this.dropTrigger({ schema: trigger.schema, trigger: tempTrigger.name });
await this.dropTrigger({ trigger: trigger.oldName }); await this.dropTrigger({ schema: trigger.schema, trigger: trigger.oldName });
await this.createTrigger(trigger); await this.createTrigger(trigger);
} }
catch (err) { catch (err) {
@@ -666,8 +665,8 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createTrigger (trigger) { async createTrigger (params) {
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${this._schema}\`.\`${trigger.name}\` ${trigger.activation} ${trigger.event} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`; const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}TRIGGER \`${params.schema}\`.\`${params.name}\` ${params.activation} ${params.event} ON \`${params.table}\` FOR EACH ROW ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -741,7 +740,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropRoutine (params) { async dropRoutine (params) {
const sql = `DROP PROCEDURE \`${this._schema}\`.\`${params.routine}\``; const sql = `DROP PROCEDURE \`${params.schema}\`.\`${params.routine}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -758,8 +757,8 @@ export class MySQLClient extends AntaresCore {
try { try {
await this.createRoutine(tempProcedure); await this.createRoutine(tempProcedure);
await this.dropRoutine({ routine: tempProcedure.name }); await this.dropRoutine({ schema: routine.schema, routine: tempProcedure.name });
await this.dropRoutine({ routine: routine.oldName }); await this.dropRoutine({ schema: routine.schema, routine: routine.oldName });
await this.createRoutine(routine); await this.createRoutine(routine);
} }
catch (err) { catch (err) {
@@ -773,21 +772,21 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createRoutine (routine) { async createRoutine (params) {
const parameters = 'parameters' in routine const parameters = 'parameters' in params
? routine.parameters.reduce((acc, curr) => { ? params.parameters.reduce((acc, curr) => {
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`); acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc; return acc;
}, []).join(',') }, []).join(',')
: ''; : '';
const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${this._schema}\`.\`${routine.name}\`(${parameters}) const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}PROCEDURE \`${params.schema}\`.\`${params.name}\`(${parameters})
LANGUAGE SQL LANGUAGE SQL
${routine.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'} ${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
${routine.dataAccess} ${params.dataAccess}
SQL SECURITY ${routine.security} SQL SECURITY ${params.security}
COMMENT '${routine.comment}' COMMENT '${params.comment}'
${routine.sql}`; ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -868,7 +867,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropFunction (params) { async dropFunction (params) {
const sql = `DROP FUNCTION \`${this._schema}\`.\`${params.func}\``; const sql = `DROP FUNCTION \`${params.schema}\`.\`${params.func}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -885,8 +884,8 @@ export class MySQLClient extends AntaresCore {
try { try {
await this.createFunction(tempProcedure); await this.createFunction(tempProcedure);
await this.dropFunction({ func: tempProcedure.name }); await this.dropFunction({ schema: func.schema, func: tempProcedure.name });
await this.dropFunction({ func: func.oldName }); await this.dropFunction({ schema: func.schema, func: func.oldName });
await this.createFunction(func); await this.createFunction(func);
} }
catch (err) { catch (err) {
@@ -900,20 +899,20 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createFunction (func) { async createFunction (params) {
const parameters = func.parameters.reduce((acc, curr) => { const parameters = params.parameters.reduce((acc, curr) => {
acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`); acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc; return acc;
}, []).join(','); }, []).join(',');
const body = func.returns ? func.sql : 'BEGIN\n RETURN 0;\nEND'; const body = params.returns ? params.sql : 'BEGIN\n RETURN 0;\nEND';
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${this._schema}\`.\`${func.name}\`(${parameters}) RETURNS ${func.returns || 'SMALLINT'}${func.returnsLength ? `(${func.returnsLength})` : ''} const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}FUNCTION \`${params.schema}\`.\`${params.name}\`(${parameters}) RETURNS ${params.returns || 'SMALLINT'}${params.returnsLength ? `(${params.returnsLength})` : ''}
LANGUAGE SQL LANGUAGE SQL
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'} ${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
${func.dataAccess} ${params.dataAccess}
SQL SECURITY ${func.security} SQL SECURITY ${params.security}
COMMENT '${func.comment}' COMMENT '${params.comment}'
${body}`; ${body}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
@@ -960,7 +959,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropEvent (params) { async dropEvent (params) {
const sql = `DROP EVENT \`${this._schema}\`.\`${params.scheduler}\``; const sql = `DROP EVENT \`${params.schema}\`.\`${params.scheduler}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -976,13 +975,13 @@ export class MySQLClient extends AntaresCore {
if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-')) if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
scheduler.every[0] = `'${scheduler.every[0]}'`; scheduler.every[0] = `'${scheduler.every[0]}'`;
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${this._schema}\`.\`${scheduler.oldName}\` const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.schema}\`.\`${scheduler.oldName}\`
ON SCHEDULE ON SCHEDULE
${scheduler.execution === 'EVERY' ${scheduler.execution === 'EVERY'
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}` ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
: `AT '${scheduler.at}'`} : `AT '${scheduler.at}'`}
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${this._schema}\`.\`${scheduler.name}\`` : ''} ${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.schema}\`.\`${scheduler.name}\`` : ''}
${scheduler.state} ${scheduler.state}
COMMENT '${scheduler.comment}' COMMENT '${scheduler.comment}'
DO ${scheduler.sql}`; DO ${scheduler.sql}`;
@@ -996,16 +995,16 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createEvent (scheduler) { async createEvent (params) {
const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${this._schema}\`.\`${scheduler.name}\` const sql = `CREATE ${params.definer ? ` DEFINER=${params.definer}` : ''} EVENT \`${params.schema}\`.\`${params.name}\`
ON SCHEDULE ON SCHEDULE
${scheduler.execution === 'EVERY' ${params.execution === 'EVERY'
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}` ? `EVERY ${params.every.join(' ')}${params.starts ? ` STARTS '${params.starts}'` : ''}${params.ends ? ` ENDS '${params.ends}'` : ''}`
: `AT '${scheduler.at}'`} : `AT '${params.at}'`}
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE ON COMPLETION${!params.preserve ? ' NOT' : ''} PRESERVE
${scheduler.state} ${params.state}
COMMENT '${scheduler.comment}' COMMENT '${params.comment}'
DO ${scheduler.sql}`; DO ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -1127,14 +1126,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createTable (params) { async createTable (params) {
const { const sql = `CREATE TABLE \`${params.schema}\`.\`${params.name}\` (\`${params.name}_ID\` INT NULL) COMMENT='${params.comment}', COLLATE='${params.collation}', ENGINE=${params.engine}`;
name,
collation,
comment,
engine
} = params;
const sql = `CREATE TABLE \`${this._schema}\`.\`${name}\` (\`${name}_ID\` INT NULL) COMMENT='${comment}', COLLATE='${collation}', ENGINE=${engine}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1148,6 +1140,7 @@ export class MySQLClient extends AntaresCore {
async alterTable (params) { async alterTable (params) {
const { const {
table, table,
schema,
additions, additions,
deletions, deletions,
changes, changes,
@@ -1156,7 +1149,7 @@ export class MySQLClient extends AntaresCore {
options options
} = params; } = params;
let sql = `ALTER TABLE \`${this._schema || params.options.schema}\`.\`${table}\` `; let sql = `ALTER TABLE \`${schema}\`.\`${table}\` `;
const alterColumns = []; const alterColumns = [];
// OPTIONS // OPTIONS
@@ -1268,7 +1261,7 @@ export class MySQLClient extends AntaresCore {
sql += alterColumns.join(', '); sql += alterColumns.join(', ');
// RENAME // RENAME
if (options.name) sql += `; RENAME TABLE \`${this._schema}\`.\`${table}\` TO \`${this._schema}\`.\`${options.name}\``; if (options.name) sql += `; RENAME TABLE \`${schema}\`.\`${table}\` TO \`${schema}\`.\`${options.name}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1280,7 +1273,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async duplicateTable (params) { async duplicateTable (params) {
const sql = `CREATE TABLE \`${this._schema}\`.\`${params.table}_copy\` LIKE \`${this._schema}\`.\`${params.table}\``; const sql = `CREATE TABLE \`${params.schema}\`.\`${params.table}_copy\` LIKE \`${params.schema}\`.\`${params.table}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1291,7 +1284,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async truncateTable (params) { async truncateTable (params) {
const sql = `TRUNCATE TABLE \`${this._schema}\`.\`${params.table}\``; const sql = `TRUNCATE TABLE \`${params.schema}\`.\`${params.table}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1302,7 +1295,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropTable (params) { async dropTable (params) {
const sql = `DROP TABLE \`${this._schema}\`.\`${params.table}\``; const sql = `DROP TABLE \`${params.schema}\`.\`${params.table}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1373,16 +1366,19 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async raw (sql, args) { async raw (sql, args) {
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
args = { args = {
nest: false, nest: false,
details: false, details: false,
split: true, split: true,
comments: true,
...args ...args
}; };
if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const nestTables = args.nest ? '.' : false; const nestTables = args.nest ? '.' : false;
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];
@@ -1419,7 +1415,7 @@ export class MySQLClient extends AntaresCore {
name: field.orgName, name: field.orgName,
alias: field.name, alias: field.name,
orgName: field.orgName, orgName: field.orgName,
schema: field.schema, schema: args.schema || field.schema,
table: field.table, table: field.table,
tableAlias: field.table, tableAlias: field.table,
orgTable: field.orgTable, orgTable: field.orgTable,

View File

@@ -483,7 +483,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropView (params) { async dropView (params) {
const sql = `DROP VIEW ${this._schema}.${params.view}`; const sql = `DROP VIEW ${params.schema}.${params.view}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -495,10 +495,10 @@ export class PostgreSQLClient extends AntaresCore {
*/ */
async alterView (params) { async alterView (params) {
const { view } = params; const { view } = params;
let sql = `CREATE OR REPLACE VIEW ${this._schema}.${view.oldName} AS ${view.sql}`; let sql = `CREATE OR REPLACE VIEW ${view.schema}.${view.oldName} AS ${view.sql}`;
if (view.name !== view.oldName) if (view.name !== view.oldName)
sql += `; ALTER VIEW ${this._schema}.${view.oldName} RENAME TO ${view.name}`; sql += `; ALTER VIEW ${view.schema}.${view.oldName} RENAME TO ${view.name}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -509,8 +509,8 @@ export class PostgreSQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createView (view) { async createView (params) {
const sql = `CREATE VIEW ${this._schema}.${view.name} AS ${view.sql}`; const sql = `CREATE VIEW ${params.schema}.${params.name} AS ${params.sql}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -560,7 +560,7 @@ export class PostgreSQLClient extends AntaresCore {
*/ */
async dropTrigger (params) { async dropTrigger (params) {
const triggerParts = params.trigger.split('.'); const triggerParts = params.trigger.split('.');
const sql = `DROP TRIGGER "${triggerParts[1]}" ON "${triggerParts[0]}"`; const sql = `DROP TRIGGER "${triggerParts[1]}" ON "${params.schema}"."${triggerParts[0]}"`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -577,8 +577,8 @@ export class PostgreSQLClient extends AntaresCore {
try { try {
await this.createTrigger(tempTrigger); await this.createTrigger(tempTrigger);
await this.dropTrigger({ trigger: `${tempTrigger.table}.${tempTrigger.name}` }); await this.dropTrigger({ schema: trigger.schema, trigger: `${tempTrigger.table}.${tempTrigger.name}` });
await this.dropTrigger({ trigger: `${trigger.table}.${trigger.oldName}` }); await this.dropTrigger({ schema: trigger.schema, trigger: `${trigger.table}.${trigger.oldName}` });
await this.createTrigger(trigger); await this.createTrigger(trigger);
} }
catch (err) { catch (err) {
@@ -592,9 +592,9 @@ export class PostgreSQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createTrigger (trigger) { async createTrigger (params) {
const eventsString = Array.isArray(trigger.event) ? trigger.event.join(' OR ') : trigger.event; const eventsString = Array.isArray(params.event) ? params.event.join(' OR ') : params.event;
const sql = `CREATE TRIGGER "${trigger.name}" ${trigger.activation} ${eventsString} ON "${trigger.table}" FOR EACH ROW ${trigger.sql}`; const sql = `CREATE TRIGGER "${params.name}" ${params.activation} ${eventsString} ON "${params.schema}"."${params.table}" FOR EACH ROW ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -637,6 +637,7 @@ export class PostgreSQLClient extends AntaresCore {
AND proc.routine_type = 'PROCEDURE' AND proc.routine_type = 'PROCEDURE'
AND proc.routine_name = '${routine}' AND proc.routine_name = '${routine}'
AND proc.specific_schema = '${schema}' AND proc.specific_schema = '${schema}'
AND args.data_type != NULL
ORDER BY procedure_schema, ORDER BY procedure_schema,
specific_name, specific_name,
procedure_name, procedure_name,
@@ -675,7 +676,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropRoutine (params) { async dropRoutine (params) {
const sql = `DROP PROCEDURE ${this._schema}.${params.routine}`; const sql = `DROP PROCEDURE "${params.schema}"."${params.routine}"`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -692,8 +693,8 @@ export class PostgreSQLClient extends AntaresCore {
try { try {
await this.createRoutine(tempProcedure); await this.createRoutine(tempProcedure);
await this.dropRoutine({ routine: tempProcedure.name }); await this.dropRoutine({ schema: routine.schema, routine: tempProcedure.name });
await this.dropRoutine({ routine: routine.oldName }); await this.dropRoutine({ schema: routine.schema, routine: routine.oldName });
await this.createRoutine(routine); await this.createRoutine(routine);
} }
catch (err) { catch (err) {
@@ -715,10 +716,10 @@ export class PostgreSQLClient extends AntaresCore {
}, []).join(',') }, []).join(',')
: ''; : '';
if (this._schema !== 'public') if (routine.schema !== 'public')
await this.use(this._schema); await this.use(routine.schema);
const sql = `CREATE PROCEDURE ${this._schema}.${routine.name}(${parameters}) const sql = `CREATE PROCEDURE "${routine.schema}"."${routine.name}"(${parameters})
LANGUAGE ${routine.language} LANGUAGE ${routine.language}
SECURITY ${routine.security} SECURITY ${routine.security}
AS ${routine.sql}`; AS ${routine.sql}`;
@@ -804,7 +805,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropFunction (params) { async dropFunction (params) {
const sql = `DROP FUNCTION ${this._schema}.${params.func}`; const sql = `DROP FUNCTION "${params.schema}"."${params.func}"`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -821,8 +822,8 @@ export class PostgreSQLClient extends AntaresCore {
try { try {
await this.createFunction(tempProcedure); await this.createFunction(tempProcedure);
await this.dropFunction({ func: tempProcedure.name }); await this.dropFunction({ schema: func.schema, func: tempProcedure.name });
await this.dropFunction({ func: func.oldName }); await this.dropFunction({ schema: func.schema, func: func.oldName });
await this.createFunction(func); await this.createFunction(func);
} }
catch (err) { catch (err) {
@@ -839,17 +840,17 @@ export class PostgreSQLClient extends AntaresCore {
async createFunction (func) { async createFunction (func) {
const parameters = 'parameters' in func const parameters = 'parameters' in func
? func.parameters.reduce((acc, curr) => { ? func.parameters.reduce((acc, curr) => {
acc.push(`${curr.context} ${curr.name} ${curr.type}${curr.length ? `(${curr.length})` : ''}`); acc.push(`${curr.context} ${curr.name || ''} ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc; return acc;
}, []).join(',') }, []).join(',')
: ''; : '';
if (this._schema !== 'public') if (func.schema !== 'public')
await this.use(this._schema); await this.use(func.schema);
const body = func.returns ? func.sql : '$function$\n$function$'; const body = func.returns ? func.sql : '$function$\n$function$';
const sql = `CREATE FUNCTION ${this._schema}.${func.name}(${parameters}) const sql = `CREATE FUNCTION "${func.schema}"."${func.name}" (${parameters})
RETURNS ${func.returns || 'void'} RETURNS ${func.returns || 'void'}
LANGUAGE ${func.language} LANGUAGE ${func.language}
SECURITY ${func.security} SECURITY ${func.security}
@@ -867,12 +868,12 @@ export class PostgreSQLClient extends AntaresCore {
async alterTriggerFunction (params) { async alterTriggerFunction (params) {
const { func } = params; const { func } = params;
if (this._schema !== 'public') if (func.schema !== 'public')
await this.use(this._schema); await this.use(func.schema);
const body = func.returns ? func.sql : '$function$\n$function$'; const body = func.returns ? func.sql : '$function$\n$function$';
const sql = `CREATE OR REPLACE FUNCTION ${this._schema}.${func.name}() const sql = `CREATE OR REPLACE FUNCTION "${func.schema}"."${func.name}" ()
RETURNS TRIGGER RETURNS TRIGGER
LANGUAGE ${func.language} LANGUAGE ${func.language}
AS ${body}`; AS ${body}`;
@@ -887,12 +888,12 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createTriggerFunction (func) { async createTriggerFunction (func) {
if (this._schema !== 'public') if (func.schema !== 'public')
await this.use(this._schema); await this.use(func.schema);
const body = func.returns ? func.sql : '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$'; const body = func.returns ? func.sql : '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$';
const sql = `CREATE FUNCTION ${this._schema}.${func.name}() const sql = `CREATE FUNCTION "${func.schema}"."${func.name}" ()
RETURNS TRIGGER RETURNS TRIGGER
LANGUAGE ${func.language} LANGUAGE ${func.language}
AS ${body}`; AS ${body}`;
@@ -988,11 +989,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createTable (params) { async createTable (params) {
const { const sql = `CREATE TABLE "${params.schema}"."${params.name}" (${params.name}_id INTEGER NULL); ALTER TABLE "${params.schema}"."${params.name}" DROP COLUMN ${params.name}_id`;
name
} = params;
const sql = `CREATE TABLE ${this._schema}.${name} (${name}_id INTEGER NULL); ALTER TABLE ${this._schema}.${name} DROP COLUMN ${name}_id`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1006,6 +1003,7 @@ export class PostgreSQLClient extends AntaresCore {
async alterTable (params) { async alterTable (params) {
const { const {
table, table,
schema,
additions, additions,
deletions, deletions,
changes, changes,
@@ -1014,8 +1012,8 @@ export class PostgreSQLClient extends AntaresCore {
options options
} = params; } = params;
if (this._schema !== 'public') if (schema !== 'public')
await this.use(this._schema); await this.use(schema);
let sql = ''; let sql = '';
const alterColumns = []; const alterColumns = [];
@@ -1056,7 +1054,7 @@ export class PostgreSQLClient extends AntaresCore {
else if (type === 'UNIQUE') else if (type === 'UNIQUE')
alterColumns.push(`ADD CONSTRAINT ${addition.name} UNIQUE (${fields})`); alterColumns.push(`ADD CONSTRAINT ${addition.name} UNIQUE (${fields})`);
else else
manageIndexes.push(`CREATE INDEX ${addition.name} ON ${this._schema}.${table}(${fields})`); manageIndexes.push(`CREATE INDEX ${addition.name} ON "${schema}"."${table}" (${fields})`);
}); });
// ADD FOREIGN KEYS // ADD FOREIGN KEYS
@@ -1094,7 +1092,7 @@ export class PostgreSQLClient extends AntaresCore {
} }
if (change.orgName !== change.name) if (change.orgName !== change.name)
renameColumns.push(`ALTER TABLE "${this._schema}"."${table}" RENAME COLUMN "${change.orgName}" TO "${change.name}"`); renameColumns.push(`ALTER TABLE "${schema}"."${table}" RENAME COLUMN "${change.orgName}" TO "${change.name}"`);
}); });
// CHANGE INDEX // CHANGE INDEX
@@ -1112,7 +1110,7 @@ export class PostgreSQLClient extends AntaresCore {
else if (type === 'UNIQUE') else if (type === 'UNIQUE')
alterColumns.push(`ADD CONSTRAINT ${change.name} UNIQUE (${fields})`); alterColumns.push(`ADD CONSTRAINT ${change.name} UNIQUE (${fields})`);
else else
manageIndexes.push(`CREATE INDEX ${change.name} ON ${this._schema}.${table}(${fields})`); manageIndexes.push(`CREATE INDEX ${change.name} ON "${schema}"."${table}" (${fields})`);
}); });
// CHANGE FOREIGN KEYS // CHANGE FOREIGN KEYS
@@ -1139,10 +1137,10 @@ export class PostgreSQLClient extends AntaresCore {
alterColumns.push(`DROP CONSTRAINT ${deletion.constraintName}`); alterColumns.push(`DROP CONSTRAINT ${deletion.constraintName}`);
}); });
if (alterColumns.length) sql += `ALTER TABLE "${this._schema}"."${table}" ${alterColumns.join(', ')}; `; if (alterColumns.length) sql += `ALTER TABLE "${schema}"."${table}" ${alterColumns.join(', ')}; `;
if (createSequences.length) sql = `${createSequences.join(';')}; ${sql}`; if (createSequences.length) sql = `${createSequences.join(';')}; ${sql}`;
if (manageIndexes.length) sql = `${manageIndexes.join(';')}; ${sql}`; if (manageIndexes.length) sql = `${manageIndexes.join(';')}; ${sql}`;
if (options.name) sql += `ALTER TABLE "${this._schema}"."${table}" RENAME TO "${options.name}"; `; if (options.name) sql += `ALTER TABLE "${schema}"."${table}" RENAME TO "${options.name}"; `;
// RENAME // RENAME
if (renameColumns.length) sql = `${renameColumns.join(';')}; ${sql}`; if (renameColumns.length) sql = `${renameColumns.join(';')}; ${sql}`;
@@ -1157,7 +1155,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async duplicateTable (params) { async duplicateTable (params) {
const sql = `CREATE TABLE ${this._schema}.${params.table}_copy (LIKE ${this._schema}.${params.table} INCLUDING ALL)`; const sql = `CREATE TABLE ${params.schema}.${params.table}_copy (LIKE ${params.schema}.${params.table} INCLUDING ALL)`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1168,7 +1166,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async truncateTable (params) { async truncateTable (params) {
const sql = `TRUNCATE TABLE ${this._schema}.${params.table}`; const sql = `TRUNCATE TABLE ${params.schema}.${params.table}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1179,7 +1177,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropTable (params) { async dropTable (params) {
const sql = `DROP TABLE ${this._schema}.${params.table}`; const sql = `DROP TABLE ${params.schema}.${params.table}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1250,18 +1248,20 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async raw (sql, args) { async raw (sql, args) {
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
args = { args = {
nest: false, nest: false,
details: false, details: false,
split: true, split: true,
comments: true,
...args ...args
}; };
if (args.schema && args.schema !== 'public') if (args.schema && args.schema !== 'public')
await this.use(args.schema); await this.use(args.schema);
if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];
const queries = args.split const queries = args.split

View File

@@ -16,7 +16,6 @@
<TheNotificationsBoard /> <TheNotificationsBoard />
<TheScratchpad v-if="isScratchpad" /> <TheScratchpad v-if="isScratchpad" />
<ModalSettings v-if="isSettingModal" /> <ModalSettings v-if="isSettingModal" />
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
<BaseTextEditor class="d-none" value="" /> <BaseTextEditor class="d-none" value="" />
</div> </div>
</div> </div>
@@ -39,7 +38,6 @@ export default {
WorkspaceAddConnectionPanel: () => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel'), WorkspaceAddConnectionPanel: () => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel'),
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'), ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'), TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges'),
BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor') BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor')
}, },
data () { data () {

View File

@@ -41,7 +41,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<button class="btn btn-primary mr-2" @click.stop="sendCredentials"> <button class="btn btn-primary mr-2" @click.stop="sendCredentials">
{{ $t('word.send') }} {{ $t('word.send') }}
</button> </button>

View File

@@ -2,8 +2,8 @@
<ConfirmModal <ConfirmModal
:confirm-text="$t('word.discard')" :confirm-text="$t('word.discard')"
:cancel-text="$t('word.stay')" :cancel-text="$t('word.stay')"
@confirm="discardUnsavedChanges" @confirm="$emit('confirm')"
@hide="closeUnsavedChangesModal" @hide="$emit('close')"
> >
<template slot="header"> <template slot="header">
<div class="d-flex"> <div class="d-flex">
@@ -19,7 +19,6 @@
</template> </template>
<script> <script>
import { mapActions } from 'vuex';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
export default { export default {
@@ -34,13 +33,6 @@ export default {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
discardUnsavedChanges: 'workspaces/discardUnsavedChanges',
closeUnsavedChangesModal: 'workspaces/closeUnsavedChangesModal'
}),
closeModal () {
this.$emit('close');
},
onKey (e) { onKey (e) {
e.stopPropagation(); e.stopPropagation();
if (e.key === 'Escape') if (e.key === 'Escape')

View File

@@ -53,7 +53,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<button class="btn btn-primary mr-2" @click.stop="updateSchema"> <button class="btn btn-primary mr-2" @click.stop="updateSchema">
{{ $t('word.update') }} {{ $t('word.update') }}
</button> </button>
@@ -72,7 +72,7 @@ import Schema from '@/ipc-api/Schema';
export default { export default {
name: 'ModalEditSchema', name: 'ModalEditSchema',
props: { props: {
selectedDatabase: String selectedSchema: String
}, },
data () { data () {
return { return {
@@ -99,7 +99,7 @@ export default {
async created () { async created () {
let actualCollation; let actualCollation;
try { try {
const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase }); const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedSchema });
if (status === 'success') if (status === 'success')
actualCollation = response; actualCollation = response;
@@ -112,8 +112,8 @@ export default {
} }
this.database = { this.database = {
name: this.selectedDatabase, name: this.selectedSchema,
prevName: this.selectedDatabase, prevName: this.selectedSchema,
collation: actualCollation || this.defaultCollation, collation: actualCollation || this.defaultCollation,
prevCollation: actualCollation || this.defaultCollation prevCollation: actualCollation || this.defaultCollation
}; };

View File

@@ -52,7 +52,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light columns"> <div class="modal-footer columns">
<div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'"> <div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'">
<div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')"> <div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
<input <input

View File

@@ -49,7 +49,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<button <button
class="btn btn-primary mr-2" class="btn btn-primary mr-2"
:class="{'loading': isLoading}" :class="{'loading': isLoading}"

View File

@@ -86,7 +86,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')"> <div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
<input <input
v-model="nInserts" v-model="nInserts"

View File

@@ -104,6 +104,19 @@
</select> </select>
</div> </div>
</div> </div>
<div class="form-group">
<div class="col-6 col-sm-12">
<label class="form-label">
{{ $t('message.restorePreviourSession') }}
</label>
</div>
<div class="col-6 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleRestoreSession">
<input type="checkbox" :checked="restoreTabs">
<i class="form-icon" />
</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="col-6 col-sm-12"> <div class="col-6 col-sm-12">
<label class="form-label"> <label class="form-label">
@@ -369,6 +382,7 @@ export default {
selectedAutoComplete: 'settings/getAutoComplete', selectedAutoComplete: 'settings/getAutoComplete',
selectedLineWrap: 'settings/getLineWrap', selectedLineWrap: 'settings/getLineWrap',
notificationsTimeout: 'settings/getNotificationsTimeout', notificationsTimeout: 'settings/getNotificationsTimeout',
restoreTabs: 'settings/getRestoreTabs',
applicationTheme: 'settings/getApplicationTheme', applicationTheme: 'settings/getApplicationTheme',
editorTheme: 'settings/getEditorTheme', editorTheme: 'settings/getEditorTheme',
editorFontSize: 'settings/getEditorFontSize', editorFontSize: 'settings/getEditorFontSize',
@@ -423,6 +437,7 @@ ORDER BY
closeModal: 'application/hideSettingModal', closeModal: 'application/hideSettingModal',
changeLocale: 'settings/changeLocale', changeLocale: 'settings/changeLocale',
changePageSize: 'settings/changePageSize', changePageSize: 'settings/changePageSize',
changeRestoreTabs: 'settings/changeRestoreTabs',
changeAutoComplete: 'settings/changeAutoComplete', changeAutoComplete: 'settings/changeAutoComplete',
changeLineWrap: 'settings/changeLineWrap', changeLineWrap: 'settings/changeLineWrap',
changeApplicationTheme: 'settings/changeApplicationTheme', changeApplicationTheme: 'settings/changeApplicationTheme',
@@ -447,6 +462,9 @@ ORDER BY
if (e.key === 'Escape') if (e.key === 'Escape')
this.closeModal(); this.closeModal();
}, },
toggleRestoreSession () {
this.changeRestoreTabs(!this.restoreTabs);
},
toggleAutoComplete () { toggleAutoComplete () {
this.changeAutoComplete(!this.selectedAutoComplete); this.changeAutoComplete(!this.selectedAutoComplete);
}, },

View File

@@ -8,21 +8,25 @@
@close-context="isContext = false" @close-context="isContext = false"
/> />
<ul class="settingbar-elements"> <ul class="settingbar-elements">
<draggable v-model="connections"> <Draggable
v-model="connections"
@start="isDragging = true"
@end="dragStop"
>
<li <li
v-for="connection in connections" v-for="connection in connections"
:key="connection.uid" :key="connection.uid"
draggable="true" draggable="true"
class="settingbar-element btn btn-link ex-tooltip" class="settingbar-element btn btn-link ex-tooltip"
:class="{'selected': connection.uid === selectedWorkspace}" :class="{'selected': connection.uid === selectedWorkspace}"
@click="selectWorkspace(connection.uid)" @click.stop="selectWorkspace(connection.uid)"
@contextmenu.prevent="contextMenu($event, connection)" @contextmenu.prevent="contextMenu($event, connection)"
@mouseover.self="tooltipPosition" @mouseover.self="tooltipPosition"
> >
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" /> <i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" />
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span> <span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
</li> </li>
</draggable> </Draggable>
<li <li
class="settingbar-element btn btn-link ex-tooltip" class="settingbar-element btn btn-link ex-tooltip"
:class="{'selected': 'NEW' === selectedWorkspace}" :class="{'selected': 'NEW' === selectedWorkspace}"
@@ -52,19 +56,20 @@
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import SettingBarContext from '@/components/SettingBarContext'; import SettingBarContext from '@/components/SettingBarContext';
export default { export default {
name: 'TheSettingBar', name: 'TheSettingBar',
components: { components: {
draggable, Draggable,
SettingBarContext SettingBarContext
}, },
data () { data () {
return { return {
dragElement: null, dragElement: null,
isContext: false, isContext: false,
isDragging: false,
contextEvent: null, contextEvent: null,
contextConnection: {}, contextConnection: {},
scale: 0 scale: 0
@@ -106,7 +111,7 @@ export default {
return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`; return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`;
}, },
tooltipPosition (e) { tooltipPosition (e) {
const el = e.target; const el = e.target ? e.target : e;
const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4); const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4);
el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`; el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`;
}, },
@@ -125,6 +130,13 @@ export default {
return ''; return '';
} }
} }
},
dragStop (e) {
this.isDragging = false;
setTimeout(() => {
this.tooltipPosition(e.originalEvent.target.parentNode);
}, 200);
} }
} }
}; };
@@ -235,7 +247,13 @@ export default {
transition: opacity 0.2s; transition: opacity 0.2s;
} }
&:hover .ex-tooltip-content { &.sortable-chosen {
.ex-tooltip-content {
opacity: 0 !important;
}
}
&:hover:not(.selected) .ex-tooltip-content {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
} }

View File

@@ -6,12 +6,163 @@
:is-selected="isSelected" :is-selected="isSelected"
/> />
<div v-if="workspace.connection_status === 'connected'" class="workspace-tabs column columns col-gapless"> <div v-if="workspace.connection_status === 'connected'" class="workspace-tabs column columns col-gapless">
<ul <Draggable
id="tabWrap"
ref="tabWrap" ref="tabWrap"
v-model="draggableTabs"
tag="ul"
group="tabs"
class="tab tab-block column col-12" class="tab tab-block column col-12"
draggable=".tab-draggable"
@mouseover.native="addWheelEvent"
> >
<li class="tab-item dropdown tools-dropdown"> <li
v-for="(tab, i) of draggableTabs"
:key="i"
class="tab-item tab-draggable"
draggable="true"
:class="{'active': selectedTab === tab.uid}"
@mousedown.left="selectTab({uid: workspace.uid, tab: tab.uid})"
@mouseup.middle="closeTab(tab)"
>
<a v-if="tab.type === 'query'" class="tab-link">
<i class="mdi mdi-18px mdi-code-tags mr-1" />
<span>
Query #{{ tab.index }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'temp-data'"
class="tab-link"
@dblclick="openAsPermanentTab(tab)"
>
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
<span :title="`${$t('word.data').toUpperCase()}: ${tab.elementType}`">
<span class=" text-italic">{{ tab.elementName }}</span>
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a v-else-if="tab.type === 'data'" class="tab-link">
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
<span :title="`${$t('word.data').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'table-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'view-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type.includes('temp-')"
class="tab-link"
:class="{'badge': tab.isChanged}"
@dblclick="openAsPermanentTab(tab)"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
<span class=" text-italic">{{ tab.elementName }}</span>
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<!-- <a
v-else-if="tab.type === 'temp-trigger-function-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
@dblclick="openAsPermanentTab(tab)"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
<span class=" text-italic">{{ tab.elementName }}</span>
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'trigger-function-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a> -->
</li>
<li slot="header" class="tab-item dropdown tools-dropdown">
<a <a
class="tab-link workspace-tools-link dropdown-toggle" class="tab-link workspace-tools-link dropdown-toggle"
tabindex="0" tabindex="0"
@@ -48,113 +199,97 @@
</li> </li>
</ul> </ul>
</li> </li>
<li <li slot="footer" class="tab-item">
v-if="schemaChild && isSettingSupported"
class="tab-item"
:class="{'active': selectedTab === 'prop'}"
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
>
<a class="tab-link">
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="schemaChild">{{ $t('word.settings').toUpperCase() }}: {{ schemaChild }}</span>
</a>
</li>
<li
v-if="workspace.breadcrumbs.table || workspace.breadcrumbs.view"
class="tab-item"
:class="{'active': selectedTab === 'data'}"
@click="selectTab({uid: workspace.uid, tab: 'data'})"
>
<a class="tab-link">
<i class="mdi mdi-18px mr-1" :class="workspace.breadcrumbs.table ? 'mdi-table' : 'mdi-table-eye'" />
<span :title="schemaChild">{{ $t('word.data').toUpperCase() }}: {{ schemaChild }}</span>
</a>
</li>
<li
v-for="tab of queryTabs"
:key="tab.uid"
class="tab-item"
:class="{'active': selectedTab === tab.uid}"
@click="selectTab({uid: workspace.uid, tab: tab.uid})"
@mouseup.middle="closeTab(tab.uid)"
>
<a class="tab-link">
<i class="mdi mdi-18px mdi-code-tags mr-1" />
<span>
Query #{{ tab.index }}
<span
v-if="queryTabs.length > 1"
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab.uid)"
/>
</span>
</a>
</li>
<li class="tab-item">
<a <a
class="tab-add" class="tab-add"
:title="$t('message.openNewTab')" :title="$t('message.openNewTab')"
@click="addTab" @click="addQueryTab"
> >
<i class="mdi mdi-24px mdi-plus" /> <i class="mdi mdi-24px mdi-plus" />
</a> </a>
</li> </li>
</ul> </Draggable>
<WorkspacePropsTab <!--<WorkspacePropsTabScheduler
v-show="selectedTab === 'prop' && workspace.breadcrumbs.table"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:table="workspace.breadcrumbs.table"
/>
<WorkspacePropsTabView
v-show="selectedTab === 'prop' && workspace.breadcrumbs.view"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:view="workspace.breadcrumbs.view"
/>
<WorkspacePropsTabTrigger
v-show="selectedTab === 'prop' && workspace.breadcrumbs.trigger"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:trigger="workspace.breadcrumbs.trigger"
/>
<WorkspacePropsTabRoutine
v-show="selectedTab === 'prop' && workspace.breadcrumbs.procedure"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:routine="workspace.breadcrumbs.procedure"
/>
<WorkspacePropsTabFunction
v-show="selectedTab === 'prop' && workspace.breadcrumbs.function"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:function="workspace.breadcrumbs.function"
/>
<WorkspacePropsTabTriggerFunction
v-show="selectedTab === 'prop' && workspace.breadcrumbs.triggerFunction"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:function="workspace.breadcrumbs.triggerFunction"
/>
<WorkspacePropsTabScheduler
v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler" v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler"
:is-selected="selectedTab === 'prop'" :is-selected="selectedTab === 'prop'"
:connection="connection" :connection="connection"
:scheduler="workspace.breadcrumbs.scheduler" :scheduler="workspace.breadcrumbs.scheduler"
/> /> -->
<WorkspaceTableTab <WorkspaceEmptyState v-if="!workspace.tabs.length" @new-tab="addQueryTab" />
v-show="selectedTab === 'data'" <template v-for="tab of workspace.tabs">
:connection="connection" <WorkspaceQueryTab
:table="workspace.breadcrumbs.table || workspace.breadcrumbs.view" v-if="tab.type==='query'"
/> :key="tab.uid"
<WorkspaceQueryTab :tab="tab"
v-for="tab of queryTabs" :is-selected="selectedTab === tab.uid"
:key="tab.uid" :connection="connection"
:tab="tab" />
:is-selected="selectedTab === tab.uid" <WorkspaceTableTab
:connection="connection" v-else-if="['temp-data', 'data'].includes(tab.type)"
/> :key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:table="tab.elementName"
:schema="tab.schema"
:element-type="tab.elementType"
/>
<WorkspacePropsTab
v-else-if="tab.type === 'table-props'"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:table="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabView
v-else-if="tab.type === 'view-props'"
:key="tab.uid"
:is-selected="selectedTab === tab.uid"
:connection="connection"
:view="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabTrigger
v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:trigger="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabTriggerFunction
v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:function="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabRoutine
v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:routine="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabFunction
v-else-if="['temp-function-props', 'function-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:function="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabScheduler
v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:scheduler="tab.elementName"
:schema="tab.schema"
/>
</template>
</div> </div>
<WorkspaceEditConnectionPanel v-else :connection="connection" /> <WorkspaceEditConnectionPanel v-else :connection="connection" />
<ModalProcessesList <ModalProcessesList
@@ -162,12 +297,20 @@
:connection="connection" :connection="connection"
@close="hideProcessesModal" @close="hideProcessesModal"
/> />
<ModalDiscardChanges
v-if="unsavedTab"
@confirm="closeTab(unsavedTab, true)"
@close="unsavedTab = null"
/>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import Draggable from 'vuedraggable';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState';
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar'; import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel'; import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel';
import WorkspaceQueryTab from '@/components/WorkspaceQueryTab'; import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
@@ -175,15 +318,18 @@ import WorkspaceTableTab from '@/components/WorkspaceTableTab';
import WorkspacePropsTab from '@/components/WorkspacePropsTab'; import WorkspacePropsTab from '@/components/WorkspacePropsTab';
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView'; import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger'; import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
import WorkspacePropsTabTriggerFunction from '@/components/WorkspacePropsTabTriggerFunction';
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine'; import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction'; import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
import WorkspacePropsTabTriggerFunction from '@/components/WorkspacePropsTabTriggerFunction';
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler'; import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
import ModalProcessesList from '@/components/ModalProcessesList'; import ModalProcessesList from '@/components/ModalProcessesList';
import ModalDiscardChanges from '@/components/ModalDiscardChanges';
export default { export default {
name: 'Workspace', name: 'Workspace',
components: { components: {
Draggable,
WorkspaceEmptyState,
WorkspaceExploreBar, WorkspaceExploreBar,
WorkspaceEditConnectionPanel, WorkspaceEditConnectionPanel,
WorkspaceQueryTab, WorkspaceQueryTab,
@@ -191,11 +337,12 @@ export default {
WorkspacePropsTab, WorkspacePropsTab,
WorkspacePropsTabView, WorkspacePropsTabView,
WorkspacePropsTabTrigger, WorkspacePropsTabTrigger,
WorkspacePropsTabTriggerFunction,
WorkspacePropsTabRoutine, WorkspacePropsTabRoutine,
WorkspacePropsTabFunction, WorkspacePropsTabFunction,
WorkspacePropsTabTriggerFunction,
WorkspacePropsTabScheduler, WorkspacePropsTabScheduler,
ModalProcessesList ModalProcessesList,
ModalDiscardChanges
}, },
props: { props: {
connection: Object connection: Object
@@ -203,7 +350,8 @@ export default {
data () { data () {
return { return {
hasWheelEvent: false, hasWheelEvent: false,
isProcessesModal: false isProcessesModal: false,
unsavedTab: null
}; };
}, },
computed: { computed: {
@@ -214,6 +362,14 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
draggableTabs: {
get () {
return this.workspace.tabs;
},
set (val) {
this.updateTabs({ uid: this.connection.uid, tabs: val });
}
},
isSelected () { isSelected () {
return this.selectedWorkspace === this.connection.uid; return this.selectedWorkspace === this.connection.uid;
}, },
@@ -228,29 +384,7 @@ export default {
return false; return false;
}, },
selectedTab () { selectedTab () {
if ( return this.workspace.selected_tab;
(
this.workspace.breadcrumbs.table === null &&
this.workspace.breadcrumbs.view === null &&
this.workspace.breadcrumbs.trigger === null &&
this.workspace.breadcrumbs.procedure === null &&
this.workspace.breadcrumbs.function === null &&
this.workspace.breadcrumbs.triggerFunction === null &&
this.workspace.breadcrumbs.scheduler === null &&
['data', 'prop'].includes(this.workspace.selected_tab)
) ||
(
this.workspace.breadcrumbs.table === null &&
this.workspace.breadcrumbs.view === null &&
this.workspace.selected_tab === 'data'
)
)
return this.queryTabs[0].uid;
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
['data', 'prop'].includes(this.workspace.selected_tab)
? this.workspace.selected_tab
: this.queryTabs[0].uid;
}, },
queryTabs () { queryTabs () {
return this.workspace.tabs.filter(tab => tab.type === 'query'); return this.workspace.tabs.filter(tab => tab.type === 'query');
@@ -269,14 +403,6 @@ export default {
if (isInitiated) if (isInitiated)
this.connectWorkspace(this.connection); this.connectWorkspace(this.connection);
}, },
mounted () {
if (this.$refs.tabWrap) {
this.$refs.tabWrap.addEventListener('wheel', e => {
if (e.deltaY > 0) this.$refs.tabWrap.scrollLeft += 50;
else this.$refs.tabWrap.scrollLeft -= 50;
});
}
},
methods: { methods: {
...mapActions({ ...mapActions({
addWorkspace: 'workspaces/addWorkspace', addWorkspace: 'workspaces/addWorkspace',
@@ -284,28 +410,55 @@ export default {
removeConnected: 'workspaces/removeConnected', removeConnected: 'workspaces/removeConnected',
selectTab: 'workspaces/selectTab', selectTab: 'workspaces/selectTab',
newTab: 'workspaces/newTab', newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab' removeTab: 'workspaces/removeTab',
updateTabs: 'workspaces/updateTabs'
}), }),
addTab () { addQueryTab () {
this.newTab({ uid: this.connection.uid }); this.newTab({ uid: this.connection.uid, type: 'query' });
if (!this.hasWheelEvent) {
this.$refs.tabWrap.addEventListener('wheel', e => {
if (e.deltaY > 0) this.$refs.tabWrap.scrollLeft += 50;
else this.$refs.tabWrap.scrollLeft -= 50;
});
this.hasWheelEvent = true;
}
}, },
closeTab (tUid) { openAsPermanentTab (tab) {
if (this.queryTabs.length === 1) return; const permanentTabs = {
this.removeTab({ uid: this.connection.uid, tab: tUid }); table: 'data',
view: 'data',
trigger: 'trigger-props',
triggerFunction: 'trigger-function-props',
function: 'function-props',
routine: 'routine-props',
scheduler: 'scheduler-props'
};
this.newTab({
uid: this.connection.uid,
schema: tab.schema,
elementName: tab.elementName,
type: permanentTabs[tab.elementType],
elementType: tab.elementType
});
},
closeTab (tab, force) {
this.unsavedTab = null;
// if (tab.type === 'query' && this.queryTabs.length === 1) return;
if (!force && tab.isChanged) {
this.unsavedTab = tab;
return;
}
this.removeTab({ uid: this.connection.uid, tab: tab.uid });
}, },
showProcessesModal () { showProcessesModal () {
this.isProcessesModal = true; this.isProcessesModal = true;
}, },
hideProcessesModal () { hideProcessesModal () {
this.isProcessesModal = false; this.isProcessesModal = false;
},
addWheelEvent () {
if (!this.hasWheelEvent) {
this.$refs.tabWrap.$el.addEventListener('wheel', e => {
if (e.deltaY > 0) this.$refs.tabWrap.$el.scrollLeft += 50;
else this.$refs.tabWrap.$el.scrollLeft -= 50;
});
this.hasWheelEvent = true;
}
} }
} }
}; };
@@ -334,20 +487,35 @@ export default {
} }
.tab-item { .tab-item {
max-width: 12rem;
width: fit-content; width: fit-content;
flex: initial; flex: initial;
> a { > a {
padding: 0.2rem 0.8rem; padding: 0.2rem 0.6rem;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
opacity: 0.7; opacity: 0.7;
transition: opacity 0.2s; transition: opacity 0.2s;
&.badge::after {
position: absolute;
right: 35px;
top: 25px;
}
.btn-clear {
margin-left: 0.5rem;
opacity: 0;
transition: opacity 0.2s;
}
&:hover { &:hover {
opacity: 1; opacity: 1;
.btn-clear {
opacity: 1;
}
} }
&.tab-add { &.tab-add {
@@ -366,9 +534,15 @@ export default {
&.active a { &.active a {
opacity: 1; opacity: 1;
.btn-clear {
opacity: 1;
}
} }
&.tools-dropdown { &.tools-dropdown {
height: 34px;
.tab-link:focus { .tab-link:focus {
opacity: 1; opacity: 1;
outline: 0; outline: 0;

View File

@@ -0,0 +1,55 @@
<template>
<div class="column col-12 empty">
<div class="empty-icon">
<img :src="require(`@/images/logo-${applicationTheme}.svg`).default" width="200">
</div>
<p class="h6 empty-subtitle">
{{ $t('message.noOpenTabs') }}
</p>
<div class="empty-action">
<button class="btn btn-gray d-flex" @click="$emit('new-tab')">
<i class="mdi mdi-24px mdi-tab-plus mr-2" />
{{ $t('message.openNewTab') }}
</button>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'WorkspaceEmptyState',
computed: {
...mapGetters({
applicationTheme: 'settings/getApplicationTheme',
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected'
}),
workspace () {
return this.getWorkspace(this.selectedWorkspace);
}
},
created () {
this.changeBreadcrumbs({ schema: this.workspace.breadcrumbs.schema });
},
methods: {
...mapActions({
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
})
}
};
</script>
<style scoped>
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-left: auto;
margin-right: auto;
}
</style>

View File

@@ -21,7 +21,7 @@
@click="refresh" @click="refresh"
/> />
<i <i
class="mdi mdi-18px mdi-power-plug-off c-hand" class="mdi mdi-18px mdi-power c-hand"
:title="$t('word.disconnect')" :title="$t('word.disconnect')"
@click="disconnectWorkspace(connection.uid)" @click="disconnectWorkspace(connection.uid)"
/> />
@@ -100,7 +100,7 @@
/> />
<DatabaseContext <DatabaseContext
v-if="isDatabaseContext" v-if="isDatabaseContext"
:selected-database="selectedDatabase" :selected-schema="selectedSchema"
:context-event="databaseContextEvent" :context-event="databaseContextEvent"
@close-context="closeDatabaseContext" @close-context="closeDatabaseContext"
@show-create-table-modal="showCreateTableModal" @show-create-table-modal="showCreateTableModal"
@@ -114,6 +114,7 @@
/> />
<TableContext <TableContext
v-if="isTableContext" v-if="isTableContext"
:selected-schema="selectedSchema"
:selected-table="selectedTable" :selected-table="selectedTable"
:context-event="tableContextEvent" :context-event="tableContextEvent"
@close-context="closeTableContext" @close-context="closeTableContext"
@@ -122,6 +123,7 @@
<MiscContext <MiscContext
v-if="isMiscContext" v-if="isMiscContext"
:selected-misc="selectedMisc" :selected-misc="selectedMisc"
:selected-schema="selectedSchema"
:context-event="miscContextEvent" :context-event="miscContextEvent"
@close-context="closeMiscContext" @close-context="closeMiscContext"
@reload="refresh" @reload="refresh"
@@ -129,6 +131,7 @@
<MiscFolderContext <MiscFolderContext
v-if="isMiscFolderContext" v-if="isMiscFolderContext"
:selected-misc="selectedMisc" :selected-misc="selectedMisc"
:selected-schema="selectedSchema"
:context-event="miscContextEvent" :context-event="miscContextEvent"
@show-create-trigger-modal="showCreateTriggerModal" @show-create-trigger-modal="showCreateTriggerModal"
@show-create-routine-modal="showCreateRoutineModal" @show-create-routine-modal="showCreateRoutineModal"
@@ -211,7 +214,7 @@ export default {
tableContextEvent: null, tableContextEvent: null,
miscContextEvent: null, miscContextEvent: null,
selectedDatabase: '', selectedSchema: '',
selectedTable: null, selectedTable: null,
selectedMisc: null, selectedMisc: null,
searchTerm: '' searchTerm: ''
@@ -271,6 +274,7 @@ export default {
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
selectTab: 'workspaces/selectTab', selectTab: 'workspaces/selectTab',
newTab: 'workspaces/newTab',
setSearchTerm: 'workspaces/setSearchTerm', setSearchTerm: 'workspaces/setSearchTerm',
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
changeExplorebarSize: 'settings/changeExplorebarSize' changeExplorebarSize: 'settings/changeExplorebarSize'
@@ -308,6 +312,7 @@ export default {
async openCreateTableEditor (payload) { async openCreateTableEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -315,14 +320,19 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, table: payload.name }); this.newTab({
this.selectTab({ uid: this.workspace.uid, tab: 'prop' }); uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'table',
type: 'table-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
}, },
openSchemaContext (payload) { openSchemaContext (payload) {
this.selectedDatabase = payload.schema; this.selectedSchema = payload.schema;
this.databaseContextEvent = payload.event; this.databaseContextEvent = payload.event;
this.isDatabaseContext = true; this.isDatabaseContext = true;
}, },
@@ -331,6 +341,7 @@ export default {
}, },
openTableContext (payload) { openTableContext (payload) {
this.selectedTable = payload.table; this.selectedTable = payload.table;
this.selectedSchema = payload.schema;
this.tableContextEvent = payload.event; this.tableContextEvent = payload.event;
this.isTableContext = true; this.isTableContext = true;
}, },
@@ -339,11 +350,13 @@ export default {
}, },
openMiscContext (payload) { openMiscContext (payload) {
this.selectedMisc = payload.misc; this.selectedMisc = payload.misc;
this.selectedSchema = payload.schema;
this.miscContextEvent = payload.event; this.miscContextEvent = payload.event;
this.isMiscContext = true; this.isMiscContext = true;
}, },
openMiscFolderContext (payload) { openMiscFolderContext (payload) {
this.selectedMisc = payload.type; this.selectedMisc = payload.type;
this.selectedSchema = payload.schema;
this.miscContextEvent = payload.event; this.miscContextEvent = payload.event;
this.isMiscFolderContext = true; this.isMiscFolderContext = true;
}, },
@@ -364,6 +377,7 @@ export default {
async openCreateViewEditor (payload) { async openCreateViewEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -371,8 +385,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, view: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, view: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'view',
type: 'view-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -388,6 +409,7 @@ export default {
async openCreateTriggerEditor (payload) { async openCreateTriggerEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -396,8 +418,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
const triggerName = this.customizations.triggerTableInName ? `${payload.table}.${payload.name}` : payload.name; const triggerName = this.customizations.triggerTableInName ? `${payload.table}.${payload.name}` : payload.name;
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: triggerName }); this.changeBreadcrumbs({ schema: this.selectedSchema, trigger: triggerName });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: triggerName,
elementType: 'trigger',
type: 'trigger-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -413,6 +442,7 @@ export default {
async openCreateRoutineEditor (payload) { async openCreateRoutineEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -420,8 +450,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, procedure: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, routine: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'routine',
type: 'routine-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -453,6 +490,7 @@ export default {
async openCreateFunctionEditor (payload) { async openCreateFunctionEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -460,8 +498,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, function: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, function: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'function',
type: 'function-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -469,6 +514,7 @@ export default {
async openCreateTriggerFunctionEditor (payload) { async openCreateTriggerFunctionEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -476,8 +522,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, triggerFunction: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, triggerFunction: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'triggerFunction',
type: 'trigger-function-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -485,6 +538,7 @@ export default {
async openCreateSchedulerEditor (payload) { async openCreateSchedulerEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -492,8 +546,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, scheduler: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, scheduler: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'scheduler',
type: 'scheduler-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });

View File

@@ -59,7 +59,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedMisc: Object selectedMisc: Object,
selectedSchema: String
}, },
data () { data () {
return { return {
@@ -97,6 +98,7 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
removeTabs: 'workspaces/removeTabs',
newTab: 'workspaces/newTab' newTab: 'workspaces/newTab'
}), }),
showCreateTableModal () { showCreateTableModal () {
@@ -126,12 +128,14 @@ export default {
case 'trigger': case 'trigger':
res = await Triggers.dropTrigger({ res = await Triggers.dropTrigger({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
trigger: this.selectedMisc.name trigger: this.selectedMisc.name
}); });
break; break;
case 'procedure': case 'procedure':
res = await Routines.dropRoutine({ res = await Routines.dropRoutine({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
routine: this.selectedMisc.name routine: this.selectedMisc.name
}); });
break; break;
@@ -139,12 +143,14 @@ export default {
case 'triggerFunction': case 'triggerFunction':
res = await Functions.dropFunction({ res = await Functions.dropFunction({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
func: this.selectedMisc.name func: this.selectedMisc.name
}); });
break; break;
case 'scheduler': case 'scheduler':
res = await Schedulers.dropScheduler({ res = await Schedulers.dropScheduler({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
scheduler: this.selectedMisc.name scheduler: this.selectedMisc.name
}); });
break; break;
@@ -153,7 +159,12 @@ export default {
const { status, response } = res; const { status, response } = res;
if (status === 'success') { if (status === 'success') {
this.changeBreadcrumbs({ [this.selectedMisc.type]: null }); this.removeTabs({
uid: this.selectedWorkspace,
elementName: this.selectedMisc.name,
elementType: this.selectedMisc.type,
schema: this.selectedSchema
});
this.closeContext(); this.closeContext();
this.$emit('reload'); this.$emit('reload');
@@ -180,8 +191,8 @@ export default {
async runRoutineCheck () { async runRoutineCheck () {
const params = { const params = {
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.workspace.breadcrumbs.schema, schema: this.selectedSchema,
routine: this.workspace.breadcrumbs.procedure routine: this.selectedMisc.name
}; };
try { try {
@@ -218,14 +229,14 @@ export default {
sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`; sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`;
} }
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true }); this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
this.closeContext(); this.closeContext();
}, },
async runFunctionCheck () { async runFunctionCheck () {
const params = { const params = {
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.workspace.breadcrumbs.schema, schema: this.selectedSchema,
func: this.workspace.breadcrumbs.function func: this.selectedMisc.name
}; };
try { try {
@@ -263,7 +274,7 @@ export default {
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`; sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
} }
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true }); this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
this.closeContext(); this.closeContext();
} }
} }

View File

@@ -52,7 +52,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedMisc: String selectedMisc: String,
selectedSchema: String
}, },
data () { data () {
return { return {

View File

@@ -19,7 +19,8 @@
:key="table.name" :key="table.name"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}" :class="{'text-bold': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
@click="setBreadcrumbs({schema: database.name, [table.type]: table.name})" @mousedown.left="selectTable({schema: database.name, table})"
@dblclick="openDataTab({schema: database.name, table})"
@contextmenu.prevent="showTableContext($event, table)" @contextmenu.prevent="showTableContext($event, table)"
> >
<a class="table-name"> <a class="table-name">
@@ -55,7 +56,8 @@
:key="trigger.name" :key="trigger.name"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
@click="setBreadcrumbs({schema: database.name, trigger: trigger.name})" @mousedown="selectMisc({schema: database.name, misc: trigger, type: 'trigger'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: trigger, type: 'trigger'})"
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})" @contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
> >
<a class="table-name"> <a class="table-name">
@@ -73,7 +75,7 @@
<details class="accordion"> <details class="accordion">
<summary <summary
class="accordion-header misc-name" class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine}"
@contextmenu.prevent="showMiscFolderContext($event, 'procedure')" @contextmenu.prevent="showMiscFolderContext($event, 'procedure')"
> >
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" /> <i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
@@ -86,8 +88,9 @@
v-for="(procedure, i) of filteredProcedures" v-for="(procedure, i) of filteredProcedures"
:key="`${procedure.name}-${i}`" :key="`${procedure.name}-${i}`"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name}"
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})" @mousedown="selectMisc({schema: database.name, misc: procedure, type: 'routine'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: procedure, type: 'routine'})"
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})" @contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
> >
<a class="table-name"> <a class="table-name">
@@ -119,7 +122,8 @@
:key="`${func.name}-${i}`" :key="`${func.name}-${i}`"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
@click="setBreadcrumbs({schema: database.name, triggerFunction: func.name})" @mousedown="selectMisc({schema: database.name, misc: func, type: 'triggerFunction'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'triggerFunction'})"
@contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})" @contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})"
> >
<a class="table-name"> <a class="table-name">
@@ -151,7 +155,8 @@
:key="`${func.name}-${i}`" :key="`${func.name}-${i}`"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
@click="setBreadcrumbs({schema: database.name, function: func.name})" @mousedown="selectMisc({schema: database.name, misc: func, type: 'function'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'function'})"
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})" @contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
> >
<a class="table-name"> <a class="table-name">
@@ -183,7 +188,8 @@
:key="scheduler.name" :key="scheduler.name"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
@click="setBreadcrumbs({schema: database.name, scheduler: scheduler.name})" @mousedown="selectMisc({schema: database.name, misc: scheduler, type: 'scheduler'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: scheduler, type: 'scheduler'})"
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})" @contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
> >
<a class="table-name"> <a class="table-name">
@@ -267,6 +273,8 @@ export default {
methods: { methods: {
...mapActions({ ...mapActions({
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
addLoadedSchema: 'workspaces/addLoadedSchema',
newTab: 'workspaces/newTab',
refreshSchema: 'workspaces/refreshSchema' refreshSchema: 'workspaces/refreshSchema'
}), }),
formatBytes, formatBytes,
@@ -274,27 +282,68 @@ export default {
if (!this.loadedSchemas.has(schema) && !this.isLoading) { if (!this.loadedSchemas.has(schema) && !this.isLoading) {
this.isLoading = true; this.isLoading = true;
await this.refreshSchema({ uid: this.connection.uid, schema }); await this.refreshSchema({ uid: this.connection.uid, schema });
this.addLoadedSchema(schema);
this.isLoading = false; this.isLoading = false;
} }
this.changeBreadcrumbs({ schema, table: null }); this.changeBreadcrumbs({ schema, table: null });
}, },
selectTable ({ schema, table }) {
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'temp-data', elementType: table.type });
this.setBreadcrumbs({ schema, [table.type]: table.name });
},
selectMisc ({ schema, misc, type }) {
const miscTempTabs = {
trigger: 'temp-trigger-props',
triggerFunction: 'temp-trigger-function-props',
function: 'temp-function-props',
routine: 'temp-routine-props',
scheduler: 'temp-scheduler-props'
};
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTempTabs[type],
elementType: type
});
this.setBreadcrumbs({ schema, [type]: misc.name });
},
openDataTab ({ schema, table }) {
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'data', elementType: table.type });
this.setBreadcrumbs({ schema, [table.type]: table.name });
},
openMiscPermanentTab ({ schema, misc, type }) {
const miscTabs = {
trigger: 'trigger-props',
triggerFunction: 'trigger-function-props',
function: 'function-props',
routine: 'routine-props',
scheduler: 'scheduler-props'
};
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTabs[type],
elementType: type
});
this.setBreadcrumbs({ schema, [type]: misc.name });
},
showSchemaContext (event, schema) { showSchemaContext (event, schema) {
this.selectSchema(schema);
this.$emit('show-schema-context', { event, schema }); this.$emit('show-schema-context', { event, schema });
}, },
showTableContext (event, table) { showTableContext (event, table) {
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name }); this.$emit('show-table-context', { event, schema: this.database.name, table });
this.$emit('show-table-context', { event, table });
}, },
showMiscContext (event, misc) { showMiscContext (event, misc) {
this.setBreadcrumbs({ schema: this.database.name, [misc.type]: misc.name }); this.$emit('show-misc-context', { event, schema: this.database.name, misc });
this.$emit('show-misc-context', { event, misc });
}, },
showMiscFolderContext (event, type) { showMiscFolderContext (event, type) {
this.selectSchema(this.database.name); this.$emit('show-misc-folder-context', { event, schema: this.database.name, type });
this.setBreadcrumbs({ schema: this.database.name, type });
this.$emit('show-misc-folder-context', { event, type });
}, },
piePercentage (val) { piePercentage (val) {
const perc = val / this.maxSize * 100; const perc = val / this.maxSize * 100;
@@ -308,6 +357,8 @@ export default {
this.changeBreadcrumbs(payload); this.changeBreadcrumbs(payload);
}, },
highlightWord (string) { highlightWord (string) {
string = string.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
if (this.searchTerm) { if (this.searchTerm) {
const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return string.replace(regexp, '<span class="text-primary">$1</span>'); return string.replace(regexp, '<span class="text-primary">$1</span>');

View File

@@ -82,13 +82,13 @@
</template> </template>
<div slot="body"> <div slot="body">
<div class="mb-2"> <div class="mb-2">
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedDatabase }}</b>"? {{ $t('message.deleteCorfirm') }} "<b>{{ selectedSchema }}</b>"?
</div> </div>
</div> </div>
</ConfirmModal> </ConfirmModal>
<ModalEditSchema <ModalEditSchema
v-if="isEditModal" v-if="isEditModal"
:selected-database="selectedDatabase" :selected-schema="selectedSchema"
@close="hideEditModal" @close="hideEditModal"
/> />
</BaseContextMenu> </BaseContextMenu>
@@ -110,7 +110,7 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedDatabase: String selectedSchema: String
}, },
data () { data () {
return { return {
@@ -173,11 +173,11 @@ export default {
try { try {
const { status, response } = await Schema.deleteSchema({ const { status, response } = await Schema.deleteSchema({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
database: this.selectedDatabase database: this.selectedSchema
}); });
if (status === 'success') { if (status === 'success') {
if (this.selectedDatabase === this.workspace.breadcrumbs.schema) if (this.selectedSchema === this.workspace.breadcrumbs.schema)
this.changeBreadcrumbs({ schema: null }); this.changeBreadcrumbs({ schema: null });
this.closeContext(); this.closeContext();

View File

@@ -3,6 +3,20 @@
:context-event="contextEvent" :context-event="contextEvent"
@close-context="closeContext" @close-context="closeContext"
> >
<div
v-if="selectedTable.type === 'table' && workspace.customizations.tableSettings"
class="context-element"
@click="openTableSettingTab"
>
<span class="d-flex"><i class="mdi mdi-18px mdi-tune-vertical-variant text-light pr-1" /> {{ $t('word.settings') }}</span>
</div>
<div
v-if="selectedTable.type === 'view' && workspace.customizations.viewSettings"
class="context-element"
@click="openViewSettingTab"
>
<span class="d-flex"><i class="mdi mdi-18px mdi-tune-vertical-variant text-light pr-1" /> {{ $t('word.settings') }}</span>
</div>
<div <div
v-if="selectedTable.type === 'table'" v-if="selectedTable.type === 'table'"
class="context-element" class="context-element"
@@ -72,7 +86,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedTable: Object selectedTable: Object,
selectedSchema: String
}, },
data () { data () {
return { return {
@@ -92,6 +107,8 @@ export default {
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
newTab: 'workspaces/newTab',
removeTabs: 'workspaces/removeTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}), }),
showCreateTableModal () { showCreateTableModal () {
@@ -112,11 +129,44 @@ export default {
closeContext () { closeContext () {
this.$emit('close-context'); this.$emit('close-context');
}, },
openTableSettingTab () {
this.newTab({
uid: this.selectedWorkspace,
elementName: this.selectedTable.name,
schema: this.selectedSchema,
type: 'table-props',
elementType: 'table'
});
this.changeBreadcrumbs({
schema: this.selectedSchema,
table: this.selectedTable.name
});
this.closeContext();
},
openViewSettingTab () {
this.newTab({
uid: this.selectedWorkspace,
elementType: 'table',
elementName: this.selectedTable.name,
schema: this.selectedSchema,
type: 'view-props'
});
this.changeBreadcrumbs({
schema: this.selectedSchema,
view: this.selectedTable.name
});
this.closeContext();
},
async duplicateTable () { async duplicateTable () {
try { try {
const { status, response } = await Tables.duplicateTable({ const { status, response } = await Tables.duplicateTable({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
table: this.selectedTable.name table: this.selectedTable.name,
schema: this.selectedSchema
}); });
if (status === 'success') { if (status === 'success') {
@@ -134,13 +184,11 @@ export default {
try { try {
const { status, response } = await Tables.truncateTable({ const { status, response } = await Tables.truncateTable({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
table: this.selectedTable.name table: this.selectedTable.name,
schema: this.selectedSchema
}); });
if (status === 'success') { if (status === 'success') {
if (this.selectedTable.name === this.workspace.breadcrumbs.table)
this.changeBreadcrumbs({ table: null });
this.closeContext(); this.closeContext();
this.$emit('reload'); this.$emit('reload');
} }
@@ -158,21 +206,27 @@ export default {
if (this.selectedTable.type === 'table') { if (this.selectedTable.type === 'table') {
res = await Tables.dropTable({ res = await Tables.dropTable({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
table: this.selectedTable.name table: this.selectedTable.name,
schema: this.selectedSchema
}); });
} }
else if (this.selectedTable.type === 'view') { else if (this.selectedTable.type === 'view') {
res = await Views.dropView({ res = await Views.dropView({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
view: this.selectedTable.name view: this.selectedTable.name,
schema: this.selectedSchema
}); });
} }
const { status, response } = res; const { status, response } = res;
if (status === 'success') { if (status === 'success') {
if (this.selectedTable.name === this.workspace.breadcrumbs.table || this.selectedTable.name === this.workspace.breadcrumbs.view) this.removeTabs({
this.changeBreadcrumbs({ table: null, view: null }); uid: this.selectedWorkspace,
elementName: this.selectedTable.name,
elementType: this.selectedTable.type,
schema: this.selectedSchema
});
this.closeContext(); this.closeContext();
this.$emit('reload'); this.$emit('reload');

View File

@@ -267,6 +267,12 @@ export default {
addNotification: 'notifications/addNotification' addNotification: 'notifications/addNotification'
}), }),
confirmForeignsChange () { confirmForeignsChange () {
this.foreignProxy = this.foreignProxy.filter(foreign =>
foreign.field &&
foreign.refField &&
foreign.table &&
foreign.refTable
);
this.$emit('foreigns-update', this.foreignProxy); this.$emit('foreigns-update', this.foreignProxy);
}, },
selectForeign (event, id) { selectForeign (event, id) {
@@ -331,6 +337,8 @@ export default {
this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._id : ''; this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._id : '';
}, },
async getRefFields () { async getRefFields () {
if (!this.selectedForeignObj.refTable) return;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedForeignObj.refSchema, schema: this.selectedForeignObj.refSchema,

View File

@@ -186,6 +186,7 @@ export default {
}, },
methods: { methods: {
confirmIndexesChange () { confirmIndexesChange () {
this.indexesProxy = this.indexesProxy.filter(index => index.fields.length);
this.$emit('indexes-update', this.indexesProxy); this.$emit('indexes-update', this.indexesProxy);
}, },
selectIndex (event, id) { selectIndex (event, id) {

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -14,7 +14,7 @@
<span>{{ $t('word.save') }}</span> <span>{{ $t('word.save') }}</span>
</button> </button>
<button <button
:disabled="!isChanged" :disabled="!isChanged || isSaving"
class="btn btn-link btn-sm mr-0" class="btn btn-link btn-sm mr-0"
:title="$t('message.clearChanges')" :title="$t('message.clearChanges')"
@click="clearChanges" @click="clearChanges"
@@ -26,6 +26,7 @@
<div class="divider-vert py-3" /> <div class="divider-vert py-3" />
<button <button
:disabled="isSaving"
class="btn btn-dark btn-sm" class="btn btn-dark btn-sm"
:title="$t('message.addNewField')" :title="$t('message.addNewField')"
@click="addField" @click="addField"
@@ -34,6 +35,7 @@
<span>{{ $t('word.add') }}</span> <span>{{ $t('word.add') }}</span>
</button> </button>
<button <button
:disabled="isSaving"
class="btn btn-dark btn-sm" class="btn btn-dark btn-sm"
:title="$t('message.manageIndexes')" :title="$t('message.manageIndexes')"
@click="showIntdexesModal" @click="showIntdexesModal"
@@ -41,15 +43,28 @@
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" /> <i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
<span>{{ $t('word.indexes') }}</span> <span>{{ $t('word.indexes') }}</span>
</button> </button>
<button class="btn btn-dark btn-sm" @click="showForeignModal"> <button
class="btn btn-dark btn-sm"
:disabled="isSaving"
@click="showForeignModal"
>
<i class="mdi mdi-24px mdi-key-link mr-1" /> <i class="mdi mdi-24px mdi-key-link mr-1" />
<span>{{ $t('word.foreignKeys') }}</span> <span>{{ $t('word.foreignKeys') }}</span>
</button> </button>
<button class="btn btn-dark btn-sm" @click="showOptionsModal"> <button
class="btn btn-dark btn-sm"
:disabled="isSaving"
@click="showOptionsModal"
>
<i class="mdi mdi-24px mdi-cogs mr-1" /> <i class="mdi mdi-24px mdi-cogs mr-1" />
<span>{{ $t('word.options') }}</span> <span>{{ $t('word.options') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="workspace-query-results column col-12 p-relative"> <div class="workspace-query-results column col-12 p-relative">
@@ -126,11 +141,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
table: String isSelected: Boolean,
table: String,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -157,6 +173,9 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
tableOptions () { tableOptions () {
const db = this.workspace.structure.find(db => db.name === this.schema); const db = this.workspace.structure.find(db => db.name === this.schema);
return db && this.table ? db.tables.find(table => table.name === this.table) : {}; return db && this.table ? db.tables.find(table => table.name === this.table) : {};
@@ -164,12 +183,6 @@ export default {
defaultEngine () { defaultEngine () {
return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || ''; return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || '';
}, },
isSelected () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.table;
},
schema () {
return this.workspace.breadcrumbs.schema;
},
schemaTables () { schemaTables () {
const schemaTables = this.workspace.structure const schemaTables = this.workspace.structure
.filter(schema => schema.name === this.schema) .filter(schema => schema.name === this.schema)
@@ -185,6 +198,12 @@ export default {
} }
}, },
watch: { watch: {
schema () {
if (this.isSelected) {
this.getFieldsData();
this.lastTable = this.table;
}
},
table () { table () {
if (this.isSelected) { if (this.isSelected) {
this.getFieldsData(); this.getFieldsData();
@@ -192,17 +211,19 @@ export default {
} }
}, },
isSelected (val) { isSelected (val) {
if (val && this.lastTable !== this.table) { if (val) {
this.getFieldsData(); this.changeBreadcrumbs({ schema: this.schema, table: this.table });
this.lastTable = this.table;
if (this.lastTable !== this.table)
this.getFieldsData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastTable === this.table && this.table !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
created () { created () {
this.getFieldsData();
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
beforeDestroy () { beforeDestroy () {
@@ -213,20 +234,25 @@ export default {
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', setUnsavedChanges: 'workspaces/setUnsavedChanges',
renameTabs: 'workspaces/renameTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}), }),
async getFieldsData () { async getFieldsData () {
if (!this.table) return; if (!this.table) return;
this.localFields = []; this.localFields = [];
this.lastTable = this.table;
this.newFieldsCounter = 0; this.newFieldsCounter = 0;
this.isLoading = true; this.isLoading = true;
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); try {
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
}
catch (err) {}
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
table: this.workspace.breadcrumbs.table table: this.table
}; };
try { // Columns data try { // Columns data
@@ -410,7 +436,7 @@ export default {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
table: this.workspace.breadcrumbs.table, table: this.table,
additions, additions,
changes, changes,
deletions, deletions,
@@ -428,11 +454,18 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localOptions.name) { if (oldName !== this.localOptions.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localOptions.name,
elementType: 'table'
});
this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name }); this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name });
} }
else
this.getFieldsData(); this.getFieldsData();
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -42,6 +42,11 @@
<span>{{ $t('word.options') }}</span> <span>{{ $t('word.options') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="workspace-query-results column col-12 mt-2 p-relative"> <div class="workspace-query-results column col-12 mt-2 p-relative">
@@ -102,11 +107,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
function: String function: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -127,11 +133,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
@@ -150,6 +153,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function;
}
},
async function () { async function () {
if (this.isSelected) { if (this.isSelected) {
await this.getFunctionData(); await this.getFunctionData();
@@ -158,26 +168,32 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastFunction !== this.function) { if (val) {
await this.getFunctionData(); this.changeBreadcrumbs({ schema: this.schema, function: this.function });
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function; setTimeout(() => {
this.resizeQueryEditor();
}, 200);
if (this.lastFunction !== this.function)
this.getRoutineData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastFunction === this.function && this.function !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -185,20 +201,22 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
newTab: 'workspaces/newTab' setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getFunctionData () { async getFunctionData () {
if (!this.function) return; if (!this.function) return;
this.isLoading = true; this.isLoading = true;
this.localFunction = { sql: '' }; this.localFunction = { sql: '' };
this.lastFunction = this.function;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
func: this.workspace.breadcrumbs.function func: this.function
}; };
try { try {
@@ -229,9 +247,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
func: { func: {
...this.localFunction, ...this.localFunction,
schema: this.schema,
oldName: this.originalFunction.name oldName: this.originalFunction.name
} }
}; };
@@ -245,11 +263,18 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localFunction.name) { if (oldName !== this.localFunction.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localFunction.name,
elementType: 'function'
});
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name }); this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name });
} }
else
this.getFunctionData(); this.getFunctionData();
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -303,7 +328,7 @@ export default {
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`; sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
} }
this.newTab({ uid: this.connection.uid, content: sql, autorun: true }); this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
}, },
showOptionsModal () { showOptionsModal () {
this.isOptionsModal = true; this.isOptionsModal = true;

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -42,6 +42,11 @@
<span>{{ $t('word.options') }}</span> <span>{{ $t('word.options') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="workspace-query-results column col-12 mt-2 p-relative"> <div class="workspace-query-results column col-12 mt-2 p-relative">
@@ -103,11 +108,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
routine: String routine: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -128,11 +134,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.routine; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine); return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
@@ -149,6 +152,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getRoutineData();
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
this.lastRoutine = this.routine;
}
},
async routine () { async routine () {
if (this.isSelected) { if (this.isSelected) {
await this.getRoutineData(); await this.getRoutineData();
@@ -157,26 +167,32 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastRoutine !== this.routine) { if (val) {
await this.getRoutineData(); this.changeBreadcrumbs({ schema: this.schema, routine: this.routine });
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
this.lastRoutine = this.routine; setTimeout(() => {
this.resizeQueryEditor();
}, 200);
if (this.lastRoutine !== this.routine)
this.getRoutineData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastRoutine === this.routine && this.routine !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getRoutineData();
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -184,19 +200,22 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
newTab: 'workspaces/newTab' setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getRoutineData () { async getRoutineData () {
if (!this.routine) return; if (!this.routine) return;
this.localRoutine = { sql: '' }; this.localRoutine = { sql: '' };
this.isLoading = true; this.isLoading = true;
this.lastRoutine = this.routine;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
routine: this.workspace.breadcrumbs.procedure routine: this.routine
}; };
try { try {
@@ -227,9 +246,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
routine: { routine: {
...this.localRoutine, ...this.localRoutine,
schema: this.schema,
oldName: this.originalRoutine.name oldName: this.originalRoutine.name
} }
}; };
@@ -243,11 +262,18 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localRoutine.name) { if (oldName !== this.localRoutine.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localRoutine.name,
elementType: 'procedure'
});
this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name }); this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name });
} }
else
this.getRoutineData(); this.getRoutineData();
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -299,7 +325,7 @@ export default {
sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`; sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`;
} }
this.newTab({ uid: this.connection.uid, content: sql, autorun: true }); this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
}, },
showOptionsModal () { showOptionsModal () {
this.isOptionsModal = true; this.isOptionsModal = true;

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -29,6 +29,11 @@
<span>{{ $t('word.timing') }}</span> <span>{{ $t('word.timing') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
@@ -153,11 +158,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
scheduler: String scheduler: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isTimingModal: false, isTimingModal: false,
@@ -176,11 +182,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.scheduler; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler); return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
@@ -197,6 +200,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getSchedulerData();
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
this.lastScheduler = this.scheduler;
}
},
async scheduler () { async scheduler () {
if (this.isSelected) { if (this.isSelected) {
await this.getSchedulerData(); await this.getSchedulerData();
@@ -205,26 +215,32 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastScheduler !== this.scheduler) { if (val) {
await this.getSchedulerData(); this.changeBreadcrumbs({ schema: this.schema, scheduler: this.scheduler });
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
this.lastScheduler = this.scheduler; setTimeout(() => {
this.resizeQueryEditor();
}, 200);
if (this.lastScheduler !== this.scheduler)
this.getSchedulerData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastScheduler === this.scheduler && this.scheduler !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getSchedulerData();
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -232,17 +248,21 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getSchedulerData () { async getSchedulerData () {
if (!this.scheduler) return; if (!this.scheduler) return;
this.isLoading = true; this.isLoading = true;
this.lastScheduler = this.scheduler;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
scheduler: this.workspace.breadcrumbs.scheduler scheduler: this.scheduler
}; };
try { try {
@@ -267,9 +287,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
scheduler: { scheduler: {
...this.localScheduler, ...this.localScheduler,
schema: this.schema,
oldName: this.originalScheduler.name oldName: this.originalScheduler.name
} }
}; };
@@ -283,11 +303,18 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localScheduler.name) { if (oldName !== this.localScheduler.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localScheduler.name,
elementType: 'scheduler'
});
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name }); this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name });
} }
else
this.getSchedulerData(); this.getSchedulerData();
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -23,6 +23,11 @@
<span>{{ $t('word.clear') }}</span> <span>{{ $t('word.clear') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
@@ -139,11 +144,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
trigger: String trigger: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
originalTrigger: null, originalTrigger: null,
@@ -162,15 +168,12 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
isSelected () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger;
},
schema () {
return this.workspace.breadcrumbs.schema;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger); return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger);
}, },
@@ -186,6 +189,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getTriggerData();
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
this.lastTrigger = this.trigger;
}
},
async trigger () { async trigger () {
if (this.isSelected) { if (this.isSelected) {
await this.getTriggerData(); await this.getTriggerData();
@@ -194,26 +204,32 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastTrigger !== this.trigger) { if (val) {
await this.getTriggerData(); this.changeBreadcrumbs({ schema: this.schema, trigger: this.trigger });
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
this.lastTrigger = this.trigger; setTimeout(() => {
this.resizeQueryEditor();
}, 200);
if (this.lastTrigger !== this.trigger)
this.getTriggerData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastTrigger === this.trigger && this.trigger !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getTriggerData();
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -221,8 +237,10 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getTriggerData () { async getTriggerData () {
if (!this.trigger) return; if (!this.trigger) return;
@@ -233,11 +251,12 @@ export default {
this.localTrigger = { sql: '' }; this.localTrigger = { sql: '' };
this.isLoading = true; this.isLoading = true;
this.lastTrigger = this.trigger;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
trigger: this.workspace.breadcrumbs.trigger trigger: this.trigger
}; };
try { try {
@@ -278,9 +297,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
trigger: { trigger: {
...this.localTrigger, ...this.localTrigger,
schema: this.schema,
oldName: this.originalTrigger.name oldName: this.originalTrigger.name
} }
}; };
@@ -289,17 +308,24 @@ export default {
const { status, response } = await Triggers.alterTrigger(params); const { status, response } = await Triggers.alterTrigger(params);
if (status === 'success') { if (status === 'success') {
const oldName = this.originalTrigger.name;
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localTrigger.name) { if (this.originalTrigger.name !== this.localTrigger.name) {
this.setUnsavedChanges(false);
const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name; const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name;
const triggerOldName = this.customizations.triggerTableInName ? `${this.originalTrigger.table}.${this.originalTrigger.name}` : this.originalTrigger.name;
this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: triggerOldName,
elementNewName: triggerName,
elementType: 'trigger'
});
this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName }); this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName });
} }
else
this.getTriggerData(); this.getTriggerData();
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -80,11 +80,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
function: String function: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -105,11 +106,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
@@ -128,6 +126,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function;
}
},
async function () { async function () {
if (this.isSelected) { if (this.isSelected) {
await this.getFunctionData(); await this.getFunctionData();
@@ -136,26 +141,32 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastFunction !== this.function) { if (val) {
await this.getFunctionData(); this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.function });
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function; setTimeout(() => {
this.resizeQueryEditor();
}, 200);
if (this.lastFunction !== this.function)
await this.getFunctionData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastFunction === this.function && this.function !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -163,20 +174,22 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
newTab: 'workspaces/newTab' setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getFunctionData () { async getFunctionData () {
if (!this.function) return; if (!this.function) return;
this.isLoading = true; this.isLoading = true;
this.localFunction = { sql: '' }; this.localFunction = { sql: '' };
this.lastFunction = this.function;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
func: this.workspace.breadcrumbs.triggerFunction func: this.function
}; };
try { try {
@@ -207,9 +220,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
func: { func: {
...this.localFunction, ...this.localFunction,
schema: this.schema,
oldName: this.originalFunction.name oldName: this.originalFunction.name
} }
}; };
@@ -223,11 +236,18 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localFunction.name) { if (oldName !== this.localFunction.name) {
this.setUnsavedChanges(false); this.renameTabs({
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name }); uid: this.connection.uid,
} schema: this.schema,
elementName: oldName,
elementNewName: this.localFunction.name,
elementType: 'triggerFunction'
});
this.getFunctionData(); this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.localFunction.name });
}
else
this.getFunctionData();
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -281,7 +301,7 @@ export default {
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`; sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
} }
this.newTab({ uid: this.connection.uid, content: sql, autorun: true }); this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
}, },
showOptionsModal () { showOptionsModal () {
this.isOptionsModal = true; this.isOptionsModal = true;

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -23,6 +23,11 @@
<span>{{ $t('word.clear') }}</span> <span>{{ $t('word.clear') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
@@ -161,7 +166,7 @@
<BaseLoader v-if="isLoading" /> <BaseLoader v-if="isLoading" />
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label> <label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
<QueryEditor <QueryEditor
v-if="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localView.sql" :value.sync="localView.sql"
:workspace="workspace" :workspace="workspace"
@@ -186,11 +191,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
isSelected: Boolean,
schema: String,
view: String view: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
originalView: null, originalView: null,
@@ -208,11 +214,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.view; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView); return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
@@ -222,6 +225,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getViewData();
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
this.lastView = this.view;
}
},
async view () { async view () {
if (this.isSelected) { if (this.isSelected) {
await this.getViewData(); await this.getViewData();
@@ -229,27 +239,33 @@ export default {
this.lastView = this.view; this.lastView = this.view;
} }
}, },
async isSelected (val) { isSelected (val) {
if (val && this.lastView !== this.view) { if (val) {
await this.getViewData(); this.changeBreadcrumbs({ schema: this.schema, view: this.view });
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
this.lastView = this.view; setTimeout(() => {
this.resizeQueryEditor();
}, 200);
if (this.lastView !== this.view)
this.getViewData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastView === this.view && this.view !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getViewData();
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -258,17 +274,19 @@ export default {
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', setUnsavedChanges: 'workspaces/setUnsavedChanges',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
renameTabs: 'workspaces/renameTabs'
}), }),
async getViewData () { async getViewData () {
if (!this.view) return; if (!this.view) return;
this.isLoading = true; this.isLoading = true;
this.localView = { sql: '' }; this.localView = { sql: '' };
this.lastView = this.view;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
view: this.workspace.breadcrumbs.view view: this.view
}; };
try { try {
@@ -293,9 +311,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
view: { view: {
...this.localView, ...this.localView,
schema: this.schema,
oldName: this.originalView.name oldName: this.originalView.name
} }
}; };
@@ -309,11 +327,18 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localView.name) { if (oldName !== this.localView.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localView.name,
elementType: 'view'
});
this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name }); this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name });
} }
else
this.getViewData(); this.getViewData();
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });

View File

@@ -100,7 +100,7 @@
</div> </div>
</div> </div>
</div> </div>
<draggable <Draggable
ref="resultTable" ref="resultTable"
:list="fields" :list="fields"
class="tbody" class="tbody"
@@ -117,14 +117,14 @@
@contextmenu="contextMenu" @contextmenu="contextMenu"
@rename-field="$emit('rename-field', $event)" @rename-field="$emit('rename-field', $event)"
/> />
</draggable> </Draggable>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import TableRow from '@/components/WorkspacePropsTableRow'; import TableRow from '@/components/WorkspacePropsTableRow';
import TableContext from '@/components/WorkspacePropsTableContext'; import TableContext from '@/components/WorkspacePropsTableContext';
@@ -133,7 +133,7 @@ export default {
components: { components: {
TableRow, TableRow,
TableContext, TableContext,
draggable Draggable
}, },
props: { props: {
fields: Array, fields: Array,

View File

@@ -450,7 +450,7 @@ export default {
}); });
this.defaultValue.onUpdate = this.localRow.onUpdate; this.defaultValue.onUpdate = this.localRow.onUpdate;
this.defaultValue.type = this.localRow.defaultType; this.defaultValue.type = this.localRow.defaultType || 'noval';
if (this.defaultValue.type === 'custom') { if (this.defaultValue.type === 'custom') {
this.defaultValue.custom = this.localRow.default this.defaultValue.custom = this.localRow.default
? this.localRow.default.includes('\'') ? this.localRow.default.includes('\'')
@@ -458,8 +458,13 @@ export default {
: this.localRow.default : this.localRow.default
: ''; : '';
} }
if (this.defaultValue.type === 'expression') else if (this.defaultValue.type === 'expression') {
this.defaultValue.expression = this.localRow.default; console.log(this.localRow.default);
if (this.localRow.default.toUpperCase().includes('ON UPDATE'))
this.defaultValue.expression = this.localRow.default.replace(/ on update.*$/i, '');
else
this.defaultValue.expression = this.localRow.default;
}
}, },
editON (event, content, field) { editON (event, content, field) {
if (field === 'length') { if (field === 'length') {

View File

@@ -1,7 +1,7 @@
<template> <template>
<div <div
v-show="isSelected" v-show="isSelected"
class="workspace-query-tab column col-12 columns col-gapless no-outline" class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
tabindex="0" tabindex="0"
@keydown.116="runQuery(query)" @keydown.116="runQuery(query)"
@keydown.ctrl.87="clear" @keydown.ctrl.87="clear"
@@ -14,7 +14,7 @@
:auto-focus="true" :auto-focus="true"
:value.sync="query" :value.sync="query"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="breadcrumbsSchema"
:is-selected="isSelected" :is-selected="isSelected"
:height="editorHeight" :height="editorHeight"
/> />
@@ -86,12 +86,16 @@
<div v-if="affectedCount"> <div v-if="affectedCount">
{{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b> {{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b>
</div> </div>
<div <div class="input-group" :title="$t('word.schema')">
v-if="workspace.breadcrumbs.schema" <i class="input-group-addon addon-sm mdi mdi-24px mdi-database" />
class="d-flex" <select v-model="selectedSchema" class="form-select select-sm text-bold">
:title="$t('word.schema')" <option :value="null">
> {{ $t('message.noSchema') }}
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ workspace.breadcrumbs.schema }}</b> </option>
<option v-for="schemaName in databaseSchemas" :key="schemaName">
{{ schemaName }}
</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@@ -142,6 +146,7 @@ export default {
lastQuery: '', lastQuery: '',
isQuering: false, isQuering: false,
results: [], results: [],
selectedSchema: null,
resultsCount: 0, resultsCount: 0,
durationsCount: 0, durationsCount: 0,
affectedCount: 0, affectedCount: 0,
@@ -156,12 +161,32 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
breadcrumbsSchema () {
return this.workspace.breadcrumbs.schema || null;
},
databaseSchemas () {
return this.workspace.structure.reduce((acc, curr) => {
acc.push(curr.name);
return acc;
}, []);
},
isWorkspaceSelected () { isWorkspaceSelected () {
return this.workspace.uid === this.selectedWorkspace; return this.workspace.uid === this.selectedWorkspace;
} }
}, },
watch: {
isSelected (val) {
if (val)
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
},
selectedSchema () {
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
}
},
created () { created () {
this.query = this.tab.content; this.query = this.tab.content;
this.selectedSchema = this.tab.schema || this.breadcrumbsSchema;
// this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
@@ -183,7 +208,9 @@ export default {
}, },
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification' addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
updateTabContent: 'workspaces/updateTabContent'
}), }),
async runQuery (query) { async runQuery (query) {
if (!query || this.isQuering) return; if (!query || this.isQuering) return;
@@ -194,7 +221,7 @@ export default {
try { // Query Data try { // Query Data
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.selectedSchema,
query query
}; };
@@ -205,6 +232,8 @@ export default {
this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0); this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
this.durationsCount += this.results.reduce((acc, curr) => acc + curr.duration, 0); this.durationsCount += this.results.reduce((acc, curr) => acc + curr.duration, 0);
this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0); this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0);
this.updateTabContent({ uid: this.connection.uid, tab: this.tab.uid, type: 'query', schema: this.selectedSchema, content: query });
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -303,8 +332,10 @@ export default {
align-items: center; align-items: center;
height: 42px; height: 42px;
.workspace-query-buttons { .workspace-query-buttons,
.workspace-query-info {
display: flex; display: flex;
align-items: center;
.btn { .btn {
display: flex; display: flex;
@@ -314,7 +345,7 @@ export default {
} }
.workspace-query-info { .workspace-query-info {
display: flex; overflow: visible;
> div + div { > div + div {
padding-left: 0.6rem; padding-left: 0.6rem;

View File

@@ -10,6 +10,7 @@
v-if="isContext" v-if="isContext"
:context-event="contextEvent" :context-event="contextEvent"
:selected-rows="selectedRows" :selected-rows="selectedRows"
:selected-cell="selectedCell"
@show-delete-modal="showDeleteConfirmModal" @show-delete-modal="showDeleteConfirmModal"
@set-null="setNull" @set-null="setNull"
@copy-cell="copyCell" @copy-cell="copyCell"
@@ -71,6 +72,7 @@
:row="row" :row="row"
:fields="fieldsObj" :fields="fieldsObj"
:key-usage="keyUsage" :key-usage="keyUsage"
:element-type="elementType"
:class="{'selected': selectedRows.includes(row._id)}" :class="{'selected': selectedRows.includes(row._id)}"
@select-row="selectRow($event, row._id)" @select-row="selectRow($event, row._id)"
@update-field="updateField($event, row)" @update-field="updateField($event, row)"
@@ -123,7 +125,8 @@ export default {
results: Array, results: Array,
connUid: String, connUid: String,
mode: String, mode: String,
isSelected: Boolean isSelected: Boolean,
elementType: { type: String, default: 'table' }
}, },
data () { data () {
return { return {
@@ -226,6 +229,9 @@ export default {
}, },
resultsetIndex () { resultsetIndex () {
this.setLocalResults(); this.setLocalResults();
},
isSelected (val) {
if (val) this.refreshScroller();
} }
}, },
updated () { updated () {

View File

@@ -28,7 +28,7 @@
</div> </div>
</div> </div>
<div <div
v-if="selectedRows.length === 1" v-if="selectedRows.length === 1 && selectedCell.isEditable"
class="context-element" class="context-element"
@click="setNull" @click="setNull"
> >
@@ -36,7 +36,11 @@
<i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }} <i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }}
</span> </span>
</div> </div>
<div class="context-element" @click="showConfirmModal"> <div
v-if="selectedCell.isEditable"
class="context-element"
@click="showConfirmModal"
>
<span class="d-flex"> <span class="d-flex">
<i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }} <i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
</span> </span>
@@ -54,7 +58,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedRows: Array selectedRows: Array,
selectedCell: Object
}, },
computed: { computed: {
}, },

View File

@@ -260,7 +260,8 @@ export default {
props: { props: {
row: Object, row: Object,
fields: Object, fields: Object,
keyUsage: Array keyUsage: Array,
elementType: { type: String, default: 'table' }
}, },
data () { data () {
return { return {
@@ -334,6 +335,8 @@ export default {
return this.keyUsage.map(key => key.field); return this.keyUsage.map(key => key.field);
}, },
isEditable () { isEditable () {
if (this.elementType === 'view') return false;
if (this.fields) { if (this.fields) {
const nElements = Object.keys(this.fields).reduce((acc, curr) => { const nElements = Object.keys(this.fields).reduce((acc, curr) => {
acc.add(this.fields[curr].table); acc.add(this.fields[curr].table);
@@ -503,10 +506,9 @@ export default {
return this.keyUsage.find(key => key.field === keyName); return this.keyUsage.find(key => key.field === keyName);
}, },
openContext (event, payload) { openContext (event, payload) {
if (this.isEditable) { payload.field = this.fields[payload.field].name;// Ensures field name only
payload.field = this.fields[payload.field].name;// Ensures field name only payload.isEditable = this.isEditable;
this.$emit('contextmenu', event, payload); this.$emit('contextmenu', event, payload);
}
}, },
onKey (e) { onKey (e) {
e.stopPropagation(); e.stopPropagation();

View File

@@ -1,5 +1,5 @@
<template> <template>
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless no-outline p-0">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -115,8 +115,8 @@
<div v-if="hasApproximately || (page > 1 && tableInfo.rows)"> <div v-if="hasApproximately || (page > 1 && tableInfo.rows)">
{{ $t('word.total') }}: <b>{{ tableInfo.rows | localeString }}</b> <small>({{ $t('word.approximately') }})</small> {{ $t('word.total') }}: <b>{{ tableInfo.rows | localeString }}</b> <small>({{ $t('word.approximately') }})</small>
</div> </div>
<div v-if="workspace.breadcrumbs.database"> <div class="d-flex" :title="$t('word.schema')">
{{ $t('word.schema') }}: <b>{{ workspace.breadcrumbs.database }}</b> <i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div> </div>
</div> </div>
</div> </div>
@@ -131,6 +131,7 @@
:conn-uid="connection.uid" :conn-uid="connection.uid"
:is-selected="isSelected" :is-selected="isSelected"
mode="table" mode="table"
:element-type="elementType"
@update-field="updateField" @update-field="updateField"
@delete-selected="deleteSelected" @delete-selected="deleteSelected"
@hard-sort="hardSort" @hard-sort="hardSort"
@@ -181,11 +182,14 @@ export default {
mixins: [tableTabs], mixins: [tableTabs],
props: { props: {
connection: Object, connection: Object,
table: String isSelected: Boolean,
table: String,
schema: String,
elementType: String
}, },
data () { data () {
return { return {
tabUid: 'data', tabUid: 'data', // ???
isQuering: false, isQuering: false,
isPageMenu: false, isPageMenu: false,
results: [], results: [],
@@ -208,9 +212,6 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () {
return this.workspace.selected_tab === 'data' && this.workspace.uid === this.selectedWorkspace;
},
isTable () { isTable () {
return !!this.workspace.breadcrumbs.table; return !!this.workspace.breadcrumbs.table;
}, },
@@ -237,6 +238,15 @@ export default {
} }
}, },
watch: { watch: {
schema () {
if (this.isSelected) {
this.page = 1;
this.sortParams = {};
this.getTableData();
this.lastTable = this.table;
this.$refs.queryTable.resetSort();
}
},
table () { table () {
if (this.isSelected) { if (this.isSelected) {
this.page = 1; this.page = 1;
@@ -253,9 +263,11 @@ export default {
} }
}, },
isSelected (val) { isSelected (val) {
if (val && this.lastTable !== this.table) { if (val) {
this.getTableData(); this.changeBreadcrumbs({ schema: this.schema, [this.elementType]: this.table });
this.lastTable = this.table;
if (this.lastTable !== this.table)
this.getTableData();
} }
} }
}, },
@@ -269,9 +281,10 @@ export default {
}, },
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification' addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}), }),
async getTableData (sortParams) { async getTableData () {
if (!this.table) return; if (!this.table) return;
this.isQuering = true; this.isQuering = true;
@@ -279,13 +292,15 @@ export default {
if (this.lastTable !== this.table) if (this.lastTable !== this.table)
this.results = []; this.results = [];
this.lastTable = this.table;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
table: this.workspace.breadcrumbs.table || this.workspace.breadcrumbs.view, table: this.table,
limit: this.limit, limit: this.limit,
page: this.page, page: this.page,
sortParams sortParams: this.sortParams
}; };
try { // Table data try { // Table data
@@ -306,14 +321,14 @@ export default {
return this.table; return this.table;
}, },
reloadTable () { reloadTable () {
this.getTableData(this.sortParams); this.getTableData();
}, },
hardSort (sortParams) { hardSort (sortParams) {
this.sortParams = sortParams; this.sortParams = sortParams;
this.getTableData(sortParams); this.getTableData();
}, },
openPageMenu () { openPageMenu () {
if (this.isQuering) return; if (this.isQuering || (this.results.length && this.results[0].rows.length < this.limit && this.page === 1)) return;
this.isPageMenu = true; this.isPageMenu = true;
if (this.isPageMenu) if (this.isPageMenu)

View File

@@ -1,489 +0,0 @@
<template>
<div class="modal active">
<a class="modal-overlay c-hand" @click="closeModal" />
<div class="modal-container">
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-server mr-1" /> {{ $t('message.editConnection') }}
</div>
</div>
<a class="btn btn-clear c-hand" @click="closeModal" />
</div>
<div class="modal-body p-0">
<div class="panel">
<div class="panel-nav">
<ul class="tab tab-block">
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'general'}"
@click="selectTab('general')"
>
<a class="tab-link">{{ $t('word.general') }}</a>
</li>
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'ssl'}"
@click="selectTab('ssl')"
>
<a class="tab-link">{{ $t('word.ssl') }}</a>
</li>
<li
class="tab-item"
:class="{'active': selectedTab === 'ssh'}"
@click="selectTab('ssh')"
>
<a class="c-hand">{{ $t('word.sshTunnel') }}</a>
</li>
</ul>
</div>
<div v-if="selectedTab === 'general'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<fieldset class="m-0" :disabled="isTesting">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.connectionName') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="localConnection.name"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.client') }}</label>
</div>
<div class="col-8 col-sm-12">
<select v-model="localConnection.client" class="form-select">
<option value="mysql">
MySQL
</option>
<option value="maria">
MariaDB
</option>
<option value="pg">
PostgreSQL
</option>
<!-- <option value="mssql">
Microsoft SQL
</option>
<option value="oracledb">
Oracle DB
</option> -->
</select>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.host"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.port"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div v-if="customizations.database" class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.database') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.database"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.user"
class="form-input"
type="text"
:disabled="localConnection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.password"
class="form-input"
type="password"
:disabled="localConnection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12" />
<div class="col-8 col-sm-12">
<label class="form-checkbox form-inline">
<input v-model="localConnection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
</label>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsl') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
<input type="checkbox" :checked="localConnection.ssl">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !localConnection.ssl">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.key"
:message="$t('word.browse')"
@clear="pathClear('key')"
@change="pathSelection($event, 'key')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.certificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.cert"
:message="$t('word.browse')"
@clear="pathClear('cert')"
@change="pathSelection($event, 'cert')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.caCertificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.ca"
:message="$t('word.browse')"
@clear="pathClear('ca')"
@change="pathSelection($event, 'ca')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.ciphers') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="localConnection.ciphers"
class="form-input"
type="text"
>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsh') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
<input type="checkbox" :checked="localConnection.ssh">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !localConnection.ssh">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshHost"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshUser"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshPass"
class="form-input"
type="password"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshPort"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.sshKey"
:message="$t('word.browse')"
@clear="pathClear('sshKey')"
@change="pathSelection($event, 'sshKey')"
/>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div class="modal-footer text-light">
<button
class="btn btn-gray mr-2"
:class="{'loading': isTesting}"
@click="startTest"
>
{{ $t('message.testConnection') }}
</button>
<button class="btn btn-primary mr-2" @click="saveEditConnection">
{{ $t('word.save') }}
</button>
<button class="btn btn-link" @click="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div>
<ModalAskCredentials
v-if="isAsking"
@close-asking="closeAsking"
@credentials="continueTest"
/>
</div>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import customizations from 'common/customizations';
import Connection from '@/ipc-api/Connection';
import ModalAskCredentials from '@/components/ModalAskCredentials';
import BaseToast from '@/components/BaseToast';
import BaseUploadInput from '@/components/BaseUploadInput';
export default {
name: 'ModalEditConnection',
components: {
ModalAskCredentials,
BaseToast,
BaseUploadInput
},
props: {
connection: Object
},
data () {
return {
toast: {
status: '',
message: ''
},
isTesting: false,
isAsking: false,
localConnection: null,
selectedTab: 'general'
};
},
computed: {
customizations () {
return customizations[this.connection.client];
}
},
created () {
this.localConnection = Object.assign({}, this.connection);
window.addEventListener('keydown', this.onKey);
setTimeout(() => {
this.$refs.firstInput.focus();
}, 20);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: {
...mapActions({
editConnection: 'connections/editConnection'
}),
async startTest () {
this.isTesting = true;
this.toast = {
status: '',
message: ''
};
if (this.localConnection.ask)
this.isAsking = true;
else {
try {
const res = await Connection.makeTest(this.localConnection);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
}
},
async continueTest (credentials) { // if "Ask for credentials" is true
this.isAsking = false;
const params = Object.assign({}, this.localConnection, credentials);
try {
const res = await Connection.makeTest(params);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
},
saveEditConnection () {
this.editConnection(this.localConnection);
this.closeModal();
},
closeAsking () {
this.isTesting = false;
this.isAsking = false;
},
closeModal () {
this.$emit('close');
},
onKey (e) {
e.stopPropagation();
if (e.key === 'Escape')
this.closeModal();
},
selectTab (tab) {
this.selectedTab = tab;
},
toggleSsl () {
this.localConnection.ssl = !this.localConnection.ssl;
},
toggleSsh () {
this.localConnection.ssh = !this.localConnection.ssh;
},
pathSelection (event, name) {
const { files } = event.target;
if (!files.length) return;
this.localConnection[name] = files[0].path;
},
pathClear (name) {
this.localConnection[name] = '';
}
}
};
</script>
<style scoped>
.modal-container {
position: absolute;
max-width: 450px;
top: 17.5vh;
}
</style>

View File

@@ -1,518 +0,0 @@
<template>
<div class="modal active">
<a class="modal-overlay c-hand" @click="closeModal" />
<div class="modal-container">
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-server-plus mr-1" />
<span class="cut-text">{{ $t('message.createNewConnection') }}</span>
</div>
</div>
<a class="btn btn-clear c-hand" @click="closeModal" />
</div>
<div class="modal-body p-0">
<div class="panel">
<div class="panel-nav">
<ul class="tab tab-block">
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'general'}"
@click="selectTab('general')"
>
<a class="tab-link">{{ $t('word.general') }}</a>
</li>
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'ssl'}"
@click="selectTab('ssl')"
>
<a class="tab-link">{{ $t('word.ssl') }}</a>
</li>
<li
class="tab-item"
:class="{'active': selectedTab === 'ssh'}"
@click="selectTab('ssh')"
>
<a class="c-hand">{{ $t('word.sshTunnel') }}</a>
</li>
</ul>
</div>
<div v-if="selectedTab === 'general'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<fieldset class="m-0" :disabled="isTesting">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.connectionName') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="connection.name"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.client') }}</label>
</div>
<div class="col-8 col-sm-12">
<select
v-model="connection.client"
class="form-select"
@change="setDefaults"
>
<option value="mysql">
MySQL
</option>
<option value="maria">
MariaDB
</option>
<option value="pg">
PostgreSQL
</option>
<!-- <option value="mssql">
Microsoft SQL
</option>
<option value="oracledb">
Oracle DB
</option> -->
</select>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.host"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.port"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div v-if="customizations.database" class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.database') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.database"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.user"
class="form-input"
type="text"
:disabled="connection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.password"
class="form-input"
type="password"
:disabled="connection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12" />
<div class="col-8 col-sm-12">
<label class="form-checkbox form-inline">
<input v-model="connection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
</label>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsl') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
<input type="checkbox" :checked="connection.ssl">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !connection.ssl">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.key"
:message="$t('word.browse')"
@clear="pathClear('key')"
@change="pathSelection($event, 'key')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.certificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.cert"
:message="$t('word.browse')"
@clear="pathClear('cert')"
@change="pathSelection($event, 'cert')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.caCertificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.ca"
:message="$t('word.browse')"
@clear="pathClear('ca')"
@change="pathSelection($event, 'ca')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.ciphers') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="connection.ciphers"
class="form-input"
type="text"
>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsh') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
<input type="checkbox" :checked="connection.ssh">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !connection.ssh">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshHost"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshUser"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshPass"
class="form-input"
type="password"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshPort"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.sshKey"
:message="$t('word.browse')"
@clear="pathClear('sshKey')"
@change="pathSelection($event, 'sshKey')"
/>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
</div>
</div>
<div class="modal-footer text-light">
<button
class="btn btn-gray mr-2"
:class="{'loading': isTesting}"
@click="startTest"
>
{{ $t('message.testConnection') }}
</button>
<button class="btn btn-primary mr-2" @click="saveNewConnection">
{{ $t('word.save') }}
</button>
<button class="btn btn-link" @click="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div>
<ModalAskCredentials
v-if="isAsking"
@close-asking="closeAsking"
@credentials="continueTest"
/>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import customizations from 'common/customizations';
import Connection from '@/ipc-api/Connection';
import { uidGen } from 'common/libs/uidGen';
import ModalAskCredentials from '@/components/ModalAskCredentials';
import BaseToast from '@/components/BaseToast';
import BaseUploadInput from '@/components/BaseUploadInput';
export default {
name: 'ModalNewConnection',
components: {
ModalAskCredentials,
BaseToast,
BaseUploadInput
},
data () {
return {
connection: {
name: '',
client: 'mysql',
host: '127.0.0.1',
database: null,
port: null,
user: null,
password: '',
ask: false,
uid: uidGen('C'),
ssl: false,
cert: '',
key: '',
ca: '',
ciphers: '',
ssh: false,
sshHost: '',
sshUser: '',
sshPass: '',
sshKey: '',
sshPort: 22
},
toast: {
status: '',
message: ''
},
isTesting: false,
isAsking: false,
selectedTab: 'general'
};
},
computed: {
customizations () {
return customizations[this.connection.client];
}
},
created () {
this.setDefaults();
window.addEventListener('keydown', this.onKey);
setTimeout(() => {
this.$refs.firstInput.focus();
}, 20);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: {
...mapActions({
closeModal: 'application/hideNewConnModal',
addConnection: 'connections/addConnection'
}),
setDefaults () {
this.connection.user = this.customizations.defaultUser;
this.connection.port = this.customizations.defaultPort;
this.connection.database = this.customizations.defaultDatabase;
},
async startTest () {
this.isTesting = true;
this.toast = {
status: '',
message: ''
};
if (this.connection.ask)
this.isAsking = true;
else {
try {
const res = await Connection.makeTest(this.connection);
console.log(res.response);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
}
},
async continueTest (credentials) { // if "Ask for credentials" is true
this.isAsking = false;
const params = Object.assign({}, this.connection, credentials);
try {
const res = await Connection.makeTest(params);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
},
saveNewConnection () {
this.addConnection(this.connection);
this.closeModal();
},
closeAsking () {
this.isAsking = false;
this.isTesting = false;
},
onKey (e) {
e.stopPropagation();
if (e.key === 'Escape')
this.closeModal();
},
selectTab (tab) {
this.selectedTab = tab;
},
toggleSsl () {
this.connection.ssl = !this.connection.ssl;
},
toggleSsh () {
this.connection.ssh = !this.connection.ssh;
},
pathSelection (event, name) {
const { files } = event.target;
if (!files.length) return;
this.connection[name] = files[0].path;
},
pathClear (name) {
this.connection[name] = '';
}
}
};
</script>
<style scoped>
.modal-container {
position: absolute;
max-width: 450px;
top: 17.5vh;
}
</style>

View File

@@ -1,37 +0,0 @@
<template>
<div class="columns">
<div class="column col-12 empty text-light">
<div class="empty-icon">
<i class="mdi mdi-48px mdi-emoticon" />
</div>
<p class="empty-title h5">
{{ $t('message.appWelcome') }}
</p>
<p class="empty-subtitle">
{{ $t('message.appFirstStep') }}
</p>
<div class="empty-action">
<button class="btn btn-primary" @click="$emit('new-conn')">
{{ $t('message.createConnection') }}
</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TheAppWelcome'
};
</script>
<style scoped>
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>

View File

@@ -1,82 +0,0 @@
<template>
<div class="columns">
<div class="column col-12 empty">
<div class="empty-icon">
<i class="mdi mdi-48px mdi-power-plug-off" />
</div>
<p class="empty-title h5">
{{ isConnecting ? $t('word.connecting') : $t('word.disconnected') }}
</p>
<div class="empty-action">
<button
class="btn btn-success"
:class="{'loading': isConnecting}"
@click="startConnection"
>
{{ $t('word.connect') }}
</button>
</div>
</div>
<ModalAskCredentials
v-if="isAsking"
@close-asking="closeAsking"
@credentials="continueTest"
/>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import ModalAskCredentials from '@/components/ModalAskCredentials';
export default {
name: 'WorkspaceConnectPanel',
components: {
ModalAskCredentials
},
props: {
connection: Object
},
data () {
return {
isConnecting: false,
isAsking: false
};
},
methods: {
...mapActions({
connectWorkspace: 'workspaces/connectWorkspace'
}),
async startConnection () {
this.isConnecting = true;
if (this.connection.ask)
this.isAsking = true;
else {
await this.connectWorkspace(this.connection);
this.isConnecting = false;
}
},
async continueTest (credentials) { // if "Ask for credentials" is true
this.isAsking = false;
const params = Object.assign({}, this.connection, credentials);
await this.connectWorkspace(params);
this.isConnecting = false;
},
closeAsking () {
this.isAsking = false;
this.isConnecting = false;
}
}
};
</script>
<style scoped>
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
}
</style>

View File

@@ -169,7 +169,7 @@ module.exports = {
deleteTable: 'Delete table', deleteTable: 'Delete table',
emptyCorfirm: 'Do you confirm to empty', emptyCorfirm: 'Do you confirm to empty',
unsavedChanges: 'Unsaved changes', unsavedChanges: 'Unsaved changes',
discardUnsavedChanges: 'You have some unsaved changes. By leaving this tab these changes will be discarded.', discardUnsavedChanges: 'You have some unsaved changes. Closing this tab these changes will be discarded.',
thereAreNoIndexes: 'There are no indexes', thereAreNoIndexes: 'There are no indexes',
thereAreNoForeign: 'There are no foreign keys', thereAreNoForeign: 'There are no foreign keys',
createNewForeign: 'Create new foreign key', createNewForeign: 'Create new foreign key',
@@ -224,7 +224,10 @@ module.exports = {
dataTabPageSize: 'DATA tab page size', dataTabPageSize: 'DATA tab page size',
enableSsh: 'Enable SSH', enableSsh: 'Enable SSH',
pageNumber: 'Page number', pageNumber: 'Page number',
duplicateTable: 'Duplicate table' duplicateTable: 'Duplicate table',
noOpenTabs: 'There are no open tabs, navigate on the left bar or:',
noSchema: 'No schema',
restorePreviourSession: 'Restore previous session'
}, },
faker: { faker: {
address: 'Address', address: 'Address',

View File

@@ -0,0 +1,301 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 1024 1024"
style="enable-background:new 0 0 1024 1024;"
xml:space="preserve"
sodipodi:docname="Antares-shape-2.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs80" /><sodipodi:namedview
id="namedview78"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.66015625"
inkscape:cx="210.55621"
inkscape:cy="511.2426"
inkscape:window-width="2560"
inkscape:window-height="1009"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g75" />
<style
type="text/css"
id="style2">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#XMLID_15_);}
.st2{opacity:0.5;fill:#FFBC00;}
.st3{fill:#FFBC00;}
.st4{fill:url(#XMLID_16_);}
.st5{fill:url(#XMLID_17_);}
.st6{fill:url(#XMLID_18_);}
.st7{fill:url(#XMLID_19_);}
.st8{fill:url(#XMLID_20_);}
.st9{fill:url(#XMLID_21_);}
.st10{opacity:0.46;}
.st11{fill:url(#XMLID_23_);}
.st12{fill:url(#XMLID_24_);}
.st13{fill:url(#XMLID_25_);}
.st14{fill:url(#XMLID_27_);}
.st15{fill:url(#XMLID_28_);}
.st16{fill:url(#XMLID_29_);}
.st17{fill:url(#XMLID_30_);}
.st18{opacity:0.75;}
.st19{opacity:0.44;clip-path:url(#XMLID_31_);}
.st20{fill:url(#XMLID_32_);}
.st21{fill:url(#XMLID_33_);}
.st22{fill:url(#XMLID_34_);}
.st23{fill:url(#XMLID_35_);}
.st24{fill:url(#XMLID_37_);}
.st25{fill:url(#XMLID_38_);}
.st26{opacity:0.43;fill:#FFBC00;}
.st27{opacity:0.58;fill:#FFBC00;}
.st28{fill:#FFBE06;}
.st29{fill:none;}
.st30{fill:url(#XMLID_42_);}
.st31{fill:url(#XMLID_43_);}
.st32{fill:url(#XMLID_44_);}
.st33{fill:url(#XMLID_45_);}
.st34{fill:url(#XMLID_46_);}
.st35{fill:url(#SVGID_4_);}
.st36{fill:url(#SVGID_5_);}
.st37{fill:url(#SVGID_6_);}
.st38{fill:url(#SVGID_7_);}
.st39{fill:url(#SVGID_8_);}
.st40{fill:url(#SVGID_11_);}
.st41{fill:url(#SVGID_12_);}
.st42{fill:url(#SVGID_13_);}
.st43{fill:url(#SVGID_14_);}
.st44{fill:#C68D00;}
.st45{fill:#CE000F;}
</style>
<g
id="g75">
<radialGradient
id="XMLID_15_"
cx="358.2692"
cy="227.2655"
r="830.0055"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
style="stop-color:#F6971E"
id="stop4" />
<stop
offset="0.6338"
style="stop-color:#F4592D"
id="stop6" />
<stop
offset="0.7025"
style="stop-color:#EF4F29"
id="stop8" />
<stop
offset="0.8178"
style="stop-color:#E1351D"
id="stop10" />
<stop
offset="0.9647"
style="stop-color:#CA0B0B"
id="stop12" />
<stop
offset="1"
style="stop-color:#C40006"
id="stop14" />
</radialGradient>
<path
id="XMLID_124_"
style="fill:#ffffff;fill-opacity:0.15000001"
class="st1"
d="M 510.30078 9.0996094 A 502.79999 502.79999 0 0 0 7.5 511.90039 A 502.79999 502.79999 0 0 0 510.30078 1014.6992 A 502.79999 502.79999 0 0 0 1013.0996 511.90039 A 502.79999 502.79999 0 0 0 510.30078 9.0996094 z M 326.17578 78.685547 C 349.74023 78.648438 372.16211 83.875 393.09961 95 C 420.19961 112.9 439.6 136.99922 455 172.69922 C 465.4 205.69922 469.30078 238.79961 465.80078 281.59961 C 457.70078 373.59961 411.09922 469.09961 341.19922 531.59961 C 312.29922 557.49961 284.4 573.59961 256.5 589.59961 L 256.69922 593.19922 C 279.39922 594.99922 301.39922 586.10078 326.19922 571.80078 C 415.89922 516.50078 483.49922 397.69922 504.69922 285.19922 C 495.49922 338.79922 478.19922 391.4 449.69922 445 C 423.79922 489.6 397.30039 527.1 359.40039 562 C 300.00039 612.9 238.89961 638.80078 183.09961 626.30078 C 122.39961 611.00078 90.399609 545.8 92.599609 461 C 92.399609 457.4 92.1 453.89961 89 455.59961 C 87.6 458.29961 84.700781 463.60078 83.300781 466.30078 C 69.000781 517.30078 77.1 562.79922 91 601.19922 C 116.5 666.39922 177.79922 688.69922 244.69922 676.19922 C 204.99922 685.99922 167.69922 683.3 134.19922 665.5 C 107.09922 647.6 84.599609 625.30039 70.599609 586.90039 C 55.899609 537.80039 50.700391 486.89922 70.400391 421.69922 C 95.300391 338.69922 139.50078 255.59922 207.30078 209.19922 C 227.30078 195.79922 245.99922 185.09961 267.69922 172.59961 C 270.79922 170.79961 270.60039 167.20039 268.90039 166.40039 C 159.40039 170.00039 46.500391 333.30039 20.900391 474.40039 C 36.400391 372.60039 87.500391 272.6 167.90039 198.5 C 213.50039 157.4 258.79922 136.89922 305.19922 130.69922 C 386.89922 122.69922 437.10078 194.1 434.80078 299.5 C 435.00078 303.1 436.70039 304.00039 438.40039 304.90039 C 446.50039 281.70039 451.39961 260.29922 451.59961 239.69922 C 451.50293 126.5894 381.21362 66.112098 291.87891 82.363281 C 288.79216 82.977349 285.71662 83.531343 282.59961 84.300781 C 285.71508 83.559319 288.80642 82.922208 291.87891 82.363281 C 303.5351 80.044429 314.99734 78.703151 326.17578 78.685547 z M 858.04883 508.3457 C 873.78027 508.64453 888.6875 512.44922 902.5 520.19922 C 920.3 532.49922 935.10078 547.69961 943.80078 573.59961 C 952.90078 606.59961 955.59961 640.70039 941.59961 683.90039 C 923.79961 739.00039 893.09961 793.80039 847.09961 823.90039 C 833.49961 832.60039 820.89922 839.4 806.19922 847.5 C 804.09922 848.6 804.20078 850.99922 805.30078 851.69922 C 878.70078 851.09922 956.39961 743.59922 975.59961 649.69922 C 963.79961 717.49922 928.2 783.50078 873.5 831.80078 C 842.5 858.60078 811.90078 871.59961 780.80078 875.09961 C 726.10078 879.29961 693.59922 830.9 696.69922 760.5 C 696.59922 758.1 695.50039 757.50039 694.40039 756.90039 C 688.70039 772.30039 685.09961 786.49922 684.59961 800.19922 C 683.15311 870.80636 723.40104 911.63389 777.53906 908.77344 C 757.57984 910.46578 738.70856 907.29881 721.59961 897.69922 C 703.79961 885.39922 691.10039 869.00039 681.40039 844.90039 C 674.90039 822.70039 672.79922 800.6 675.69922 772 C 682.39922 710.7 714.9 647.60078 762.5 606.80078 C 782.1 589.90078 801.00039 579.60078 819.90039 569.30078 L 819.80078 566.90039 C 804.70078 565.40039 789.89961 570.99922 773.09961 580.19922 C 712.39961 615.89922 665.50078 694.2 649.80078 769 C 656.70078 733.4 669.00078 698.39961 688.80078 663.09961 C 706.80078 633.69961 725.00078 609.00078 750.80078 586.30078 C 791.20078 553.20078 832.40039 536.80039 869.40039 545.90039 C 909.80039 556.90039 930.2 600.9 927.5 657.5 C 927.6 659.9 927.70078 662.29961 929.80078 661.09961 C 930.80078 659.29961 932.80078 655.8 933.80078 654 C 944.00078 620.2 939.3 589.70078 930.5 563.80078 C 914.4 519.90078 873.80039 504.1 828.90039 511.5 C 838.87539 509.25 848.60996 508.16641 858.04883 508.3457 z " />
<linearGradient
id="XMLID_16_"
gradientUnits="userSpaceOnUse"
x1="505.4734"
y1="-52.674"
x2="505.4734"
y2="155.1105">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop18" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop20" />
</linearGradient>
<linearGradient
id="XMLID_17_"
gradientUnits="userSpaceOnUse"
x1="503.3253"
y1="-583.7885"
x2="503.3253"
y2="-376.4571"
gradientTransform="matrix(-1 0 0 -1 1017 456.5313)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop24" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop26" />
</linearGradient>
<linearGradient
id="XMLID_18_"
gradientUnits="userSpaceOnUse"
x1="506.1886"
y1="-38.7551"
x2="506.1886"
y2="169.0294"
gradientTransform="matrix(4.489700e-11 1 -1 4.489700e-11 1026.6101 -2.3899)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop30" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop32" />
</linearGradient>
<linearGradient
id="XMLID_19_"
gradientUnits="userSpaceOnUse"
x1="502.6101"
y1="-660.7308"
x2="502.6101"
y2="-450.373"
gradientTransform="matrix(-4.489700e-11 -1 1 -4.489700e-11 570.0789 1014.6101)">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop36" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop38" />
</linearGradient>
<g
id="XMLID_39_"
class="st10">
<defs
id="defs43">
<ellipse
id="XMLID_36_"
transform="matrix(0.8019 -0.5974 0.5974 0.8019 -204.724 406.2339)"
class="st10"
cx="510.3"
cy="511.9"
ry="502.8"
rx="502.8" />
</defs>
<clipPath
id="XMLID_20_">
<use
xlink:href="#XMLID_36_"
style="overflow:visible;"
id="use45" />
</clipPath>
</g>
<g
id="XMLID_41_">
<linearGradient
id="XMLID_21_"
gradientUnits="userSpaceOnUse"
x1="64.4989"
y1="234.8705"
x2="401.1502"
y2="480.7042">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop49" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop51" />
</linearGradient>
<linearGradient
id="XMLID_22_"
gradientUnits="userSpaceOnUse"
x1="438.1351"
y1="490.0881"
x2="196.6566"
y2="356.1772">
<stop
offset="0"
style="stop-color:#F64626"
id="stop55" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop57" />
</linearGradient>
</g>
<g
id="XMLID_11_">
<linearGradient
id="XMLID_23_"
gradientUnits="userSpaceOnUse"
x1="-316.9261"
y1="9.5862"
x2="-92.0354"
y2="173.8087"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop62" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop64" />
</linearGradient>
<linearGradient
id="XMLID_24_"
gradientUnits="userSpaceOnUse"
x1="-52.9013"
y1="188.0779"
x2="-214.2144"
y2="98.6225"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F42C2D"
id="stop68" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop70" />
</linearGradient>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,301 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 1024 1024"
style="enable-background:new 0 0 1024 1024;"
xml:space="preserve"
sodipodi:docname="Antares-shape-1.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs80" /><sodipodi:namedview
id="namedview78"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.66015625"
inkscape:cx="512"
inkscape:cy="511.2426"
inkscape:window-width="2560"
inkscape:window-height="1009"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g75" />
<style
type="text/css"
id="style2">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#XMLID_15_);}
.st2{opacity:0.5;fill:#FFBC00;}
.st3{fill:#FFBC00;}
.st4{fill:url(#XMLID_16_);}
.st5{fill:url(#XMLID_17_);}
.st6{fill:url(#XMLID_18_);}
.st7{fill:url(#XMLID_19_);}
.st8{fill:url(#XMLID_20_);}
.st9{fill:url(#XMLID_21_);}
.st10{opacity:0.46;}
.st11{fill:url(#XMLID_23_);}
.st12{fill:url(#XMLID_24_);}
.st13{fill:url(#XMLID_25_);}
.st14{fill:url(#XMLID_27_);}
.st15{fill:url(#XMLID_28_);}
.st16{fill:url(#XMLID_29_);}
.st17{fill:url(#XMLID_30_);}
.st18{opacity:0.75;}
.st19{opacity:0.44;clip-path:url(#XMLID_31_);}
.st20{fill:url(#XMLID_32_);}
.st21{fill:url(#XMLID_33_);}
.st22{fill:url(#XMLID_34_);}
.st23{fill:url(#XMLID_35_);}
.st24{fill:url(#XMLID_37_);}
.st25{fill:url(#XMLID_38_);}
.st26{opacity:0.43;fill:#FFBC00;}
.st27{opacity:0.58;fill:#FFBC00;}
.st28{fill:#FFBE06;}
.st29{fill:none;}
.st30{fill:url(#XMLID_42_);}
.st31{fill:url(#XMLID_43_);}
.st32{fill:url(#XMLID_44_);}
.st33{fill:url(#XMLID_45_);}
.st34{fill:url(#XMLID_46_);}
.st35{fill:url(#SVGID_4_);}
.st36{fill:url(#SVGID_5_);}
.st37{fill:url(#SVGID_6_);}
.st38{fill:url(#SVGID_7_);}
.st39{fill:url(#SVGID_8_);}
.st40{fill:url(#SVGID_11_);}
.st41{fill:url(#SVGID_12_);}
.st42{fill:url(#SVGID_13_);}
.st43{fill:url(#SVGID_14_);}
.st44{fill:#C68D00;}
.st45{fill:#CE000F;}
</style>
<g
id="g75">
<radialGradient
id="XMLID_15_"
cx="358.2692"
cy="227.2655"
r="830.0055"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
style="stop-color:#F6971E"
id="stop4" />
<stop
offset="0.6338"
style="stop-color:#F4592D"
id="stop6" />
<stop
offset="0.7025"
style="stop-color:#EF4F29"
id="stop8" />
<stop
offset="0.8178"
style="stop-color:#E1351D"
id="stop10" />
<stop
offset="0.9647"
style="stop-color:#CA0B0B"
id="stop12" />
<stop
offset="1"
style="stop-color:#C40006"
id="stop14" />
</radialGradient>
<path
id="XMLID_124_"
style="fill:#000000;fill-opacity:0.5"
class="st1"
d="M 510.30078 9.0996094 A 502.79999 502.79999 0 0 0 7.5 511.90039 A 502.79999 502.79999 0 0 0 510.30078 1014.6992 A 502.79999 502.79999 0 0 0 1013.0996 511.90039 A 502.79999 502.79999 0 0 0 510.30078 9.0996094 z M 326.17578 78.685547 C 349.74023 78.648438 372.16211 83.875 393.09961 95 C 420.19961 112.9 439.6 136.99922 455 172.69922 C 465.4 205.69922 469.30078 238.79961 465.80078 281.59961 C 457.70078 373.59961 411.09922 469.09961 341.19922 531.59961 C 312.29922 557.49961 284.4 573.59961 256.5 589.59961 L 256.69922 593.19922 C 279.39922 594.99922 301.39922 586.10078 326.19922 571.80078 C 415.89922 516.50078 483.49922 397.69922 504.69922 285.19922 C 495.49922 338.79922 478.19922 391.4 449.69922 445 C 423.79922 489.6 397.30039 527.1 359.40039 562 C 300.00039 612.9 238.89961 638.80078 183.09961 626.30078 C 122.39961 611.00078 90.399609 545.8 92.599609 461 C 92.399609 457.4 92.1 453.89961 89 455.59961 C 87.6 458.29961 84.700781 463.60078 83.300781 466.30078 C 69.000781 517.30078 77.1 562.79922 91 601.19922 C 116.5 666.39922 177.79922 688.69922 244.69922 676.19922 C 204.99922 685.99922 167.69922 683.3 134.19922 665.5 C 107.09922 647.6 84.599609 625.30039 70.599609 586.90039 C 55.899609 537.80039 50.700391 486.89922 70.400391 421.69922 C 95.300391 338.69922 139.50078 255.59922 207.30078 209.19922 C 227.30078 195.79922 245.99922 185.09961 267.69922 172.59961 C 270.79922 170.79961 270.60039 167.20039 268.90039 166.40039 C 159.40039 170.00039 46.500391 333.30039 20.900391 474.40039 C 36.400391 372.60039 87.500391 272.6 167.90039 198.5 C 213.50039 157.4 258.79922 136.89922 305.19922 130.69922 C 386.89922 122.69922 437.10078 194.1 434.80078 299.5 C 435.00078 303.1 436.70039 304.00039 438.40039 304.90039 C 446.50039 281.70039 451.39961 260.29922 451.59961 239.69922 C 451.50293 126.5894 381.21362 66.112098 291.87891 82.363281 C 288.79216 82.977349 285.71662 83.531343 282.59961 84.300781 C 285.71508 83.559319 288.80642 82.922208 291.87891 82.363281 C 303.5351 80.044429 314.99734 78.703151 326.17578 78.685547 z M 858.04883 508.3457 C 873.78027 508.64453 888.6875 512.44922 902.5 520.19922 C 920.3 532.49922 935.10078 547.69961 943.80078 573.59961 C 952.90078 606.59961 955.59961 640.70039 941.59961 683.90039 C 923.79961 739.00039 893.09961 793.80039 847.09961 823.90039 C 833.49961 832.60039 820.89922 839.4 806.19922 847.5 C 804.09922 848.6 804.20078 850.99922 805.30078 851.69922 C 878.70078 851.09922 956.39961 743.59922 975.59961 649.69922 C 963.79961 717.49922 928.2 783.50078 873.5 831.80078 C 842.5 858.60078 811.90078 871.59961 780.80078 875.09961 C 726.10078 879.29961 693.59922 830.9 696.69922 760.5 C 696.59922 758.1 695.50039 757.50039 694.40039 756.90039 C 688.70039 772.30039 685.09961 786.49922 684.59961 800.19922 C 683.15311 870.80636 723.40104 911.63389 777.53906 908.77344 C 757.57984 910.46578 738.70856 907.29881 721.59961 897.69922 C 703.79961 885.39922 691.10039 869.00039 681.40039 844.90039 C 674.90039 822.70039 672.79922 800.6 675.69922 772 C 682.39922 710.7 714.9 647.60078 762.5 606.80078 C 782.1 589.90078 801.00039 579.60078 819.90039 569.30078 L 819.80078 566.90039 C 804.70078 565.40039 789.89961 570.99922 773.09961 580.19922 C 712.39961 615.89922 665.50078 694.2 649.80078 769 C 656.70078 733.4 669.00078 698.39961 688.80078 663.09961 C 706.80078 633.69961 725.00078 609.00078 750.80078 586.30078 C 791.20078 553.20078 832.40039 536.80039 869.40039 545.90039 C 909.80039 556.90039 930.2 600.9 927.5 657.5 C 927.6 659.9 927.70078 662.29961 929.80078 661.09961 C 930.80078 659.29961 932.80078 655.8 933.80078 654 C 944.00078 620.2 939.3 589.70078 930.5 563.80078 C 914.4 519.90078 873.80039 504.1 828.90039 511.5 C 838.87539 509.25 848.60996 508.16641 858.04883 508.3457 z " />
<linearGradient
id="XMLID_16_"
gradientUnits="userSpaceOnUse"
x1="505.4734"
y1="-52.674"
x2="505.4734"
y2="155.1105">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop18" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop20" />
</linearGradient>
<linearGradient
id="XMLID_17_"
gradientUnits="userSpaceOnUse"
x1="503.3253"
y1="-583.7885"
x2="503.3253"
y2="-376.4571"
gradientTransform="matrix(-1 0 0 -1 1017 456.5313)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop24" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop26" />
</linearGradient>
<linearGradient
id="XMLID_18_"
gradientUnits="userSpaceOnUse"
x1="506.1886"
y1="-38.7551"
x2="506.1886"
y2="169.0294"
gradientTransform="matrix(4.489700e-11 1 -1 4.489700e-11 1026.6101 -2.3899)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop30" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop32" />
</linearGradient>
<linearGradient
id="XMLID_19_"
gradientUnits="userSpaceOnUse"
x1="502.6101"
y1="-660.7308"
x2="502.6101"
y2="-450.373"
gradientTransform="matrix(-4.489700e-11 -1 1 -4.489700e-11 570.0789 1014.6101)">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop36" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop38" />
</linearGradient>
<g
id="XMLID_39_"
class="st10">
<defs
id="defs43">
<ellipse
id="XMLID_36_"
transform="matrix(0.8019 -0.5974 0.5974 0.8019 -204.724 406.2339)"
class="st10"
cx="510.3"
cy="511.9"
ry="502.8"
rx="502.8" />
</defs>
<clipPath
id="XMLID_20_">
<use
xlink:href="#XMLID_36_"
style="overflow:visible;"
id="use45" />
</clipPath>
</g>
<g
id="XMLID_41_">
<linearGradient
id="XMLID_21_"
gradientUnits="userSpaceOnUse"
x1="64.4989"
y1="234.8705"
x2="401.1502"
y2="480.7042">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop49" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop51" />
</linearGradient>
<linearGradient
id="XMLID_22_"
gradientUnits="userSpaceOnUse"
x1="438.1351"
y1="490.0881"
x2="196.6566"
y2="356.1772">
<stop
offset="0"
style="stop-color:#F64626"
id="stop55" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop57" />
</linearGradient>
</g>
<g
id="XMLID_11_">
<linearGradient
id="XMLID_23_"
gradientUnits="userSpaceOnUse"
x1="-316.9261"
y1="9.5862"
x2="-92.0354"
y2="173.8087"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop62" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop64" />
</linearGradient>
<linearGradient
id="XMLID_24_"
gradientUnits="userSpaceOnUse"
x1="-52.9013"
y1="188.0779"
x2="-214.2144"
y2="98.6225"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F42C2D"
id="stop68" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop70" />
</linearGradient>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,11 +1,6 @@
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
export default { export default {
computed: {
schema () {
return this.workspace.breadcrumbs.schema;
}
},
methods: { methods: {
async updateField (payload) { async updateField (payload) {
this.isQuering = true; this.isQuering = true;

View File

@@ -63,6 +63,12 @@
background-color: $primary-color; background-color: $primary-color;
} }
} }
&.btn-clear {
&:hover {
background: rgba($light-color, 20%);
}
}
} }
.modal { .modal {
@@ -191,19 +197,8 @@
color: $body-font-color-dark; color: $body-font-color-dark;
} }
& &.tools-dropdown { &.tools-dropdown {
.tab-link:focus { background-color: $bg-color-light-dark;
color: $primary-color;
}
.menu {
.menu-item a {
&:hover {
color: $primary-color;
background: $bg-color-gray;
}
}
}
} }
} }
@@ -392,11 +387,6 @@
background: rgba(48, 55, 66, 0.95); background: rgba(48, 55, 66, 0.95);
color: #fff; color: #fff;
} }
&:hover .ex-tooltip-content {
visibility: visible;
opacity: 1;
}
} }
#footer { #footer {

View File

@@ -168,11 +168,6 @@
background: rgba(48, 55, 66, 0.95); background: rgba(48, 55, 66, 0.95);
color: #fff; color: #fff;
} }
&:hover .ex-tooltip-content {
visibility: visible;
opacity: 1;
}
} }
.workspace { .workspace {
@@ -191,6 +186,16 @@
} }
} }
.workspace-tabs {
.tab-block {
.tab-item {
&.tools-dropdown {
background-color: $body-bg;
}
}
}
}
.workspace-query-results { .workspace-query-results {
.table { .table {
.th { .th {

View File

@@ -16,7 +16,8 @@ export default {
line_wrap: persistentStore.get('line_wrap', true), line_wrap: persistentStore.get('line_wrap', true),
application_theme: persistentStore.get('application_theme', 'dark'), application_theme: persistentStore.get('application_theme', 'dark'),
editor_theme: persistentStore.get('editor_theme', 'twilight'), editor_theme: persistentStore.get('editor_theme', 'twilight'),
editor_font_size: persistentStore.get('editor_font_size', 'medium') editor_font_size: persistentStore.get('editor_font_size', 'medium'),
restore_tabs: persistentStore.get('restore_tabs', true)
}, },
getters: { getters: {
getLocale: state => state.locale, getLocale: state => state.locale,
@@ -28,7 +29,8 @@ export default {
getLineWrap: state => state.line_wrap, getLineWrap: state => state.line_wrap,
getApplicationTheme: state => state.application_theme, getApplicationTheme: state => state.application_theme,
getEditorTheme: state => state.editor_theme, getEditorTheme: state => state.editor_theme,
getEditorFontSize: state => state.editor_font_size getEditorFontSize: state => state.editor_font_size,
getRestoreTabs: state => state.restore_tabs
}, },
mutations: { mutations: {
SET_LOCALE (state, locale) { SET_LOCALE (state, locale) {
@@ -71,6 +73,10 @@ export default {
SET_EDITOR_FONT_SIZE (state, size) { SET_EDITOR_FONT_SIZE (state, size) {
state.editor_font_size = size; state.editor_font_size = size;
persistentStore.set('editor_font_size', state.editor_font_size); persistentStore.set('editor_font_size', state.editor_font_size);
},
SET_RESTORE_TABS (state, val) {
state.restore_tabs = val;
persistentStore.set('restore_tabs', state.restore_tabs);
} }
}, },
actions: { actions: {
@@ -103,6 +109,9 @@ export default {
}, },
changeEditorFontSize ({ commit }, size) { changeEditorFontSize ({ commit }, size) {
commit('SET_EDITOR_FONT_SIZE', size); commit('SET_EDITOR_FONT_SIZE', size);
},
changeRestoreTabs ({ commit }, size) {
commit('SET_RESTORE_TABS', size);
} }
} }
}; };

View File

@@ -1,20 +1,18 @@
'use strict'; 'use strict';
import Store from 'electron-store';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import Users from '@/ipc-api/Users'; import Users from '@/ipc-api/Users';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
const persistentStore = new Store({ name: 'tabs' });
const tabIndex = []; const tabIndex = [];
let lastBreadcrumbs = {};
export default { export default {
namespaced: true, namespaced: true,
strict: true, strict: true,
state: { state: {
workspaces: [], workspaces: [],
selected_workspace: null, selected_workspace: null
has_unsaved_changes: false,
is_unsaved_discard_modal: false,
pending_breadcrumbs: {}
}, },
getters: { getters: {
getSelected: state => { getSelected: state => {
@@ -45,9 +43,6 @@ export default {
}, },
getSearchTerm: state => uid => { getSearchTerm: state => uid => {
return state.workspaces.find(workspace => workspace.uid === uid).search_term; return state.workspaces.find(workspace => workspace.uid === uid).search_term;
},
isUnsavedDiscardModal: state => {
return state.is_unsaved_discard_modal;
} }
}, },
mutations: { mutations: {
@@ -60,6 +55,15 @@ export default {
SET_CONNECTED (state, payload) { SET_CONNECTED (state, payload) {
const { uid, client, dataTypes, indexTypes, customizations, structure, version } = payload; const { uid, client, dataTypes, indexTypes, customizations, structure, version } = payload;
const cachedTabs = payload.restoreTabs ? persistentStore.get(uid, []) : [];
if (cachedTabs.length) {
tabIndex[uid] = cachedTabs.reduce((acc, curr) => {
if (curr.index > acc) acc = curr.index;
return acc;
}, null);
}
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
? { ? {
...workspace, ...workspace,
@@ -69,6 +73,8 @@ export default {
customizations, customizations,
structure, structure,
connection_status: 'connected', connection_status: 'connected',
tabs: cachedTabs,
selected_tab: cachedTabs.length ? cachedTabs[0].uid : null,
version version
} }
: workspace); : workspace);
@@ -178,18 +184,24 @@ export default {
} }
: workspace); : workspace);
}, },
NEW_TAB (state, { uid, tab, content, autorun }) { NEW_TAB (state, { uid, tab, content, type, autorun, schema, elementName, elementType }) {
tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1; if (type === 'query')
tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1;
const newTab = { const newTab = {
uid: tab, uid: tab,
index: tabIndex[uid], index: type === 'query' ? tabIndex[uid] : null,
selected: false, selected: false,
type: 'query', type,
schema,
elementName,
elementType,
fields: [], fields: [],
keyUsage: [], keyUsage: [],
content: content || '', content: content || '',
autorun: !!autorun autorun: !!autorun
}; };
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) { if (workspace.uid === uid) {
return { return {
@@ -200,6 +212,8 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
REMOVE_TAB (state, { uid, tab: tUid }) { REMOVE_TAB (state, { uid, tab: tUid }) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
@@ -212,10 +226,78 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
REMOVE_TABS (state, { uid, schema, elementName, elementType }) { // Multiple tabs based on schema and element name
if (elementType === 'procedure') elementType = 'routine'; // TODO: pass directly "routine"
state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) {
return {
...workspace,
tabs: workspace.tabs.filter(tab =>
tab.schema !== schema ||
tab.elementName !== elementName ||
tab.elementType !== elementType
)
};
}
else
return workspace;
});
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
REPLACE_TAB (state, { uid, tab: tUid, type, schema, content, elementName, elementType }) {
state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) {
return {
...workspace,
tabs: workspace.tabs.map(tab => {
if (tab.uid === tUid)
return { ...tab, type, schema, content, elementName, elementType };
return tab;
})
};
}
else
return workspace;
});
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
RENAME_TABS (state, { uid, schema, elementName, elementType, elementNewName }) {
state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) {
return {
...workspace,
tabs: workspace.tabs.map(tab => {
if (tab.elementName === elementName && tab.schema === schema) {
return {
...tab,
elementName: elementNewName
};
}
return tab;
})
};
}
else
return workspace;
});
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
SELECT_TAB (state, { uid, tab }) { SELECT_TAB (state, { uid, tab }) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, selected_tab: tab } : workspace); state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, selected_tab: tab } : workspace);
}, },
UPDATE_TABS (state, { uid, tabs }) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, tabs } : workspace);
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
SET_TAB_FIELDS (state, { cUid, tUid, fields }) { SET_TAB_FIELDS (state, { cUid, tUid, fields }) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === cUid) { if (workspace.uid === cUid) {
@@ -232,6 +314,8 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
SET_TAB_KEY_USAGE (state, { cUid, tUid, keyUsage }) { SET_TAB_KEY_USAGE (state, { cUid, tUid, keyUsage }) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
@@ -249,15 +333,25 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
SET_UNSAVED_CHANGES (state, val) { SET_UNSAVED_CHANGES (state, { uid, tUid, isChanged }) {
state.has_unsaved_changes = !!val; state.workspaces = state.workspaces.map(workspace => {
}, if (workspace.uid === uid) {
SET_UNSAVED_DISCARD_MODAL (state, val) { return {
state.is_unsaved_discard_modal = !!val; ...workspace,
}, tabs: workspace.tabs.map(tab => {
SET_PENDING_BREADCRUMBS (state, payload) { if (tab.uid === tUid)
state.pending_breadcrumbs = payload; return { ...tab, isChanged };
return tab;
})
};
}
else
return workspace;
});
}, },
ADD_LOADED_SCHEMA (state, payload) { ADD_LOADED_SCHEMA (state, payload) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
@@ -271,7 +365,7 @@ export default {
selectWorkspace ({ commit }, uid) { selectWorkspace ({ commit }, uid) {
commit('SELECT_WORKSPACE', uid); commit('SELECT_WORKSPACE', uid);
}, },
async connectWorkspace ({ dispatch, commit }, connection) { async connectWorkspace ({ dispatch, commit, getters, rootGetters }, connection) {
commit('SET_CONNECTING', connection.uid); commit('SET_CONNECTING', connection.uid);
try { try {
@@ -304,6 +398,7 @@ export default {
if (status === 'error') if (status === 'error')
dispatch('notifications/addNotification', { status, message: version }, { root: true }); dispatch('notifications/addNotification', { status, message: version }, { root: true });
// Check if Maria or MySQL
const isMySQL = version.name.includes('MySQL'); const isMySQL = version.name.includes('MySQL');
if (isMySQL && connection.client !== 'mysql') { if (isMySQL && connection.client !== 'mysql') {
@@ -324,7 +419,8 @@ export default {
indexTypes, indexTypes,
customizations, customizations,
structure: response, structure: response,
version version,
restoreTabs: rootGetters['settings/getRestoreTabs']
}); });
dispatch('refreshCollations', connection.uid); dispatch('refreshCollations', connection.uid);
dispatch('refreshVariables', connection.uid); dispatch('refreshVariables', connection.uid);
@@ -414,7 +510,7 @@ export default {
commit('SET_DISCONNECTED', uid); commit('SET_DISCONNECTED', uid);
commit('SELECT_TAB', { uid, tab: 0 }); commit('SELECT_TAB', { uid, tab: 0 });
}, },
addWorkspace ({ commit, dispatch, getters }, uid) { addWorkspace ({ commit }, uid) {
const workspace = { const workspace = {
uid, uid,
connection_status: 'disconnected', connection_status: 'disconnected',
@@ -430,75 +526,211 @@ export default {
}; };
commit('ADD_WORKSPACE', workspace); commit('ADD_WORKSPACE', workspace);
if (getters.getWorkspace(uid).tabs.length < 3)
dispatch('newTab', { uid });
dispatch('setUnsavedChanges', false);
}, },
changeBreadcrumbs ({ state, commit, getters }, payload) { changeBreadcrumbs ({ commit, getters }, payload) {
if (state.has_unsaved_changes) {
commit('SET_UNSAVED_DISCARD_MODAL', true);
commit('SET_PENDING_BREADCRUMBS', payload);
return;
}
const breadcrumbsObj = { const breadcrumbsObj = {
schema: null, schema: null,
table: null, table: null,
trigger: null, trigger: null,
triggerFunction: null,
procedure: null, procedure: null,
function: null, function: null,
scheduler: null, scheduler: null,
view: null view: null,
query: null
}; };
const hasLastChildren = Object.keys(lastBreadcrumbs).filter(b => b !== 'schema').some(b => lastBreadcrumbs[b]);
const hasChildren = Object.keys(payload).filter(b => b !== 'schema').some(b => payload[b]);
if (lastBreadcrumbs.schema === payload.schema && hasLastChildren && !hasChildren) return;
if (lastBreadcrumbs.schema !== payload.schema)
Schema.useSchema({ uid: getters.getSelected, schema: payload.schema });
commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } }); commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } });
lastBreadcrumbs = { ...breadcrumbsObj, ...payload }; },
addLoadedSchema ({ commit, getters }, schema) {
if (payload.schema) commit('ADD_LOADED_SCHEMA', { uid: getters.getSelected, schema });
commit('ADD_LOADED_SCHEMA', { uid: getters.getSelected, schema: payload.schema });
}, },
setSearchTerm ({ commit, getters }, term) { setSearchTerm ({ commit, getters }, term) {
commit('SET_SEARCH_TERM', { uid: getters.getSelected, term }); commit('SET_SEARCH_TERM', { uid: getters.getSelected, term });
}, },
newTab ({ commit }, { uid, content, autorun }) { newTab ({ state, commit }, { uid, content, type, autorun, schema, elementName, elementType }) {
const tab = uidGen('T'); let tabUid;
const workspaceTabs = state.workspaces.find(workspace => workspace.uid === uid);
commit('NEW_TAB', { uid, tab, content, autorun }); switch (type) {
commit('SELECT_TAB', { uid, tab }); case 'temp-data': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
['temp-data', 'data'].includes(tab.type))
: false;
if (existentTab) { // if data tab exists
tabUid = existentTab.uid;
}
else {
const tempTabs = workspaceTabs ? workspaceTabs.tabs.filter(tab => tab.type === 'temp-data') : false;
if (tempTabs && tempTabs.length) { // if temp table already opened
for (const tab of tempTabs) {
commit('REPLACE_TAB', { uid, tab: tab.uid, type, schema, elementName, elementType });
tabUid = tab.uid;
}
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
}
break;
case 'data': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
['temp-data', 'data'].includes(tab.type))
: false;
if (existentTab) {
commit('REPLACE_TAB', { uid, tab: existentTab.uid, type, schema, elementName, elementType });
tabUid = existentTab.uid;
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
break;
case 'table-props': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.elementName === elementName &&
tab.elementType === elementType &&
tab.type === type)
: false;
if (existentTab) {
commit('REPLACE_TAB', { uid, tab: existentTab.uid, type, schema, elementName, elementType });
tabUid = existentTab.uid;
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
break;
case 'temp-trigger-props':
case 'temp-trigger-function-props':
case 'temp-function-props':
case 'temp-routine-props':
case 'temp-scheduler-props': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
[type, type.replace('temp-', '')].includes(tab.type))
: false;
if (existentTab) { // if tab exists
tabUid = existentTab.uid;
}
else {
const tempTabs = workspaceTabs ? workspaceTabs.tabs.filter(tab => tab.type.includes('temp-')) : false;
if (tempTabs && tempTabs.length) { // if temp tab already opened
for (const tab of tempTabs) {
if (tab.isChanged) {
commit('REPLACE_TAB', { // make permanent a temp table with unsaved changes
uid,
tab: tab.uid,
type: tab.type.replace('temp-', ''),
schema: tab.schema,
elementName: tab.elementName,
elementType: tab.elementType
});
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
else {
commit('REPLACE_TAB', { uid, tab: tab.uid, type, schema, elementName, elementType });
tabUid = tab.uid;
}
}
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
}
break;
case 'trigger-props':
case 'trigger-function-props':
case 'function-props':
case 'routine-props':
case 'scheduler-props': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
[`temp-${type}`, type].includes(tab.type))
: false;
if (existentTab) {
commit('REPLACE_TAB', { uid, tab: existentTab.uid, type, schema, elementName, elementType });
tabUid = existentTab.uid;
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
break;
default:
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
break;
}
commit('SELECT_TAB', { uid, tab: tabUid });
}, },
removeTab ({ commit }, payload) { checkSelectedTabExists ({ state, commit }, uid) {
const workspace = state.workspaces.find(workspace => workspace.uid === uid);
const isSelectedExistent = workspace
? workspace.tabs.some(tab => tab.uid === workspace.selected_tab)
: false;
if (!isSelectedExistent && workspace.tabs.length)
commit('SELECT_TAB', { uid, tab: workspace.tabs[workspace.tabs.length - 1].uid });
},
updateTabContent ({ commit }, { uid, tab, type, schema, content }) {
commit('REPLACE_TAB', { uid, tab, type, schema, content });
},
renameTabs ({ commit }, payload) {
commit('RENAME_TABS', payload);
},
removeTab ({ commit, dispatch }, payload) {
commit('REMOVE_TAB', payload); commit('REMOVE_TAB', payload);
dispatch('checkSelectedTabExists', payload.uid);
},
removeTabs ({ commit, dispatch }, payload) {
commit('REMOVE_TABS', payload);
dispatch('checkSelectedTabExists', payload.uid);
}, },
selectTab ({ commit }, payload) { selectTab ({ commit }, payload) {
commit('SELECT_TAB', payload); commit('SELECT_TAB', payload);
}, },
updateTabs ({ commit }, payload) {
commit('UPDATE_TABS', payload);
},
setTabFields ({ commit }, payload) { setTabFields ({ commit }, payload) {
commit('SET_TAB_FIELDS', payload); commit('SET_TAB_FIELDS', payload);
}, },
setTabKeyUsage ({ commit }, payload) { setTabKeyUsage ({ commit }, payload) {
commit('SET_TAB_KEY_USAGE', payload); commit('SET_TAB_KEY_USAGE', payload);
}, },
setUnsavedChanges ({ commit }, val) { setUnsavedChanges ({ commit }, payload) {
commit('SET_UNSAVED_CHANGES', val); commit('SET_UNSAVED_CHANGES', payload);
},
discardUnsavedChanges ({ state, commit, dispatch }) {
dispatch('setUnsavedChanges', false);
dispatch('changeBreadcrumbs', state.pending_breadcrumbs);
commit('SET_UNSAVED_DISCARD_MODAL', false);
commit('SET_PENDING_BREADCRUMBS', {});
},
closeUnsavedChangesModal ({ commit }) {
commit('SET_UNSAVED_DISCARD_MODAL', false);
} }
} }
}; };