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

Compare commits

..

13 Commits

31 changed files with 407 additions and 164 deletions

View File

@@ -1,52 +0,0 @@
language: node_js
node_js: 12
cache:
directories:
- node_modules
- app/node_modules
- $HOME/.cache/electron
- $HOME/.cache/electron-builder
- $HOME/.npm/_prebuilds
env:
global:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
jobs:
include:
- stage: Test
before_install:
- sudo apt-get install libsecret-1-dev
- npm install
script:
- npm test
- stage: Deploy Linux & Windows
if: tag IS present
os: linux
services: docker
before_install:
- sudo apt-get install libsecret-1-dev
- npm install
script:
- docker run --rm --env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') -v ${PWD}:/project -v ~/.cache/electron:/root/.cache/electron -v ~/.cache/electron-builder:/root/.cache/electron-builder electronuserland/builder:wine /bin/bash -c "npm run build -- --linux --win -p always"
before_cache:
- rm -rf $HOME/.cache/electron-builder/wine
- stage: Deploy Mac
if: tag IS present
os: osx
before_install:
- npm install
osx_image: xcode10.2
script:
- npm run build -- -p always
# - stage: Deploy ARM Linux
# if: tag IS present
# os: linux
# arch: arm64
# script:
# - npm run build -- --linux AppImage -p always

View File

