From aea94f0325040fcd1f7a4b32692e2ba92a4b516f Mon Sep 17 00:00:00 2001 From: Fabio <fabio286@gmail.com> Date: Thu, 14 May 2020 15:21:57 +0200 Subject: [PATCH] Additions --- .gitignore | 3 +- src/main/index.js | 10 ++-- src/main/ipc-api/connection.js | 24 -------- src/main/ipc-handlers/connection.js | 50 ++++++++++++++++ src/main/{ipc-api => ipc-handlers}/index.js | 0 src/main/models/InformationSchema.js | 10 ++++ src/renderer/App.vue | 30 ++++++---- ...eExploreBar.vue => DatabaseExploreBar.vue} | 20 +++++-- src/renderer/components/DatabaseWorkspace.vue | 57 +++++++++++++++++++ .../components/ModalNewConnection.vue | 41 ++++++------- src/renderer/components/TheAppWelcome.vue | 30 +++++----- src/renderer/components/TheSettingBar.vue | 41 +++++++++---- src/renderer/ipc-api/Connection.js | 19 +++++++ src/renderer/scss/_variables.scss | 4 +- src/renderer/store/index.js | 4 +- .../store/modules/connections.store.js | 6 ++ 16 files changed, 247 insertions(+), 102 deletions(-) delete mode 100644 src/main/ipc-api/connection.js create mode 100644 src/main/ipc-handlers/connection.js rename src/main/{ipc-api => ipc-handlers}/index.js (100%) create mode 100644 src/main/models/InformationSchema.js rename src/renderer/components/{TheExploreBar.vue => DatabaseExploreBar.vue} (52%) create mode 100644 src/renderer/components/DatabaseWorkspace.vue create mode 100644 src/renderer/ipc-api/Connection.js diff --git a/.gitignore b/.gitignore index 3fa65893..2526b5df 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ dist/ node_modules/ thumbs.db .idea/ -.vscode \ No newline at end of file +.vscode +TODO.md \ No newline at end of file diff --git a/src/main/index.js b/src/main/index.js index 443950b0..bcab7887 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -5,7 +5,7 @@ import * as path from 'path'; import { format as formatUrl } from 'url'; import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'; -import ipcApi from './ipc-api'; +import ipcHandlers from './ipc-handlers'; const isDevelopment = process.env.NODE_ENV !== 'production'; process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'; @@ -15,8 +15,8 @@ let mainWindow; function createMainWindow () { const window = new BrowserWindow({ - width: 1200, - height: 900, + width: 1600, + height: 1000, minHeight: 550, minWidth: 450, title: 'Antares', @@ -61,8 +61,8 @@ function createMainWindow () { }); }); - // Initialize ipcApi - ipcApi(); + // Initialize ipcHandlers + ipcHandlers(); return window; }; diff --git a/src/main/ipc-api/connection.js b/src/main/ipc-api/connection.js deleted file mode 100644 index 90503672..00000000 --- a/src/main/ipc-api/connection.js +++ /dev/null @@ -1,24 +0,0 @@ - -import { ipcMain } from 'electron'; -import knex from 'knex'; - -export default () => { - ipcMain.handle('testConnection', async (event, conn) => { - try { - await knex({ - client: conn.client, - connection: { - host: conn.host, - port: +conn.port, - user: conn.user, - password: conn.password - } - }).raw('SELECT 1+1 AS result'); - - return { status: 'success' }; - } - catch (err) { - return { status: 'error', response: err }; - } - }); -}; diff --git a/src/main/ipc-handlers/connection.js b/src/main/ipc-handlers/connection.js new file mode 100644 index 00000000..628d8c54 --- /dev/null +++ b/src/main/ipc-handlers/connection.js @@ -0,0 +1,50 @@ + +import { ipcMain } from 'electron'; +import knex from 'knex'; +import InformationSchema from '../models/InformationSchema'; + +const connections = {}; + +export default () => { + ipcMain.handle('testConnection', async (event, conn) => { + try { + await knex({ + client: conn.client, + connection: { + host: conn.host, + port: +conn.port, + user: conn.user, + password: conn.password + } + }).raw('SELECT 1+1 AS result'); + + return { status: 'success' }; + } + catch (err) { + return { status: 'error', response: err }; + } + }); + + ipcMain.handle('checkConnection', async (event, uid) => { + return uid in connections; + }); + + ipcMain.handle('connect', async (event, conn) => { + // TODO: make a test before + connections[conn.uid] = knex({ + client: conn.client, + connection: { + host: conn.host, + port: +conn.port, + user: conn.user, + password: conn.password + }, + pool: { + min: 1, + max: 3 + } + }); + + return await InformationSchema.getStructure(connections[conn.uid]); + }); +}; diff --git a/src/main/ipc-api/index.js b/src/main/ipc-handlers/index.js similarity index 100% rename from src/main/ipc-api/index.js rename to src/main/ipc-handlers/index.js diff --git a/src/main/models/InformationSchema.js b/src/main/models/InformationSchema.js new file mode 100644 index 00000000..1d88c054 --- /dev/null +++ b/src/main/models/InformationSchema.js @@ -0,0 +1,10 @@ +'use strict'; + +export default class { + static getStructure (connection) { + return connection() + .select('*') + .withSchema('information_schema') + .from('TABLES'); + } +} diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 21803ecf..b716b072 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -1,14 +1,15 @@ <template> <div id="wrapper"> - <!-- <TheHeader /> --> <TheSettingBar /> - <TheExploreBar /> <div id="main-content" class="container"> - <!-- <BaseLoaderLayer - id="main-loader" - :is-loading="isLoading" - /> --> <TheAppWelcome v-if="!connections.length" @newConn="showNewConnModal" /> + <div v-else class="columns col-gapless"> + <DatabaseWorkspace + v-for="connection in connections" + :key="connection.uid" + :connection="connection" + /> + </div> </div> <TheFooter /> <ModalNewConnection v-if="isNewConnModal" /> @@ -18,18 +19,18 @@ <script> import { mapActions, mapGetters } from 'vuex'; import TheSettingBar from '@/components/TheSettingBar'; -import TheExploreBar from '@/components/TheExploreBar'; import TheFooter from '@/components/TheFooter'; import TheAppWelcome from '@/components/TheAppWelcome'; +import DatabaseWorkspace from '@/components/DatabaseWorkspace'; import ModalNewConnection from '@/components/ModalNewConnection'; export default { name: 'App', components: { TheSettingBar, - TheExploreBar, TheFooter, TheAppWelcome, + DatabaseWorkspace, ModalNewConnection }, data () { @@ -51,7 +52,7 @@ export default { }; </script> -<style> +<style lang="scss"> html, body{ height: 100%; @@ -63,7 +64,12 @@ export default { position: relative; } - /* #main-content{ - background: #232524; - } */ + #main-content { + padding: 0; + justify-content: flex-start; + + > .columns{ + height: 100vh; + } + } </style> diff --git a/src/renderer/components/TheExploreBar.vue b/src/renderer/components/DatabaseExploreBar.vue similarity index 52% rename from src/renderer/components/TheExploreBar.vue rename to src/renderer/components/DatabaseExploreBar.vue index 20b573f0..35bcd934 100644 --- a/src/renderer/components/TheExploreBar.vue +++ b/src/renderer/components/DatabaseExploreBar.vue @@ -1,18 +1,27 @@ <template> - <div id="explorebar" class="container"> - <!-- aaa --> + <div class="workspace-explorebar column"> + <button + v-if="!isConnected" + class="btn btn-primary mt-4" + @click="$emit('connect')" + > + Connect + </button> </div> </template> <script> export default { - name: 'TheExploreBar' - + name: 'DatabaseExploreBar', + props: { + uid: String, + isConnected: Boolean + } }; </script> <style lang="scss"> - #explorebar{ + .workspace-explorebar{ width: $explorebar-width; display: flex; flex-direction: column; @@ -22,5 +31,6 @@ export default { margin-bottom: $footer-height; box-shadow: 0 0 1px 0px #000; z-index: 8; + flex: initial; } </style> diff --git a/src/renderer/components/DatabaseWorkspace.vue b/src/renderer/components/DatabaseWorkspace.vue new file mode 100644 index 00000000..ceef30ff --- /dev/null +++ b/src/renderer/components/DatabaseWorkspace.vue @@ -0,0 +1,57 @@ +<template> + <div v-show="selectedConnection === connection.uid" class="workspace column columns"> + <DatabaseExploreBar + :uid="connection.uid" + :is-connected="isConnected" + @connect="startConnection" + /> + <div class="workspace-tabs column"> + <p>{{ connection.uid }}</p> + </div> + </div> +</template> + +<script> +import { mapGetters } from 'vuex'; +import Connection from '@/ipc-api/Connection'; +import DatabaseExploreBar from '@/components/DatabaseExploreBar'; + +export default { + name: 'DatabaseWorkspace', + components: { + DatabaseExploreBar + }, + props: { + connection: Object + }, + data () { + return { + isConnected: false, + structure: null + }; + }, + computed: { + ...mapGetters({ + selectedConnection: 'connections/getSelected' + }) + }, + async created () { + this.isConnected = await Connection.checkConnection(this.connection.uid); + if (this.isConnected) + this.structure = await Connection.connect(this.connection);// TODO: use refresh + }, + methods: { + async startConnection () { + this.structure = await Connection.connect(this.connection); + this.isConnected = true; + } + } +}; +</script> + +<style lang="scss"> + .workspace{ + padding: 0; + margin: 0; + } +</style> diff --git a/src/renderer/components/ModalNewConnection.vue b/src/renderer/components/ModalNewConnection.vue index f63053c7..7a68fc73 100644 --- a/src/renderer/components/ModalNewConnection.vue +++ b/src/renderer/components/ModalNewConnection.vue @@ -128,7 +128,7 @@ <script> import { mapActions } from 'vuex'; -import { ipcRenderer } from 'electron'; +import Connection from '@/ipc-api/Connection'; import ModalAskCredentials from '@/components/ModalAskCredentials'; import BaseToast from '@/components/BaseToast'; @@ -177,34 +177,25 @@ export default { if (this.connection.ask) this.isAsking = true; - else - await this.invokeTest(this.connection); + else { + try { + const res = await Connection.makeTest(this.connection); + if (res.status === 'error') + this.toast = { status: 'error', message: res.response.message }; + else + this.toast = { status: 'success', message: 'Connection successifully made!' }; + } + 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); - await this.invokeTest(params); - }, - invokeTest (params) { - return new Promise((resolve, reject) => { - ipcRenderer.invoke('testConnection', params).then(res => { - if (res.status === 'error') { - this.toast = { - status: 'error', - message: res.response.message - }; - } - else { - this.toast = { - status: 'success', - message: 'Connection successifully made!' - }; - } - - this.isTesting = false; - resolve(); - }); - }); + await Connection.makeTest(params); }, saveNewConnection () { this.addConnection(this.connection); diff --git a/src/renderer/components/TheAppWelcome.vue b/src/renderer/components/TheAppWelcome.vue index 7c106328..ba360fce 100644 --- a/src/renderer/components/TheAppWelcome.vue +++ b/src/renderer/components/TheAppWelcome.vue @@ -1,18 +1,20 @@ <template> - <div class="empty text-light"> - <div class="empty-icon"> - <i class="material-icons md-48">mood</i> - </div> - <p class="empty-title h5"> - Welcome to Antares SQL Client! - </p> - <p class="empty-subtitle"> - Your first step: create a new database connection. - </p> - <div class="empty-action"> - <button class="btn btn-primary" @click="$emit('newConn')"> - Create connection - </button> + <div class="columns"> + <div class="column col-12 empty text-light"> + <div class="empty-icon"> + <i class="material-icons md-48">mood</i> + </div> + <p class="empty-title h5"> + Welcome to Antares SQL Client! + </p> + <p class="empty-subtitle"> + Your first step: create a new database connection. + </p> + <div class="empty-action"> + <button class="btn btn-primary" @click="$emit('newConn')"> + Create connection + </button> + </div> </div> </div> </template> diff --git a/src/renderer/components/TheSettingBar.vue b/src/renderer/components/TheSettingBar.vue index a28df167..e2f33235 100644 --- a/src/renderer/components/TheSettingBar.vue +++ b/src/renderer/components/TheSettingBar.vue @@ -1,30 +1,31 @@ <template> - <div id="settingbar" class="container"> + <div id="settingbar"> <div class="settingbar-top-elements"> <ul class="settingbar-elements"> <li v-for="connection in connections" :key="connection.uid" - class="settingbar-element btn btn-link tooltip tooltip-right p-0" + class="settingbar-element btn btn-link tooltip tooltip-right" :class="{'selected': connection.uid === selectedConnection}" :data-tooltip="`${connection.user}@${connection.host}:${connection.port}`" + @click="selectConnection(connection.uid)" > - <i class="dbi" :class="`dbi-${connection.client}`" /> + <i class="settingbar-element-icon dbi" :class="`dbi-${connection.client}`" /> </li> <li - class="settingbar-element btn btn-link tooltip tooltip-right" + class="settingbar-element btn btn-link tooltip tooltip-right pt-3" data-tooltip="Add connection" @click="showNewConnModal" > - <i class="material-icons text-light">add</i> + <i class="settingbar-element-icon material-icons text-light">add</i> </li> </ul> </div> <div class="settingbar-bottom-elements"> <ul class="settingbar-elements"> - <li class="settingbar-element btn btn-link tooltip tooltip-right" data-tooltip="Settings"> - <i class="material-icons text-light">settings</i> + <li class="settingbar-element btn btn-link tooltip tooltip-right mb-2" data-tooltip="Settings"> + <i class="settingbar-element-icon material-icons text-light">settings</i> </li> </ul> </div> @@ -44,9 +45,9 @@ export default { }, methods: { ...mapActions({ - showNewConnModal: 'connections/showNewConnModal' - }), - isActiveTab: uid => uid === this.selectedConnection + showNewConnModal: 'connections/showNewConnModal', + selectConnection: 'connections/selectConnection' + }) } }; </script> @@ -59,7 +60,7 @@ export default { justify-content: space-between; align-items: center; background: $bg-color-light; - padding: .5rem 0; + padding: 0; margin-bottom: $footer-height; box-shadow: 0 0 1px 0px #000; z-index: 9; @@ -72,9 +73,25 @@ export default { .settingbar-element{ height: initial; + width: 100%; + padding: 0; + padding: .3rem 0 0; + margin: 0; + border-left: 3px solid transparent; + opacity: .5; + transition: opacity .2s; + + &:hover{ + opacity: 1; + } + + &.selected{ + border-left-color: $body-font-color; + opacity: 1; + } .settingbar-element-icon{ - width: 42px; + margin-left: -3px; } } diff --git a/src/renderer/ipc-api/Connection.js b/src/renderer/ipc-api/Connection.js new file mode 100644 index 00000000..7867fef4 --- /dev/null +++ b/src/renderer/ipc-api/Connection.js @@ -0,0 +1,19 @@ +'use strict'; +import { ipcRenderer } from 'electron'; + +export default class { + static makeTest (params) { + return ipcRenderer.invoke('testConnection', params); + } + + static checkConnection (params) { + return ipcRenderer.invoke('checkConnection', params); + } + + static connect (params) { + return ipcRenderer.invoke('connect', params); + } + + // TODO: refresh + // TODO: disconnect +} diff --git a/src/renderer/scss/_variables.scss b/src/renderer/scss/_variables.scss index 7c84b971..0893bc85 100644 --- a/src/renderer/scss/_variables.scss +++ b/src/renderer/scss/_variables.scss @@ -7,6 +7,6 @@ $bg-color-light: #3f3f3f; $bg-color-gray: #272727; /*Sizes*/ -$settingbar-width: 4rem; -$explorebar-width: 16rem; +$settingbar-width: 3rem; +$explorebar-width: 14rem; $footer-height: 1.5rem; \ No newline at end of file diff --git a/src/renderer/store/index.js b/src/renderer/store/index.js index e77b85a4..d08b40e3 100644 --- a/src/renderer/store/index.js +++ b/src/renderer/store/index.js @@ -8,10 +8,10 @@ import application from './modules/application.store'; import connections from './modules/connections.store'; const vuexLocalStorage = new VuexPersist({ - key: 'vuex', // The key to store the state on in the storage provider. + key: 'application', // The key to store the state on in the storage provider. storage: window.localStorage, reducer: state => ({ - connections: state.connections.connections + connections: state.connections }) }); diff --git a/src/renderer/store/modules/connections.store.js b/src/renderer/store/modules/connections.store.js index c7e77935..fde95d60 100644 --- a/src/renderer/store/modules/connections.store.js +++ b/src/renderer/store/modules/connections.store.js @@ -26,6 +26,9 @@ export default { }, HIDE_NEW_CONNECTION_MODAL (state) { state.is_new_modal = false; + }, + SELECT_CONNECTION (state, uid) { + state.connection_selected = uid; } }, actions: { @@ -38,6 +41,9 @@ export default { }, hideNewConnModal ({ commit }) { commit('HIDE_NEW_CONNECTION_MODAL'); + }, + selectConnection ({ commit }, uid) { + commit('SELECT_CONNECTION', uid); } } };