2022-07-02 15:30:36 +02:00
|
|
|
<template>
|
|
|
|
<Teleport to="#window-content">
|
|
|
|
<div class="modal active">
|
|
|
|
<a class="modal-overlay" @click.stop="closeModal" />
|
|
|
|
<div ref="trapRef" class="modal-container p-0 pb-4">
|
|
|
|
<div class="modal-header pl-2">
|
|
|
|
<div class="modal-title h6">
|
|
|
|
<div class="d-flex">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiApps"
|
|
|
|
class="mr-1"
|
|
|
|
:size="24"
|
|
|
|
/>
|
2023-08-03 18:28:50 +02:00
|
|
|
<span class="cut-text">{{ t('connection.allConnections') }}</span>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
|
|
|
</div>
|
|
|
|
<div class="modal-body py-0">
|
|
|
|
<div class="columns">
|
2022-07-04 12:27:04 +02:00
|
|
|
<div class="connections-search column col-12 columns col-gapless">
|
|
|
|
<div class="column col-12 mt-2">
|
2022-07-04 17:03:24 +02:00
|
|
|
<div ref="searchForm" class="form-group has-icon-right p-2 m-0">
|
2022-07-04 12:27:04 +02:00
|
|
|
<input
|
|
|
|
v-model="searchTerm"
|
|
|
|
class="form-input"
|
|
|
|
type="text"
|
2023-08-03 18:28:50 +02:00
|
|
|
:placeholder="t('connection.searchForConnections')"
|
2022-07-04 12:27:04 +02:00
|
|
|
@keypress.esc="searchTerm = ''"
|
|
|
|
>
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
v-if="!searchTerm"
|
|
|
|
icon-name="mdiMagnify"
|
|
|
|
class="form-icon pr-4"
|
|
|
|
:size="18"
|
|
|
|
/>
|
|
|
|
<BaseIcon
|
2022-07-04 12:27:04 +02:00
|
|
|
v-else
|
2023-09-17 18:49:37 +02:00
|
|
|
icon-name="mdiBackspace"
|
|
|
|
class="form-icon c-hand pr-4"
|
|
|
|
:size="18"
|
2022-07-04 12:27:04 +02:00
|
|
|
@click="searchTerm = ''"
|
|
|
|
/>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
2022-07-04 12:27:04 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-04 17:03:24 +02:00
|
|
|
<TransitionGroup name="fade" :duration="{ enter: 200, leave: 200 }">
|
2022-07-04 12:27:04 +02:00
|
|
|
<div
|
|
|
|
v-for="connection in filteredConnections"
|
|
|
|
:key="connection.uid"
|
|
|
|
class="connection-block column col-md-6 col-lg-4 col-3 p-3"
|
|
|
|
tabindex="0"
|
|
|
|
@click.stop="selectConnection(connection.uid)"
|
|
|
|
@keypress.stop.enter="selectConnection(connection.uid)"
|
2022-07-04 17:03:24 +02:00
|
|
|
@mouseover="connectionHover = connection.uid"
|
|
|
|
@mouseleave="connectionHover = null"
|
2022-07-04 12:27:04 +02:00
|
|
|
>
|
|
|
|
<div class="panel">
|
2022-07-04 17:03:24 +02:00
|
|
|
<div class="panel-header p-2 text-center p-relative">
|
2022-07-04 12:27:04 +02:00
|
|
|
<figure class="avatar avatar-lg pt-1 mb-1">
|
|
|
|
<i class="settingbar-element-icon dbi" :class="[`dbi-${connection.client}`]" />
|
|
|
|
</figure>
|
|
|
|
<div class="panel-title h6 text-ellipsis">
|
|
|
|
{{ getConnectionName(connection.uid) }}
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
2022-07-04 12:27:04 +02:00
|
|
|
<div class="panel-subtitle">
|
|
|
|
{{ clients.get(connection.client) || connection.client }}
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
2022-11-21 20:19:02 +01:00
|
|
|
<div class="all-connections-buttons p-absolute d-flex" :style="'top: 0; right: 0;'">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiDelete"
|
|
|
|
class="all-connections-delete ml-2"
|
2023-08-03 18:28:50 +02:00
|
|
|
:title="t('general.delete')"
|
2023-09-17 18:49:37 +02:00
|
|
|
:size="18"
|
2022-07-04 17:03:24 +02:00
|
|
|
@click.stop="askToDelete(connection)"
|
|
|
|
/>
|
|
|
|
</div>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
2022-07-04 12:27:04 +02:00
|
|
|
<div class="panel-body text-center">
|
|
|
|
<div v-if="connection.databasePath">
|
|
|
|
<div class="text-ellipsis" :title="connection.databasePath">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiDatabase"
|
|
|
|
class="p-relative"
|
|
|
|
:style="'top:3px'"
|
|
|
|
:size="18"
|
|
|
|
/> <span class="text-bold">{{
|
2022-07-04 17:03:24 +02:00
|
|
|
connection.databasePath
|
|
|
|
}}</span>
|
2022-07-04 12:27:04 +02:00
|
|
|
</div>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
2022-07-04 12:27:04 +02:00
|
|
|
<div v-else>
|
|
|
|
<div class="text-ellipsis" :title="`${connection.host}:${connection.port}`">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiServer"
|
|
|
|
class="p-relative"
|
|
|
|
:style="'top:3px'"
|
|
|
|
:size="18"
|
|
|
|
/> <span class="text-bold">{{ connection.host
|
2022-07-04 17:03:24 +02:00
|
|
|
}}:{{ connection.port }}</span>
|
2022-07-04 12:27:04 +02:00
|
|
|
</div>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
2022-07-04 12:27:04 +02:00
|
|
|
<div v-if="connection.user">
|
|
|
|
<div class="text-ellipsis">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiAccount"
|
|
|
|
class="p-relative"
|
|
|
|
:style="'top:3px'"
|
|
|
|
:size="18"
|
|
|
|
/> <span class="text-bold">{{ connection.user
|
2022-07-04 17:03:24 +02:00
|
|
|
}}</span>
|
2022-07-04 12:27:04 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="connection.schema">
|
|
|
|
<div class="text-ellipsis">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiDatabase"
|
|
|
|
class="p-relative"
|
|
|
|
:style="'top:3px'"
|
|
|
|
:size="18"
|
|
|
|
/> <span class="text-bold">{{ connection.schema
|
2022-07-04 17:03:24 +02:00
|
|
|
}}</span>
|
2022-07-04 12:27:04 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="connection.database">
|
|
|
|
<div class="text-ellipsis">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiDatabase"
|
|
|
|
class="p-relative"
|
|
|
|
:style="'top:3px'"
|
|
|
|
:size="18"
|
|
|
|
/> <span class="text-bold">{{
|
2022-07-04 17:03:24 +02:00
|
|
|
connection.database
|
|
|
|
}}</span>
|
2022-07-04 12:27:04 +02:00
|
|
|
</div>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-04 12:27:04 +02:00
|
|
|
<div class="panel-footer text-center py-0">
|
|
|
|
<div v-if="connection.ssl" class="chip bg-success mt-2">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiShieldKey"
|
|
|
|
class="mr-1"
|
|
|
|
:size="18"
|
|
|
|
/>
|
2022-07-04 12:27:04 +02:00
|
|
|
SSL
|
|
|
|
</div>
|
|
|
|
<div v-if="connection.ssh" class="chip bg-success mt-2">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiConsoleNetwork"
|
|
|
|
class="mr-1"
|
|
|
|
:size="18"
|
|
|
|
/>
|
2022-07-04 12:27:04 +02:00
|
|
|
SSH
|
|
|
|
</div>
|
2023-12-06 08:44:07 +01:00
|
|
|
<div v-if="connection.readonly" class="chip bg-success mt-2">
|
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiLock"
|
|
|
|
class="mr-1"
|
|
|
|
:size="18"
|
|
|
|
/>
|
|
|
|
Read-only
|
|
|
|
</div>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-04 15:10:40 +02:00
|
|
|
<input
|
2022-07-04 17:03:24 +02:00
|
|
|
key="trick"
|
2022-07-04 15:10:40 +02:00
|
|
|
readonly
|
|
|
|
class="p-absolute"
|
2022-11-21 20:19:02 +01:00
|
|
|
:style="'width: 1px; height: 1px; opacity: 0;'"
|
2022-07-04 15:10:40 +02:00
|
|
|
type="text"
|
2022-07-04 17:03:24 +02:00
|
|
|
>
|
|
|
|
<!-- workaround for useFocusTrap $lastFocusable -->
|
2022-07-04 12:27:04 +02:00
|
|
|
</TransitionGroup>
|
2022-07-02 15:30:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-04 17:03:24 +02:00
|
|
|
|
|
|
|
<ConfirmModal
|
|
|
|
v-if="isConfirmModal"
|
|
|
|
@confirm="confirmDeleteConnection"
|
|
|
|
@hide="isConfirmModal = false"
|
|
|
|
>
|
|
|
|
<template #header>
|
|
|
|
<div class="d-flex">
|
2023-09-17 18:49:37 +02:00
|
|
|
<BaseIcon
|
|
|
|
icon-name="mdiServerRemove"
|
|
|
|
class="mr-1"
|
|
|
|
:size="24"
|
|
|
|
/> {{ t('connection.deleteConnection') }}
|
2022-07-04 17:03:24 +02:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<template #body>
|
|
|
|
<div class="mb-2">
|
2023-08-03 18:28:50 +02:00
|
|
|
{{ t('general.deleteConfirm') }} <b>{{ selectedConnectionName }}</b>?
|
2022-07-04 17:03:24 +02:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</ConfirmModal>
|
2022-07-02 15:30:36 +02:00
|
|
|
</Teleport>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2023-08-18 15:57:31 +02:00
|
|
|
import { ConnectionParams } from 'common/interfaces/antares';
|
2022-07-04 12:27:04 +02:00
|
|
|
import { storeToRefs } from 'pinia';
|
2023-08-18 15:57:31 +02:00
|
|
|
import { computed, onBeforeUnmount, Ref, ref } from 'vue';
|
2022-07-04 12:27:04 +02:00
|
|
|
import { useI18n } from 'vue-i18n';
|
2023-08-18 15:57:31 +02:00
|
|
|
|
|
|
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
2023-09-17 18:49:37 +02:00
|
|
|
import BaseIcon from '@/components/BaseIcon.vue';
|
2022-07-02 15:30:36 +02:00
|
|
|
import { useFocusTrap } from '@/composables/useFocusTrap';
|
|
|
|
import { useConnectionsStore } from '@/stores/connections';
|
|
|
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
2022-07-04 12:27:04 +02:00
|
|
|
|
|
|
|
const { t } = useI18n();
|
2022-07-02 15:30:36 +02:00
|
|
|
|
|
|
|
const connectionsStore = useConnectionsStore();
|
|
|
|
const workspacesStore = useWorkspacesStore();
|
|
|
|
|
2022-07-04 17:03:24 +02:00
|
|
|
const { connections,
|
|
|
|
lastConnections
|
|
|
|
} = storeToRefs(connectionsStore);
|
|
|
|
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
|
|
|
|
|
|
const {
|
|
|
|
getConnectionName,
|
|
|
|
deleteConnection
|
|
|
|
} = connectionsStore;
|
2022-07-04 12:27:04 +02:00
|
|
|
const { selectWorkspace } = workspacesStore;
|
2022-07-02 15:30:36 +02:00
|
|
|
|
|
|
|
const { trapRef } = useFocusTrap();
|
|
|
|
|
|
|
|
const emit = defineEmits(['close']);
|
|
|
|
|
2022-07-04 12:27:04 +02:00
|
|
|
const clients = new Map([
|
|
|
|
['mysql', 'MySQL'],
|
|
|
|
['maria', 'MariaDB'],
|
|
|
|
['pg', 'PostgreSQL'],
|
|
|
|
['sqlite', 'SQLite']
|
|
|
|
]);
|
|
|
|
|
|
|
|
const searchTerm = ref('');
|
2022-07-04 17:03:24 +02:00
|
|
|
const isConfirmModal = ref(false);
|
|
|
|
const connectionHover: Ref<string> = ref(null);
|
|
|
|
const selectedConnection: Ref<ConnectionParams> = ref(null);
|
|
|
|
|
|
|
|
const sortedConnections = computed(() => {
|
|
|
|
return connections.value
|
|
|
|
.map(c => {
|
|
|
|
const connTime = lastConnections.value.find((lc) => lc.uid === c.uid)?.time || 0;
|
|
|
|
return {
|
|
|
|
...c,
|
2022-11-21 20:19:02 +01:00
|
|
|
time: connTime
|
2022-07-04 17:03:24 +02:00
|
|
|
};
|
|
|
|
})
|
|
|
|
.sort((a, b) => {
|
|
|
|
if (a.time < b.time) return 1;
|
|
|
|
if (a.time > b.time) return -1;
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
});
|
2022-07-04 12:27:04 +02:00
|
|
|
|
|
|
|
const filteredConnections = computed(() => {
|
2022-07-04 17:03:24 +02:00
|
|
|
return sortedConnections.value.filter(connection => {
|
2022-07-04 12:27:04 +02:00
|
|
|
return connection.name?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
|
|
|
|
connection.host?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
|
|
|
|
connection.database?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
|
|
|
|
connection.databasePath?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
|
|
|
|
connection.schema?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
|
|
|
|
connection.user?.toLocaleLowerCase().includes(searchTerm.value.toLocaleLowerCase()) ||
|
|
|
|
String(connection.port)?.includes(searchTerm.value);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-07-04 17:03:24 +02:00
|
|
|
const selectedConnectionName = computed(() => getConnectionName(selectedConnection.value?.uid));
|
|
|
|
|
2022-07-02 15:30:36 +02:00
|
|
|
const closeModal = () => emit('close');
|
|
|
|
|
2022-07-04 12:27:04 +02:00
|
|
|
const selectConnection = (uid: string) => {
|
|
|
|
selectWorkspace(uid);
|
|
|
|
closeModal();
|
2022-07-02 15:30:36 +02:00
|
|
|
};
|
|
|
|
|
2022-07-04 17:03:24 +02:00
|
|
|
const askToDelete = (connection: ConnectionParams) => {
|
|
|
|
selectedConnection.value = connection;
|
|
|
|
isConfirmModal.value = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
const confirmDeleteConnection = () => {
|
|
|
|
if (selectedWorkspace.value === selectedConnection.value.uid)
|
|
|
|
selectWorkspace(null);
|
|
|
|
deleteConnection(selectedConnection.value);
|
|
|
|
};
|
|
|
|
|
|
|
|
const onKey = (e: KeyboardEvent) => {
|
2022-07-04 17:25:37 +02:00
|
|
|
e.stopPropagation();
|
2022-07-04 12:27:04 +02:00
|
|
|
if (e.key === 'Escape') {
|
|
|
|
if ((e.target as HTMLInputElement).tagName === 'INPUT' && searchTerm.value.length > 0)
|
|
|
|
searchTerm.value = '';
|
|
|
|
else
|
|
|
|
closeModal();
|
2022-07-02 15:30:36 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-07-04 17:25:37 +02:00
|
|
|
window.addEventListener('keydown', onKey);
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
window.removeEventListener('keydown', onKey);
|
|
|
|
});
|
2022-07-02 15:30:36 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.vscroll {
|
2022-08-09 16:18:21 +02:00
|
|
|
height: 1000px;
|
|
|
|
overflow: auto;
|
|
|
|
overflow-anchor: none;
|
2022-07-02 15:30:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.column-resizable {
|
2022-08-09 16:18:21 +02:00
|
|
|
&:hover,
|
|
|
|
&:active {
|
|
|
|
resize: horizontal;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
2022-07-02 15:30:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.table-column-title {
|
2022-08-09 16:18:21 +02:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
2022-07-02 15:30:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.sort-icon {
|
2022-08-09 16:18:21 +02:00
|
|
|
font-size: 0.7rem;
|
|
|
|
line-height: 1;
|
|
|
|
margin-left: 0.2rem;
|
2022-07-02 15:30:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.modal {
|
2022-08-09 16:18:21 +02:00
|
|
|
align-items: flex-start;
|
2022-07-02 15:30:36 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
.modal-container {
|
|
|
|
max-width: 75vw;
|
|
|
|
margin-top: 10vh;
|
2022-07-02 15:30:36 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
.modal-body {
|
|
|
|
height: 80vh;
|
|
|
|
}
|
|
|
|
}
|
2022-07-02 15:30:36 +02:00
|
|
|
}
|
|
|
|
|
2022-07-04 17:03:24 +02:00
|
|
|
.connections-search {
|
2022-08-09 16:18:21 +02:00
|
|
|
display: flex;
|
|
|
|
justify-content: space-around;
|
2022-07-04 12:27:04 +02:00
|
|
|
}
|
|
|
|
|
2022-07-04 17:03:24 +02:00
|
|
|
.connection-block {
|
2022-08-09 16:18:21 +02:00
|
|
|
cursor: pointer;
|
|
|
|
transition: all 0.2s;
|
|
|
|
border-radius: $border-radius;
|
|
|
|
outline: none;
|
2022-07-04 17:03:24 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
&:focus {
|
|
|
|
box-shadow: 0 0 3px 0.1rem rgba($primary-color, 80%);
|
|
|
|
}
|
2022-07-04 17:03:24 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
&:hover {
|
|
|
|
.all-connections-buttons {
|
|
|
|
.all-connections-delete,
|
|
|
|
.all-connections-pinned,
|
|
|
|
.all-connections-pin {
|
|
|
|
opacity: 0.5;
|
2022-07-04 17:03:24 +02:00
|
|
|
}
|
2022-08-09 16:18:21 +02:00
|
|
|
}
|
|
|
|
}
|
2022-07-04 17:03:24 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
.all-connections-buttons {
|
|
|
|
.all-connections-pinned {
|
|
|
|
opacity: 0.3;
|
|
|
|
transition: opacity 0.2s;
|
2022-07-04 17:03:24 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
&:hover {
|
|
|
|
opacity: 1;
|
2022-07-04 17:03:24 +02:00
|
|
|
}
|
2022-08-09 16:18:21 +02:00
|
|
|
}
|
2022-07-04 17:03:24 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
.all-connections-delete,
|
|
|
|
.all-connections-pin {
|
|
|
|
opacity: 0;
|
|
|
|
transition: opacity 0.2s;
|
2022-07-04 17:03:24 +02:00
|
|
|
|
2022-08-09 16:18:21 +02:00
|
|
|
&:hover {
|
|
|
|
opacity: 1;
|
2022-07-04 17:03:24 +02:00
|
|
|
}
|
2022-08-09 16:18:21 +02:00
|
|
|
}
|
|
|
|
}
|
2022-07-02 15:30:36 +02:00
|
|
|
}
|
|
|
|
</style>
|