@@ -2,6 +2,28 @@
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.1.4](https://github.com/Fabio286/antares/compare/v0.1.3...v0.1.4) (2021-04-22)
### Features
* query results export ([1d363f7](https://github.com/Fabio286/antares/commit/1d363f755e025d0fc6fec61cbd47ff87a8f25728))
* **UI:** canc press to delete selected rows ([20cba6e](https://github.com/Fabio286/antares/commit/20cba6ee9bc1daa902b04d6e2ddcb31d04fbf805))
* **UI:** ctrl+s shortcut to save changes ([16e17b3](https://github.com/Fabio286/antares/commit/16e17b39b6c8b561cc018d02afee2276190ce304))
* **UI:** format and clear queries ([9ffd443](https://github.com/Fabio286/antares/commit/9ffd443a66303f88fc4529896f6d1d7917454f7a))
### Bug Fixes
* launch from shortcut of procedures or functions with parameters without name dont works ([f82dbd2](https://github.com/Fabio286/antares/commit/f82dbd24dcef7b4d8d127a604e256b3f79a6c617))
* wrong changelog in some cases ([a41cf1a](https://github.com/Fabio286/antares/commit/a41cf1ab5662f5f5fdedff4a9e1c626c23071377))
* **UI:** data type not listed in selection if not present in global types ([6eb2977](https://github.com/Fabio286/antares/commit/6eb2977568987b9440b62ae7dbd7183338bfcc9b))
### Improvements
* **UI:** improved connection status indicator ([5ceddb8](https://github.com/Fabio286/antares/commit/5ceddb8e00f3bc1984b8e47de270dc39b367903f))
### [0.1.3](https://github.com/Fabio286/antares/compare/v0.1.2...v0.1.3) (2021-04-17) ### [0.1.3](https://github.com/Fabio286/antares/compare/v0.1.2...v0.1.3) (2021-04-17)

View File

@@ -4,7 +4,7 @@
# Antares SQL Client # Antares SQL Client
![GitHub package.json version](https://img.shields.io/github/package-json/v/fabio286/antares) [![Build Status](https://travis-ci.com/Fabio286/antares.svg?branch=master)](https://travis-ci.com/Fabio286/antares) ![GitHub All Releases](https://img.shields.io/github/downloads/fabio286/antares/total) ![GitHub](https://img.shields.io/github/license/fabio286/antares) [![antares](https://snapcraft.io/antares/badge.svg)](https://snapcraft.io/antares) [![antares](https://snapcraft.io/antares/trending.svg?name=0)](https://snapcraft.io/antares) ![GitHub package.json version](https://img.shields.io/github/package-json/v/fabio286/antares) ![GitHub All Releases](https://img.shields.io/github/downloads/fabio286/antares/total) ![GitHub](https://img.shields.io/github/license/fabio286/antares) [![antares](https://snapcraft.io/antares/badge.svg)](https://snapcraft.io/antares) [![antares](https://snapcraft.io/antares/trending.svg?name=0)](https://snapcraft.io/antares) [![Plant a Tree](https://raw.githubusercontent.com/Fabio286/treedom-badge/master/svg/plant-a-tree.svg)](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers. Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers.
My target is to support as many databases as possible, and all major operating systems, including the ARM versions. My target is to support as many databases as possible, and all major operating systems, including the ARM versions.

View File

@@ -1,7 +1,7 @@
{ {
"name": "antares", "name": "antares",
"productName": "Antares", "productName": "Antares",
"version": "0.1.3", "version": "0.1.4",
"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",
@@ -73,7 +73,6 @@
"electron-store": "^7.0.0", "electron-store": "^7.0.0",
"electron-updater": "^4.3.5", "electron-updater": "^4.3.5",
"faker": "^5.3.1", "faker": "^5.3.1",
"keytar": "^7.3.0",
"marked": "^2.0.2", "marked": "^2.0.2",
"moment": "^2.29.1", "moment": "^2.29.1",
"mssql": "^6.2.3", "mssql": "^6.2.3",
@@ -83,6 +82,7 @@
"pgsql-ast-parser": "^7.0.2", "pgsql-ast-parser": "^7.0.2",
"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",
"v-mask": "^2.2.4", "v-mask": "^2.2.4",
"vue-i18n": "^8.22.4", "vue-i18n": "^8.22.4",
"vuedraggable": "^2.24.3", "vuedraggable": "^2.24.3",
@@ -105,7 +105,7 @@
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
"sass-loader": "^10.1.1", "sass-loader": "^10.1.1",
"standard-version": "^9.2.0", "standard-version": "^9.2.0",
"stylelint": "^13.9.0", "stylelint": "^13.12.0",
"stylelint-config-standard": "^21.0.0", "stylelint-config-standard": "^21.0.0",
"stylelint-scss": "^3.19.0", "stylelint-scss": "^3.19.0",
"vue": "^2.6.12", "vue": "^2.6.12",

View File

@@ -16,6 +16,7 @@ module.exports = {
tables: false, tables: false,
views: false, views: false,
triggers: false, triggers: false,
triggerFunctions: false,
routines: false, routines: false,
functions: false, functions: false,
schedulers: false, schedulers: false,
@@ -23,6 +24,7 @@ module.exports = {
tableAdd: false, tableAdd: false,
viewAdd: false, viewAdd: false,
triggerAdd: false, triggerAdd: false,
triggerFunctionAdd: false,
routineAdd: false, routineAdd: false,
functionAdd: false, functionAdd: false,
schedulerAdd: false, schedulerAdd: false,
@@ -31,6 +33,7 @@ module.exports = {
tableSettings: false, tableSettings: false,
viewSettings: false, viewSettings: false,
triggerSettings: false, triggerSettings: false,
triggerFunctionSettings: false,
routineSettings: false, routineSettings: false,
functionSettings: false, functionSettings: false,
schedulerSettings: false, schedulerSettings: false,
@@ -59,6 +62,8 @@ module.exports = {
functionSql: false, functionSql: false,
functionContext: false, functionContext: false,
functionLanguage: false, functionLanguage: false,
triggerMiltipleEvents: false,
triggerUpdateColumns: false,
parametersLength: false, parametersLength: false,
languages: false languages: false
}; };

View File

@@ -2,9 +2,7 @@
import { app, BrowserWindow, nativeImage } from 'electron'; import { app, BrowserWindow, nativeImage } from 'electron';
import * as path from 'path'; import * as path from 'path';
import crypto from 'crypto';
import { format as formatUrl } from 'url'; import { format as formatUrl } from 'url';
import keytar from 'keytar';
import Store from 'electron-store'; import Store from 'electron-store';
import ipcHandlers from './ipc-handlers'; import ipcHandlers from './ipc-handlers';
@@ -96,18 +94,6 @@ else {
// create main BrowserWindow when electron is ready // create main BrowserWindow when electron is ready
app.on('ready', async () => { app.on('ready', async () => {
try {
let key = await keytar.getPassword('antares', 'user');
if (!key) {
key = crypto.randomBytes(16).toString('hex');
keytar.setPassword('antares', 'user', key);
}
}
catch (err) {
console.log(err);
}
mainWindow = createMainWindow(); mainWindow = createMainWindow();
}); });
} }

View File

@@ -1,4 +1,3 @@
import keytar from 'keytar';
import { app, ipcMain } from 'electron'; import { app, ipcMain } from 'electron';
export default () => { export default () => {
@@ -7,14 +6,7 @@ export default () => {
}); });
ipcMain.on('get-key', async event => { ipcMain.on('get-key', async event => {
let key = false; const key = false;
try {
key = await keytar.getPassword('antares', 'user');
}
catch (err) {
console.log(err);
}
event.returnValue = key; event.returnValue = key;
}); });
}; };

View File

@@ -77,6 +77,7 @@ export class PostgreSQLClient extends AntaresCore {
*/ */
use (schema) { use (schema) {
this._schema = schema; this._schema = schema;
if (schema)
return this.raw(`SET search_path TO ${schema}`); return this.raw(`SET search_path TO ${schema}`);
} }
@@ -186,12 +187,14 @@ export class PostgreSQLClient extends AntaresCore {
// TRIGGERS // TRIGGERS
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.database).map(trigger => { const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.database).map(trigger => {
return { return {
name: trigger.trigger_name, name: `${trigger.table_name}.${trigger.trigger_name}`,
orgName: trigger.trigger_name,
timing: trigger.activation, timing: trigger.activation,
definer: trigger.definition, // ??? definer: '',
definition: trigger.definition,
event: trigger.event, event: trigger.event,
table: trigger.table_trigger, table: trigger.table_name,
sqlMode: trigger.sql_mode sqlMode: ''
}; };
}); });
@@ -274,7 +277,7 @@ export class PostgreSQLClient extends AntaresCore {
*/ */
async getTableIndexes ({ schema, table }) { async getTableIndexes ({ schema, table }) {
if (schema !== 'public') if (schema !== 'public')
this.use(schema); await this.use(schema);
const { rows } = await this.raw(`WITH ndx_list AS ( const { rows } = await this.raw(`WITH ndx_list AS (
SELECT pg_index.indexrelid, pg_class.oid SELECT pg_index.indexrelid, pg_class.oid
@@ -515,7 +518,8 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropTrigger (params) { async dropTrigger (params) {
const sql = `DROP TRIGGER \`${params.trigger}\``; const triggerParts = params.trigger.split('.');
const sql = `DROP TRIGGER ${triggerParts[1]} ON ${triggerParts[0]}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -670,7 +674,7 @@ export class PostgreSQLClient extends AntaresCore {
: ''; : '';
if (this._schema !== 'public') if (this._schema !== 'public')
this.use(this._schema); await this.use(this._schema);
const sql = `CREATE PROCEDURE ${this._schema}.${routine.name}(${parameters}) const sql = `CREATE PROCEDURE ${this._schema}.${routine.name}(${parameters})
LANGUAGE ${routine.language} LANGUAGE ${routine.language}
@@ -799,7 +803,7 @@ export class PostgreSQLClient extends AntaresCore {
: ''; : '';
if (this._schema !== 'public') if (this._schema !== 'public')
this.use(this._schema); await this.use(this._schema);
const body = func.returns ? func.sql : '$BODY$\n$BODY$'; const body = func.returns ? func.sql : '$BODY$\n$BODY$';
@@ -1018,7 +1022,7 @@ export class PostgreSQLClient extends AntaresCore {
} = params; } = params;
if (this._schema !== 'public') if (this._schema !== 'public')
this.use(this._schema); await this.use(this._schema);
let sql = ''; let sql = '';
const alterColumns = []; const alterColumns = [];
@@ -1247,7 +1251,7 @@ export class PostgreSQLClient extends AntaresCore {
}; };
if (args.nest && this._schema !== 'public') if (args.nest && this._schema !== 'public')
this.use(this._schema); await this.use(this._schema);
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];

View File

@@ -26,7 +26,7 @@
<div class="input-group"> <div class="input-group">
<input <input
:ref="i === 0 ? 'firstInput' : ''" :ref="i === 0 ? 'firstInput' : ''"
v-model="values[parameter.name]" v-model="values[`${i}-${parameter.name}`]"
class="form-input" class="form-input"
type="text" type="text"
> >
@@ -88,7 +88,7 @@ export default {
return ''; return '';
}, },
runRoutine () { runRoutine () {
const valArr = Object.keys(this.values).reduce((acc, curr) => { const valArr = Object.keys(this.values).reduce((acc, curr, i) => {
let qc; let qc;
switch (this.client) { switch (this.client) {
case 'maria': case 'maria':
@@ -102,7 +102,7 @@ export default {
qc = '"'; qc = '"';
} }
const param = this.localRoutine.parameters.find(param => param.name === curr); const param = this.localRoutine.parameters.find(param => `${i}-${param.name}` === curr);
const value = [...NUMBER, ...FLOAT].includes(param.type) ? this.values[curr] : `${qc}${this.values[curr]}${qc}`; const value = [...NUMBER, ...FLOAT].includes(param.type) ? this.values[curr] : `${qc}${this.values[curr]}${qc}`;
acc.push(value); acc.push(value);

View File

@@ -15,6 +15,7 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex';
import marked from 'marked'; import marked from 'marked';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
@@ -31,13 +32,16 @@ export default {
isError: false isError: false
}; };
}, },
computed: {
...mapGetters({ appVersion: 'application/appVersion' })
},
created () { created () {
this.getChangelog(); this.getChangelog();
}, },
methods: { methods: {
async getChangelog () { async getChangelog () {
try { try {
const apiRes = await fetch('https://api.github.com/repos/Fabio286/antares/releases/latest', { const apiRes = await fetch(`https://api.github.com/repos/Fabio286/antares/releases/tags/v${this.appVersion}`, {
method: 'GET' method: 'GET'
}); });

View File

@@ -19,7 +19,7 @@
@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} ${connected.includes(connection.uid) ? 'badge' : ''}`" /> <i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" />
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span> <span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
</li> </li>
</draggable> </draggable>
@@ -73,7 +73,7 @@ export default {
...mapGetters({ ...mapGetters({
getConnections: 'connections/getConnections', getConnections: 'connections/getConnections',
getConnectionName: 'connections/getConnectionName', getConnectionName: 'connections/getConnectionName',
connected: 'workspaces/getConnected', getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected', selectedWorkspace: 'workspaces/getSelected',
updateStatus: 'application/getUpdateStatus' updateStatus: 'application/getUpdateStatus'
}), }),
@@ -109,6 +109,22 @@ export default {
const el = e.target; const el = e.target;
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`;
},
getStatusBadge (uid) {
if (this.getWorkspace(uid)) {
const status = this.getWorkspace(uid).connection_status;
switch (status) {
case 'connected':
return 'badge badge-connected';
case 'connecting':
return 'badge badge-connecting';
case 'failed':
return 'badge badge-failed';
default:
return '';
}
}
} }
} }
}; };

View File

@@ -1,7 +1,7 @@
<template> <template>
<div v-show="isSelected" class="workspace column columns col-gapless"> <div v-show="isSelected" class="workspace column columns col-gapless">
<WorkspaceExploreBar :connection="connection" :is-selected="isSelected" /> <WorkspaceExploreBar :connection="connection" :is-selected="isSelected" />
<div v-if="workspace.connected" class="workspace-tabs column columns col-gapless"> <div v-if="workspace.connection_status === 'connected'" class="workspace-tabs column columns col-gapless">
<ul <ul
id="tabWrap" id="tabWrap"
ref="tabWrap" ref="tabWrap"

View File

@@ -8,7 +8,7 @@
> >
<div class="workspace-explorebar-header"> <div class="workspace-explorebar-header">
<span class="workspace-explorebar-title">{{ connectionName }}</span> <span class="workspace-explorebar-title">{{ connectionName }}</span>
<span v-if="workspace.connected" class="workspace-explorebar-tools"> <span v-if="workspace.connection_status === 'connected'" class="workspace-explorebar-tools">
<i <i
class="mdi mdi-18px mdi-database-plus c-hand mr-2" class="mdi mdi-18px mdi-database-plus c-hand mr-2"
:title="$t('message.createNewSchema')" :title="$t('message.createNewSchema')"
@@ -28,7 +28,7 @@
</span> </span>
</div> </div>
<div class="workspace-explorebar-search"> <div class="workspace-explorebar-search">
<div v-if="workspace.connected" class="has-icon-right"> <div v-if="workspace.connection_status === 'connected'" class="has-icon-right">
<input <input
v-model="searchTerm" v-model="searchTerm"
class="form-input input-sm" class="form-input input-sm"
@@ -39,7 +39,7 @@
</div> </div>
</div> </div>
<WorkspaceConnectPanel <WorkspaceConnectPanel
v-if="!workspace.connected" v-if="workspace.connection_status !== 'connected'"
class="workspace-explorebar-body" class="workspace-explorebar-body"
:connection="connection" :connection="connection"
/> />

View File

@@ -80,6 +80,9 @@
<option v-if="localOptions.returns === 'VOID'"> <option v-if="localOptions.returns === 'VOID'">
VOID VOID
</option> </option>
<option v-if="!isInDataTypes">
{{ localOptions.returns }}
</option>
<optgroup <optgroup
v-for="group in workspace.dataTypes" v-for="group in workspace.dataTypes"
:key="group.group" :key="group.group"
@@ -178,6 +181,16 @@ export default {
}, },
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
},
isInDataTypes () {
let typeNames = [];
for (const group of this.workspace.dataTypes) {
typeNames = group.types.reduce((acc, curr) => {
acc.push(curr.name);
return acc;
}, []);
}
return typeNames.includes(this.localOptions.returns);
} }
}, },
created () { created () {

View File

@@ -7,6 +7,7 @@
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
:disabled="!isChanged" :disabled="!isChanged"
:class="{'loading':isSaving}" :class="{'loading':isSaving}"
title="CTRL+S"
@click="saveChanges" @click="saveChanges"
> >
<span>{{ $t('word.save') }}</span> <span>{{ $t('word.save') }}</span>
@@ -150,6 +151,7 @@ export default {
computed: { computed: {
...mapGetters({ ...mapGetters({
getWorkspace: 'workspaces/getWorkspace', getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected',
getDatabaseVariable: 'workspaces/getDatabaseVariable' getDatabaseVariable: 'workspaces/getDatabaseVariable'
}), }),
workspace () { workspace () {
@@ -163,7 +165,7 @@ export default {
return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || ''; return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || '';
}, },
isSelected () { isSelected () {
return this.workspace.selected_tab === 'prop'; return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.table;
}, },
schema () { schema () {
return this.workspace.breadcrumbs.schema; return this.workspace.breadcrumbs.schema;
@@ -200,6 +202,12 @@ export default {
this.setUnsavedChanges(val); this.setUnsavedChanges(val);
} }
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
@@ -519,6 +527,15 @@ export default {
}, },
foreignsUpdate (foreigns) { foreignsUpdate (foreigns) {
this.localKeyUsage = foreigns; this.localKeyUsage = foreigns;
},
onKey (e) {
if (this.isSelected) {
e.stopPropagation();
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
if (this.isChanged)
this.saveChanges();
}
}
} }
} }
}; };

View File

@@ -7,6 +7,7 @@
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
:disabled="!isChanged" :disabled="!isChanged"
:class="{'loading':isSaving}" :class="{'loading':isSaving}"
title="CTRL+S"
@click="saveChanges" @click="saveChanges"
> >
<span>{{ $t('word.save') }}</span> <span>{{ $t('word.save') }}</span>
@@ -120,13 +121,14 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace' getWorkspace: 'workspaces/getWorkspace'
}), }),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { isSelected () {
return this.workspace.selected_tab === 'prop'; return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function;
}, },
schema () { schema () {
return this.workspace.breadcrumbs.schema; return this.workspace.breadcrumbs.schema;
@@ -171,6 +173,12 @@ export default {
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
@@ -312,6 +320,15 @@ export default {
}, },
hideAskParamsModal () { hideAskParamsModal () {
this.isAskingParameters = false; this.isAskingParameters = false;
},
onKey (e) {
if (this.isSelected) {
e.stopPropagation();
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
if (this.isChanged)
this.saveChanges();
}
}
} }
} }
}; };

View File

@@ -7,6 +7,7 @@
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
:disabled="!isChanged" :disabled="!isChanged"
:class="{'loading':isSaving}" :class="{'loading':isSaving}"
title="CTRL+S"
@click="saveChanges" @click="saveChanges"
> >
<span>{{ $t('word.save') }}</span> <span>{{ $t('word.save') }}</span>
@@ -121,13 +122,14 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace' getWorkspace: 'workspaces/getWorkspace'
}), }),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { isSelected () {
return this.workspace.selected_tab === 'prop'; return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.routine;
}, },
schema () { schema () {
return this.workspace.breadcrumbs.schema; return this.workspace.breadcrumbs.schema;
@@ -172,6 +174,12 @@ export default {
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
@@ -310,6 +318,15 @@ export default {
}, },
hideAskParamsModal () { hideAskParamsModal () {
this.isAskingParameters = false; this.isAskingParameters = false;
},
onKey (e) {
if (this.isSelected) {
e.stopPropagation();
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
if (this.isChanged)
this.saveChanges();
}
}
} }
} }
}; };

