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);
       }
    }
 };