View File

@@ -7,6 +7,7 @@
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
:disabled="!isChanged" :disabled="!isChanged"
:class="{'loading':isSaving}" :class="{'loading':isSaving}"
title="CTRL+S"
@click="saveChanges" @click="saveChanges"
> >
<span>{{ $t('word.save') }}</span> <span>{{ $t('word.save') }}</span>
@@ -169,13 +170,14 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace' getWorkspace: 'workspaces/getWorkspace'
}), }),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { isSelected () {
return this.workspace.selected_tab === 'prop'; return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.scheduler;
}, },
schema () { schema () {
return this.workspace.breadcrumbs.schema; return this.workspace.breadcrumbs.schema;
@@ -220,6 +222,12 @@ export default {
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
@@ -310,6 +318,15 @@ export default {
}, },
timingUpdate (options) { timingUpdate (options) {
this.localScheduler = options; this.localScheduler = options;
},
onKey (e) {
if (this.isSelected) {
e.stopPropagation();
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
if (this.isChanged)
this.saveChanges();
}
}
} }
} }
}; };

View File

@@ -7,6 +7,7 @@
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
:disabled="!isChanged" :disabled="!isChanged"
:class="{'loading':isSaving}" :class="{'loading':isSaving}"
title="CTRL+S"
@click="saveChanges" @click="saveChanges"
> >
<span>{{ $t('word.save') }}</span> <span>{{ $t('word.save') }}</span>
@@ -140,13 +141,14 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace' getWorkspace: 'workspaces/getWorkspace'
}), }),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { isSelected () {
return this.workspace.selected_tab === 'prop'; return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger;
}, },
schema () { schema () {
return this.workspace.breadcrumbs.schema; return this.workspace.breadcrumbs.schema;
@@ -191,6 +193,12 @@ export default {
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
@@ -274,6 +282,15 @@ export default {
this.editorHeight = size; this.editorHeight = size;
this.$refs.queryEditor.editor.resize(); this.$refs.queryEditor.editor.resize();
} }
},
onKey (e) {
if (this.isSelected) {
e.stopPropagation();
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
if (this.isChanged)
this.saveChanges();
}
}
} }
} }
}; };

View File

@@ -7,6 +7,7 @@
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
:disabled="!isChanged" :disabled="!isChanged"
:class="{'loading':isSaving}" :class="{'loading':isSaving}"
title="CTRL+S"
@click="saveChanges" @click="saveChanges"
> >
<span>{{ $t('word.save') }}</span> <span>{{ $t('word.save') }}</span>
@@ -201,13 +202,14 @@ export default {
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace' getWorkspace: 'workspaces/getWorkspace'
}), }),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { isSelected () {
return this.workspace.selected_tab === 'prop'; return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.view;
}, },
schema () { schema () {
return this.workspace.breadcrumbs.schema; return this.workspace.breadcrumbs.schema;
@@ -245,6 +247,12 @@ export default {
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
@@ -327,6 +335,15 @@ export default {
this.editorHeight = size; this.editorHeight = size;
this.$refs.queryEditor.editor.resize(); this.$refs.queryEditor.editor.resize();
} }
},
onKey (e) {
if (this.isSelected) {
e.stopPropagation();
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
if (this.isChanged)
this.saveChanges();
}
}
} }
} }
}; };

View File

@@ -60,6 +60,9 @@
class="form-select editable-field small-select text-uppercase" class="form-select editable-field small-select text-uppercase"
@blur="editOFF" @blur="editOFF"
> >
<option v-if="!isInDataTypes">
{{ row.type }}
</option>
<optgroup <optgroup
v-for="group in dataTypes" v-for="group in dataTypes"
:key="group.group" :key="group.group"
@@ -68,7 +71,7 @@
<option <option
v-for="type in group.types" v-for="type in group.types"
:key="type.name" :key="type.name"
:selected="localRow.type.toUpperCase() === type.name" :selected="localRow.type === type.name"
:value="type.name" :value="type.name"
> >
{{ type.name }} {{ type.name }}
@@ -374,6 +377,16 @@ export default {
}, },
isNullable () { isNullable () {
return !this.indexes.some(index => ['PRIMARY'].includes(index.type)); return !this.indexes.some(index => ['PRIMARY'].includes(index.type));
},
isInDataTypes () {
let typeNames = [];
for (const group of this.dataTypes) {
typeNames = group.types.reduce((acc, curr) => {
acc.push(curr.name);
return acc;
}, []);
}
return typeNames.includes(this.row.type);
} }
}, },
watch: { watch: {

View File

@@ -1,5 +1,12 @@
<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"
tabindex="0"
@keydown.116="runQuery(query)"
@keydown.ctrl.87="clear"
@keydown.ctrl.119="beautify"
>
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
@@ -24,6 +31,43 @@
<span>{{ $t('word.run') }}</span> <span>{{ $t('word.run') }}</span>
<i class="mdi mdi-24px mdi-play" /> <i class="mdi mdi-24px mdi-play" />
</button> </button>
<div class="dropdown export-dropdown pr-2">
<button
:disabled="!results.length || isQuering"
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
tabindex="0"
>
<span>{{ $t('word.export') }}</span>
<i class="mdi mdi-24px mdi-file-export ml-1" />
<i class="mdi mdi-24px mdi-menu-down" />
</button>
<ul class="menu text-left">
<li class="menu-item">
<a class="c-hand" @click="downloadTable('json')">JSON</a>
</li>
<li class="menu-item">
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
</li>
</ul>
</div>
<button
class="btn btn-dark btn-sm"
:disabled="!query || isQuering"
title="CTRL+F8"
@click="beautify()"
>
<span>{{ $t('word.format') }}</span>
<i class="mdi mdi-24px mdi-brush pl-1" />
</button>
<button
class="btn btn-link btn-sm"
:disabled="!query || isQuering"
title="CTRL+W"
@click="clear()"
>
<span>{{ $t('word.clear') }}</span>
<i class="mdi mdi-24px mdi-delete-sweep pl-1" />
</button>
</div> </div>
<div class="workspace-query-info"> <div class="workspace-query-info">
<div <div
@@ -68,6 +112,7 @@
</template> </template>
<script> <script>
import { format } from 'sql-formatter';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
@@ -192,12 +237,36 @@ export default {
if (this.$refs.queryEditor) if (this.$refs.queryEditor)
this.$refs.queryEditor.editor.resize(); this.$refs.queryEditor.editor.resize();
}, },
onKey (e) { beautify () {
if (this.isSelected && this.isWorkspaceSelected) { if (this.$refs.queryEditor) {
e.stopPropagation(); let language = 'sql';
if (e.key === 'F5')
this.runQuery(this.query); switch (this.workspace.client) {
case 'mysql':
language = 'mysql';
break;
case 'maria':
language = 'mariadb';
break;
case 'pg':
language = 'postgresql';
break;
} }
const formattedQuery = format(this.query, {
language,
uppercase: true
});
this.$refs.queryEditor.editor.session.setValue(formattedQuery);
}
},
clear () {
if (this.$refs.queryEditor)
this.$refs.queryEditor.editor.session.setValue('');
this.clearTabData();
},
downloadTable (format) {
this.$refs.queryTable.downloadTable(format, `${this.tab.type}-${this.tab.index}`);
} }
} }
}; };
@@ -251,4 +320,9 @@ export default {
} }
} }
.export-dropdown {
.menu {
min-width: 100%;
}
}
</style> </style>

View File

@@ -1,16 +1,18 @@
<template> <template>
<div <div
ref="tableWrapper" ref="tableWrapper"
class="vscroll" class="vscroll no-outline"
tabindex="0"
:style="{'height': resultsSize+'px'}" :style="{'height': resultsSize+'px'}"
@keyup.46="showDeleteConfirmModal"
> >
<TableContext <TableContext
v-if="isContext" v-if="isContext"
:context-event="contextEvent" :context-event="contextEvent"
:selected-rows="selectedRows" :selected-rows="selectedRows"
@delete-selected="deleteSelected" @show-delete-modal="showDeleteConfirmModal"
@set-null="setNull" @set-null="setNull"
@close-context="isContext = false" @close-context="closeContext"
/> />
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs"> <ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
<li <li
@@ -75,6 +77,23 @@
</template> </template>
</BaseVirtualScroll> </BaseVirtualScroll>
</div> </div>
<ConfirmModal
v-if="isDeleteConfirmModal"
@confirm="deleteSelected"
@hide="hideDeleteConfirmModal"
>
<template :slot="'header'">
<div class="d-flex">
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
</div>
</template>
<div :slot="'body'">
<div class="mb-2">
{{ $tc('message.confirmToDeleteRows', selectedRows.length) }}
</div>
</div>
</ConfirmModal>
</div> </div>
</template> </template>
@@ -85,6 +104,7 @@ import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
import BaseVirtualScroll from '@/components/BaseVirtualScroll'; import BaseVirtualScroll from '@/components/BaseVirtualScroll';
import WorkspaceQueryTableRow from '@/components/WorkspaceQueryTableRow'; import WorkspaceQueryTableRow from '@/components/WorkspaceQueryTableRow';
import TableContext from '@/components/WorkspaceQueryTableContext'; import TableContext from '@/components/WorkspaceQueryTableContext';
import ConfirmModal from '@/components/BaseConfirmModal';
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import moment from 'moment'; import moment from 'moment';
@@ -93,7 +113,8 @@ export default {
components: { components: {
BaseVirtualScroll, BaseVirtualScroll,
WorkspaceQueryTableRow, WorkspaceQueryTableRow,
TableContext TableContext,
ConfirmModal
}, },
props: { props: {
results: Array, results: Array,
@@ -106,6 +127,7 @@ export default {
resultsSize: 1000, resultsSize: 1000,
localResults: [], localResults: [],
isContext: false, isContext: false,
isDeleteConfirmModal: false,
contextEvent: null, contextEvent: null,
selectedCell: null, selectedCell: null,
selectedRows: [], selectedRows: [],
@@ -322,7 +344,17 @@ export default {
}; };
this.$emit('update-field', params); this.$emit('update-field', params);
}, },
closeContext () {
this.isContext = false;
},
showDeleteConfirmModal () {
this.isDeleteConfirmModal = true;
},
hideDeleteConfirmModal () {
this.isDeleteConfirmModal = false;
},
deleteSelected () { deleteSelected () {
this.closeContext();
const rows = this.localResults.filter(row => this.selectedRows.includes(row._id)).map(row => { const rows = this.localResults.filter(row => this.selectedRows.includes(row._id)).map(row => {
delete row._id; delete row._id;
return row; return row;

View File

@@ -17,61 +17,30 @@
<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>
</div> </div>
<ConfirmModal
v-if="isConfirmModal"
@confirm="deleteRows"
@hide="hideConfirmModal"
>
<template :slot="'header'">
<div class="d-flex">
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
</div>
</template>
<div :slot="'body'">
<div class="mb-2">
{{ $tc('message.confirmToDeleteRows', selectedRows.length) }}
</div>
</div>
</ConfirmModal>
</BaseContextMenu> </BaseContextMenu>
</template> </template>
<script> <script>
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu';
import ConfirmModal from '@/components/BaseConfirmModal';
export default { export default {
name: 'WorkspaceQueryTableContext', name: 'WorkspaceQueryTableContext',
components: { components: {
BaseContextMenu, BaseContextMenu
ConfirmModal
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedRows: Array selectedRows: Array
}, },
data () {
return {
isConfirmModal: false
};
},
computed: { computed: {
}, },
methods: { methods: {
showConfirmModal () { showConfirmModal () {
this.isConfirmModal = true; this.$emit('show-delete-modal');
},
hideConfirmModal () {
this.isConfirmModal = false;
}, },
closeContext () { closeContext () {
this.$emit('close-context'); this.$emit('close-context');
}, },
deleteRows () {
this.$emit('delete-selected');
this.closeContext();
},
setNull () { setNull () {
this.$emit('set-null'); this.$emit('set-null');
this.closeContext(); this.closeContext();

View File

@@ -104,7 +104,8 @@ module.exports = {
database: 'Database', database: 'Database',
scratchpad: 'Scratchpad', scratchpad: 'Scratchpad',
array: 'Array', array: 'Array',
changelog: 'Changelog' changelog: 'Changelog',
format: 'Format'
}, },
message: { message: {
appWelcome: 'Welcome to Antares SQL Client!', appWelcome: 'Welcome to Antares SQL Client!',

View File

@@ -30,6 +30,12 @@
animation: jump-down-in 0.2s reverse; animation: jump-down-in 0.2s reverse;
} }
.pulse {
animation-name: pulse;
animation-duration: 2s;
animation-iteration-count: infinite;
}
@keyframes jump-down-in { @keyframes jump-down-in {
0% { 0% {
transform: scale(0); transform: scale(0);
@@ -39,3 +45,17 @@
transform: scale(1); transform: scale(1);
} }
} }
@keyframes pulse {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@@ -30,6 +30,10 @@ body {
cursor: help; cursor: help;
} }
.no-outline {
outline: none !important;
}
.no-border { .no-border {
outline: none !important; outline: none !important;
border: none !important; border: none !important;
@@ -132,6 +136,21 @@ body {
box-shadow: none; box-shadow: none;
} }
} }
&.badge-connected::after {
background: $success-color;
}
&.badge-connecting::after {
background: $warning-color;
animation-name: pulse;
animation-duration: 2s;
animation-iteration-count: infinite;
}
&.badge-failed::after {
background: $error-color;
}
} }
.form-select { .form-select {

View File

@@ -386,7 +386,6 @@
bottom: -10px; bottom: -10px;
right: 0; right: 0;
position: absolute; position: absolute;
background: $success-color;
} }
&.badge-update::after { &.badge-update::after {

View File

@@ -168,7 +168,6 @@
bottom: -10px; bottom: -10px;
right: 0; right: 0;
position: absolute; position: absolute;
background: $success-color;
} }
&.badge-update::after { &.badge-update::after {

View File

@@ -1,8 +1,7 @@
'use strict'; 'use strict';
import Store from 'electron-store'; import Store from 'electron-store';
import crypto from 'crypto'; import crypto from 'crypto';
import Application from '../../ipc-api/Application'; const key = localStorage.getItem('key');
const key = Application.getKey() || localStorage.getItem('key');
if (!key) if (!key)
localStorage.setItem('key', crypto.randomBytes(16).toString('hex')); localStorage.setItem('key', crypto.randomBytes(16).toString('hex'));

View File

@@ -37,7 +37,7 @@ export default {
}, },
getConnected: state => { getConnected: state => {
return state.workspaces return state.workspaces
.filter(workspace => workspace.connected) .filter(workspace => workspace.connection_status === 'connected')
.map(workspace => workspace.uid); .map(workspace => workspace.uid);
}, },
getLoadedSchemas: state => uid => { getLoadedSchemas: state => uid => {
@@ -54,7 +54,7 @@ export default {
SELECT_WORKSPACE (state, uid) { SELECT_WORKSPACE (state, uid) {
state.selected_workspace = uid; state.selected_workspace = uid;
}, },
ADD_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;
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
@@ -65,19 +65,41 @@ export default {
indexTypes, indexTypes,
customizations, customizations,
structure, structure,
connected: true, connection_status: 'connected',
version version
} }
: workspace); : workspace);
}, },
REMOVE_CONNECTED (state, uid) { SET_CONNECTING (state, uid) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
? { ? {
...workspace, ...workspace,
structure: {}, structure: {},
breadcrumbs: {}, breadcrumbs: {},
loaded_schemas: new Set(), loaded_schemas: new Set(),
connected: false connection_status: 'connecting'
}
: workspace);
},
SET_FAILED (state, uid) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
? {
...workspace,
structure: {},
breadcrumbs: {},
loaded_schemas: new Set(),
connection_status: 'failed'
}
: workspace);
},
SET_DISCONNECTED (state, uid) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
? {
...workspace,
structure: {},
breadcrumbs: {},
loaded_schemas: new Set(),
connection_status: 'disconnected'
} }
: workspace); : workspace);
}, },
@@ -247,10 +269,14 @@ export default {
commit('SELECT_WORKSPACE', uid); commit('SELECT_WORKSPACE', uid);
}, },
async connectWorkspace ({ dispatch, commit }, connection) { async connectWorkspace ({ dispatch, commit }, connection) {
commit('SET_CONNECTING', connection.uid);
try { try {
const { status, response } = await Connection.connect(connection); const { status, response } = await Connection.connect(connection);
if (status === 'error') if (status === 'error') {
dispatch('notifications/addNotification', { status, message: response }, { root: true }); dispatch('notifications/addNotification', { status, message: response }, { root: true });
commit('SET_FAILED', connection.uid);
}
else { else {
let dataTypes = []; let dataTypes = [];
let indexTypes = []; let indexTypes = [];
@@ -288,7 +314,7 @@ export default {
dispatch('connections/editConnection', connProxy, { root: true }); dispatch('connections/editConnection', connProxy, { root: true });
} }
commit('ADD_CONNECTED', { commit('SET_CONNECTED', {
uid: connection.uid, uid: connection.uid,
client: connection.client, client: connection.client,
dataTypes, dataTypes,
@@ -382,13 +408,13 @@ export default {
}, },
removeConnected ({ commit }, uid) { removeConnected ({ commit }, uid) {
Connection.disconnect(uid); Connection.disconnect(uid);
commit('REMOVE_CONNECTED', uid); commit('SET_DISCONNECTED', uid);
commit('SELECT_TAB', { uid, tab: 0 }); commit('SELECT_TAB', { uid, tab: 0 });
}, },
addWorkspace ({ commit, dispatch, getters }, uid) { addWorkspace ({ commit, dispatch, getters }, uid) {
const workspace = { const workspace = {
uid, uid,
connected: false, connection_status: 'disconnected',
selected_tab: 0, selected_tab: 0,
search_term: '', search_term: '',
tabs: [], tabs: [],