refactor: ts and composition api on WorkspaceExplorebar* components

This commit is contained in:
Fabio Di Stasio 2022-06-09 20:08:32 +02:00
parent be70b5be7f
commit bd46d17424
16 changed files with 853 additions and 910 deletions

View File

@ -87,7 +87,7 @@ export interface TableInfos {
updated: Date; updated: Date;
engine: string; engine: string;
comment: string; comment: string;
size: number; size: number | false;
autoIncrement: number; autoIncrement: number;
collation: string; collation: string;
} }
@ -216,6 +216,7 @@ export interface TriggerInfos {
sqlMode: string; sqlMode: string;
created: Date; created: Date;
charset: string; charset: string;
enabled?: boolean;
} }
export interface CreateTriggerParams { export interface CreateTriggerParams {
@ -231,8 +232,21 @@ export interface CreateTriggerParams {
export interface AlterTriggerParams extends CreateTriggerParams { export interface AlterTriggerParams extends CreateTriggerParams {
oldName?: string; oldName?: string;
} }
export interface TriggerFunctionInfos {
name: string;
type: string;
security: string;
}
// Routines & Functions // Routines & Functions
export interface FunctionParam {
context: string;
name: string;
type: string;
length: number;
}
export interface RoutineInfos { export interface RoutineInfos {
name: string; name: string;
type: string; type: string;
@ -242,17 +256,11 @@ export interface RoutineInfos {
comment?: string; comment?: string;
charset?: string; charset?: string;
security?: string; security?: string;
parameters?: FunctionParam[];
} }
export type FunctionInfos = RoutineInfos export type FunctionInfos = RoutineInfos
export interface FunctionParam {
context: string;
name: string;
type: string;
length: number;
}
export interface CreateRoutineParams { export interface CreateRoutineParams {
name: string; name: string;
parameters?: FunctionParam[]; parameters?: FunctionParam[];
@ -337,7 +345,7 @@ export interface SchemaInfos {
name: string; name: string;
size: number; size: number;
tables: TableInfos[]; tables: TableInfos[];
functions: RoutineInfos[]; functions: FunctionInfos[];
procedures: RoutineInfos[]; procedures: RoutineInfos[];
triggers: TriggerInfos[]; triggers: TriggerInfos[];
schedulers: EventInfos[]; schedulers: EventInfos[];

View File

@ -101,7 +101,7 @@ const contextMenu = (event: MouseEvent, connection: ConnectionParams) => {
const tooltipPosition = (e: Event) => { const tooltipPosition = (e: Event) => {
const el = e.target ? e.target : e; const el = e.target ? e.target : e;
const fromTop = window.pageYOffset + (el as HTMLElement).getBoundingClientRect().top - ((el as HTMLElement).offsetHeight / 4); const fromTop = window.scrollY + (el as HTMLElement).getBoundingClientRect().top - ((el as HTMLElement).offsetHeight / 4);
(el as HTMLElement).querySelector<HTMLElement>('.ex-tooltip-content').style.top = `${fromTop}px`; (el as HTMLElement).querySelector<HTMLElement>('.ex-tooltip-content').style.top = `${fromTop}px`;
}; };

View File

@ -161,8 +161,7 @@ const {
const searchInput: Ref<HTMLInputElement> = ref(null); const searchInput: Ref<HTMLInputElement> = ref(null);
const explorebar: Ref<HTMLInputElement> = ref(null); const explorebar: Ref<HTMLInputElement> = ref(null);
const resizer: Ref<HTMLInputElement> = ref(null); const resizer: Ref<HTMLInputElement> = ref(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any const schema: Ref<Component & { selectSchema: (name: string) => void; $refs: {schemaAccordion: HTMLDetailsElement} }[]> = ref(null);
const schema: Ref<Component & { selectSchema: (name: string) => void; $refs: any }[]> = ref(null);
const isRefreshing = ref(false); const isRefreshing = ref(false);
const isNewDBModal = ref(false); const isNewDBModal = ref(false);
const localWidth = ref(null); const localWidth = ref(null);

View File

@ -56,7 +56,7 @@
</ConfirmModal> </ConfirmModal>
<ModalAskParameters <ModalAskParameters
v-if="isAskingParameters" v-if="isAskingParameters"
:local-routine="localElement" :local-routine="(localElement as any)"
:client="workspace.client" :client="workspace.client"
@confirm="runElement" @confirm="runElement"
@close="hideAskParamsModal" @close="hideAskParamsModal"
@ -64,336 +64,330 @@
</BaseContextMenu> </BaseContextMenu>
</template> </template>
<script> <script setup lang="ts">
import { computed, Prop, Ref, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useNotificationsStore } from '@/stores/notifications'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces'; import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu.vue';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal.vue';
import ModalAskParameters from '@/components/ModalAskParameters'; import ModalAskParameters from '@/components/ModalAskParameters.vue';
import Triggers from '@/ipc-api/Triggers'; import Triggers from '@/ipc-api/Triggers';
import Routines from '@/ipc-api/Routines'; import Routines from '@/ipc-api/Routines';
import Functions from '@/ipc-api/Functions'; import Functions from '@/ipc-api/Functions';
import Schedulers from '@/ipc-api/Schedulers'; import Schedulers from '@/ipc-api/Schedulers';
import { storeToRefs } from 'pinia'; import { EventInfos, FunctionInfos, RoutineInfos, TriggerInfos } from 'common/interfaces/antares';
export default { const { t } = useI18n();
name: 'WorkspaceExploreBarMiscContext',
components: {
BaseContextMenu,
ConfirmModal,
ModalAskParameters
},
props: {
contextEvent: MouseEvent,
selectedMisc: Object,
selectedSchema: String
},
emits: ['close-context', 'reload'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore); const props = defineProps({
contextEvent: MouseEvent,
selectedMisc: Object as Prop<{ name:string; type:string; enabled?: boolean }>,
selectedSchema: String
});
const { const emit = defineEmits(['close-context', 'reload']);
getWorkspace,
changeBreadcrumbs,
addLoadingElement,
removeLoadingElement,
removeTabs,
newTab
} = workspacesStore;
return { const { addNotification } = useNotificationsStore();
addNotification, const workspacesStore = useWorkspacesStore();
selectedWorkspace,
getWorkspace,
changeBreadcrumbs,
addLoadingElement,
removeLoadingElement,
removeTabs,
newTab
};
},
data () {
return {
isDeleteModal: false,
isRunModal: false,
isAskingParameters: false,
localElement: {}
};
},
computed: {
workspace () {
return this.getWorkspace(this.selectedWorkspace);
},
customizations () {
return this.getWorkspace(this.selectedWorkspace).customizations;
},
deleteMessage () {
switch (this.selectedMisc.type) {
case 'trigger':
return this.$t('message.deleteTrigger');
case 'procedure':
return this.$t('message.deleteRoutine');
case 'function':
case 'triggerFunction':
return this.$t('message.deleteFunction');
case 'scheduler':
return this.$t('message.deleteScheduler');
default:
return '';
}
}
},
methods: {
showDeleteModal () {
this.isDeleteModal = true;
},
hideDeleteModal () {
this.isDeleteModal = false;
},
showAskParamsModal () {
this.isAskingParameters = true;
},
hideAskParamsModal () {
this.isAskingParameters = false;
this.closeContext();
},
closeContext () {
this.$emit('close-context');
},
async deleteMisc () {
try {
let res;
switch (this.selectedMisc.type) { const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
case 'trigger':
res = await Triggers.dropTrigger({
uid: this.selectedWorkspace,
schema: this.selectedSchema,
trigger: this.selectedMisc.name
});
break;
case 'procedure':
res = await Routines.dropRoutine({
uid: this.selectedWorkspace,
schema: this.selectedSchema,
routine: this.selectedMisc.name
});
break;
case 'function':
case 'triggerFunction':
res = await Functions.dropFunction({
uid: this.selectedWorkspace,
schema: this.selectedSchema,
func: this.selectedMisc.name
});
break;
case 'scheduler':
res = await Schedulers.dropScheduler({
uid: this.selectedWorkspace,
schema: this.selectedSchema,
scheduler: this.selectedMisc.name
});
break;
}
const { status, response } = res; const {
getWorkspace,
addLoadingElement,
removeLoadingElement,
removeTabs,
newTab
} = workspacesStore;
if (status === 'success') { const isDeleteModal = ref(false);
this.removeTabs({ const isAskingParameters = ref(false);
uid: this.selectedWorkspace, const localElement: Ref<TriggerInfos | RoutineInfos | FunctionInfos | EventInfos> = ref(null);
elementName: this.selectedMisc.name,
elementType: this.selectedMisc.type,
schema: this.selectedSchema
});
this.closeContext(); const workspace = computed(() => {
this.$emit('reload'); return getWorkspace(selectedWorkspace.value);
} });
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
},
runElementCheck () {
if (this.selectedMisc.type === 'procedure')
this.runRoutineCheck();
else if (this.selectedMisc.type === 'function')
this.runFunctionCheck();
},
runElement (params) {
if (this.selectedMisc.type === 'procedure')
this.runRoutine(params);
else if (this.selectedMisc.type === 'function')
this.runFunction(params);
},
async runRoutineCheck () {
const params = {
uid: this.selectedWorkspace,
schema: this.selectedSchema,
routine: this.selectedMisc.name
};
try { const customizations = computed(() => {
const { status, response } = await Routines.getRoutineInformations(params); return getWorkspace(selectedWorkspace.value).customizations;
if (status === 'success') });
this.localElement = response;
else const deleteMessage = computed(() => {
this.addNotification({ status: 'error', message: response }); switch (props.selectedMisc.type) {
} case 'trigger':
catch (err) { return t('message.deleteTrigger');
this.addNotification({ status: 'error', message: err.stack }); case 'procedure':
} return t('message.deleteRoutine');
case 'function':
case 'triggerFunction':
return t('message.deleteFunction');
case 'scheduler':
return t('message.deleteScheduler');
default:
return '';
}
});
if (this.localElement.parameters.length) const showDeleteModal = () => {
this.showAskParamsModal(); isDeleteModal.value = true;
else };
this.runRoutine();
},
runRoutine (params) {
if (!params) params = [];
let sql; const hideDeleteModal = () => {
switch (this.workspace.client) { // TODO: move in a better place isDeleteModal.value = false;
case 'maria': };
case 'mysql':
case 'pg':
sql = `CALL ${this.localElement.name}(${params.join(',')})`;
break;
case 'mssql':
sql = `EXEC ${this.localElement.name} ${params.join(',')}`;
break;
default:
sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`;
}
this.newTab({ const showAskParamsModal = () => {
uid: this.workspace.uid, isAskingParameters.value = true;
content: sql, };
type: 'query',
schema: this.selectedSchema,
autorun: true
});
this.closeContext();
},
async runFunctionCheck () {
const params = {
uid: this.selectedWorkspace,
schema: this.selectedSchema,
func: this.selectedMisc.name
};
try { const hideAskParamsModal = () => {
const { status, response } = await Functions.getFunctionInformations(params); isAskingParameters.value = false;
if (status === 'success') closeContext();
this.localElement = response; };
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
if (this.localElement.parameters.length) const closeContext = () => {
this.showAskParamsModal(); emit('close-context');
else };
this.runFunction();
},
runFunction (params) {
if (!params) params = [];
let sql; const deleteMisc = async () => {
switch (this.workspace.client) { // TODO: move in a better place try {
case 'maria': let res;
case 'mysql':
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
break;
case 'pg':
sql = `SELECT ${this.localElement.name}(${params.join(',')})`;
break;
case 'mssql':
sql = `SELECT ${this.localElement.name} ${params.join(',')}`;
break;
default:
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
}
this.newTab({ switch (props.selectedMisc.type) {
uid: this.workspace.uid, case 'trigger':
content: sql, res = await Triggers.dropTrigger({
type: 'query', uid: selectedWorkspace.value,
schema: this.selectedSchema, schema: props.selectedSchema,
autorun: true trigger: props.selectedMisc.name
});
this.closeContext();
},
async toggleTrigger () {
this.addLoadingElement({
name: this.selectedMisc.name,
schema: this.selectedSchema,
type: 'trigger'
});
try {
const { status, response } = await Triggers.toggleTrigger({
uid: this.selectedWorkspace,
schema: this.selectedSchema,
trigger: this.selectedMisc.name,
enabled: this.selectedMisc.enabled
}); });
break;
if (status !== 'success') case 'procedure':
this.addNotification({ status: 'error', message: response }); res = await Routines.dropRoutine({
} uid: selectedWorkspace.value,
catch (err) { schema: props.selectedSchema,
this.addNotification({ status: 'error', message: err.stack }); routine: props.selectedMisc.name
}
this.removeLoadingElement({
name: this.selectedMisc.name,
schema: this.selectedSchema,
type: 'trigger'
});
this.closeContext();
this.$emit('reload');
},
async toggleScheduler () {
this.addLoadingElement({
name: this.selectedMisc.name,
schema: this.selectedSchema,
type: 'scheduler'
});
try {
const { status, response } = await Schedulers.toggleScheduler({
uid: this.selectedWorkspace,
schema: this.selectedSchema,
scheduler: this.selectedMisc.name,
enabled: this.selectedMisc.enabled
}); });
break;
case 'function':
case 'triggerFunction':
res = await Functions.dropFunction({
uid: selectedWorkspace.value,
schema: props.selectedSchema,
func: props.selectedMisc.name
});
break;
case 'scheduler':
res = await Schedulers.dropScheduler({
uid: selectedWorkspace.value,
schema: props.selectedSchema,
scheduler: props.selectedMisc.name
});
break;
}
if (status !== 'success') const { status, response } = res;
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
this.removeLoadingElement({ if (status === 'success') {
name: this.selectedMisc.name, removeTabs({
schema: this.selectedSchema, uid: selectedWorkspace.value,
type: 'scheduler' elementName: props.selectedMisc.name,
elementType: props.selectedMisc.type,
schema: props.selectedSchema
}); });
this.closeContext(); closeContext();
this.$emit('reload'); emit('reload');
} }
else
addNotification({ status: 'error', message: response });
}
catch (err) {
addNotification({ status: 'error', message: err.stack });
} }
}; };
const runElementCheck = () => {
if (props.selectedMisc.type === 'procedure')
runRoutineCheck();
else if (props.selectedMisc.type === 'function')
runFunctionCheck();
};
const runElement = (params: string[]) => {
if (props.selectedMisc.type === 'procedure')
runRoutine(params);
else if (props.selectedMisc.type === 'function')
runFunction(params);
};
const runRoutineCheck = async () => {
const params = {
uid: selectedWorkspace.value,
schema: props.selectedSchema,
routine: props.selectedMisc.name
};
try {
const { status, response } = await Routines.getRoutineInformations(params);
if (status === 'success')
localElement.value = response;
else
addNotification({ status: 'error', message: response });
}
catch (err) {
addNotification({ status: 'error', message: err.stack });
}
if ((localElement.value as RoutineInfos).parameters.length)
showAskParamsModal();
else
runRoutine();
};
const runRoutine = (params?: string[]) => {
if (!params) params = [];
let sql;
switch (workspace.value.client) { // TODO: move in a better place
case 'maria':
case 'mysql':
case 'pg':
sql = `CALL ${localElement.value.name}(${params.join(',')})`;
break;
case 'mssql':
sql = `EXEC ${localElement.value.name} ${params.join(',')}`;
break;
default:
sql = `CALL \`${localElement.value.name}\`(${params.join(',')})`;
}
newTab({
uid: workspace.value.uid,
content: sql,
type: 'query',
schema: props.selectedSchema,
autorun: true
});
closeContext();
};
const runFunctionCheck = async () => {
const params = {
uid: selectedWorkspace.value,
schema: props.selectedSchema,
func: props.selectedMisc.name
};
try {
const { status, response } = await Functions.getFunctionInformations(params);
if (status === 'success')
localElement.value = response;
else
addNotification({ status: 'error', message: response });
}
catch (err) {
addNotification({ status: 'error', message: err.stack });
}
if ((localElement.value as FunctionInfos).parameters.length)
showAskParamsModal();
else
runFunction();
};
const runFunction = (params?: string[]) => {
if (!params) params = [];
let sql;
switch (workspace.value.client) { // TODO: move in a better place
case 'maria':
case 'mysql':
sql = `SELECT \`${localElement.value.name}\` (${params.join(',')})`;
break;
case 'pg':
sql = `SELECT ${localElement.value.name}(${params.join(',')})`;
break;
case 'mssql':
sql = `SELECT ${localElement.value.name} ${params.join(',')}`;
break;
default:
sql = `SELECT \`${localElement.value.name}\` (${params.join(',')})`;
}
newTab({
uid: workspace.value.uid,
content: sql,
type: 'query',
schema: props.selectedSchema,
autorun: true
});
closeContext();
};
const toggleTrigger = async () => {
addLoadingElement({
name: props.selectedMisc.name,
schema: props.selectedSchema,
type: 'trigger'
});
try {
const { status, response } = await Triggers.toggleTrigger({
uid: selectedWorkspace.value,
schema: props.selectedSchema,
trigger: props.selectedMisc.name,
enabled: props.selectedMisc.enabled
});
if (status !== 'success')
addNotification({ status: 'error', message: response });
}
catch (err) {
addNotification({ status: 'error', message: err.stack });
}
removeLoadingElement({
name: props.selectedMisc.name,
schema: props.selectedSchema,
type: 'trigger'
});
closeContext();
emit('reload');
};
const toggleScheduler = async () => {
addLoadingElement({
name: props.selectedMisc.name,
schema: props.selectedSchema,
type: 'scheduler'
});
try {
const { status, response } = await Schedulers.toggleScheduler({
uid: selectedWorkspace.value,
schema: props.selectedSchema,
scheduler: props.selectedMisc.name,
enabled: props.selectedMisc.enabled
});
if (status !== 'success')
addNotification({ status: 'error', message: response });
}
catch (err) {
addNotification({ status: 'error', message: err.stack });
}
removeLoadingElement({
name: props.selectedMisc.name,
schema: props.selectedSchema,
type: 'scheduler'
});
closeContext();
emit('reload');
};
</script> </script>

View File

@ -1,112 +1,68 @@
<template> <template>
<BaseContextMenu <BaseContextMenu
:context-event="contextEvent" :context-event="props.contextEvent"
@close-context="closeContext" @close-context="closeContext"
> >
<div <div
v-if="selectedMisc === 'trigger'" v-if="props.selectedMisc === 'trigger'"
class="context-element" class="context-element"
@click="$emit('open-create-trigger-tab')" @click="emit('open-create-trigger-tab')"
> >
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $t('message.createNewTrigger') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ t('message.createNewTrigger') }}</span>
</div> </div>
<div <div
v-if="selectedMisc === 'procedure'" v-if="props.selectedMisc === 'procedure'"
class="context-element" class="context-element"
@click="$emit('open-create-routine-tab')" @click="emit('open-create-routine-tab')"
> >
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle text-light pr-1" /> {{ $t('message.createNewRoutine') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle text-light pr-1" /> {{ t('message.createNewRoutine') }}</span>
</div> </div>
<div <div
v-if="selectedMisc === 'function'" v-if="props.selectedMisc === 'function'"
class="context-element" class="context-element"
@click="$emit('open-create-function-tab')" @click="emit('open-create-function-tab')"
> >
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box text-light pr-1" /> {{ $t('message.createNewFunction') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box text-light pr-1" /> {{ t('message.createNewFunction') }}</span>
</div> </div>
<div <div
v-if="selectedMisc === 'triggerFunction'" v-if="props.selectedMisc === 'triggerFunction'"
class="context-element" class="context-element"
@click="$emit('open-create-trigger-function-tab')" @click="emit('open-create-trigger-function-tab')"
> >
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise text-light pr-1" /> {{ $t('message.createNewFunction') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise text-light pr-1" /> {{ t('message.createNewFunction') }}</span>
</div> </div>
<div <div
v-if="selectedMisc === 'scheduler'" v-if="props.selectedMisc === 'scheduler'"
class="context-element" class="context-element"
@click="$emit('open-create-scheduler-tab')" @click="emit('open-create-scheduler-tab')"
> >
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $t('message.createNewScheduler') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ t('message.createNewScheduler') }}</span>
</div> </div>
</BaseContextMenu> </BaseContextMenu>
</template> </template>
<script> <script setup lang="ts">
import { useNotificationsStore } from '@/stores/notifications'; import { useI18n } from 'vue-i18n';
import { useWorkspacesStore } from '@/stores/workspaces'; import BaseContextMenu from '@/components/BaseContextMenu.vue';
import BaseContextMenu from '@/components/BaseContextMenu';
import { storeToRefs } from 'pinia';
export default { const { t } = useI18n();
name: 'WorkspaceExploreBarMiscContext',
components: {
BaseContextMenu
},
props: {
contextEvent: MouseEvent,
selectedMisc: String,
selectedSchema: String
},
emits: [
'open-create-trigger-tab',
'open-create-routine-tab',
'open-create-function-tab',
'open-create-trigger-function-tab',
'open-create-scheduler-tab',
'close-context'
],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore); const props = defineProps({
contextEvent: MouseEvent,
selectedMisc: String,
selectedSchema: String
});
const { getWorkspace, changeBreadcrumbs } = workspacesStore; const emit = defineEmits([
'open-create-trigger-tab',
'open-create-routine-tab',
'open-create-function-tab',
'open-create-trigger-function-tab',
'open-create-scheduler-tab',
'close-context'
]);
return { const closeContext = () => {
addNotification, emit('close-context');
selectedWorkspace,
getWorkspace,
changeBreadcrumbs
};
},
data () {
return {
localElement: {}
};
},
computed: {
workspace () {
return this.getWorkspace(this.selectedWorkspace);
}
},
methods: {
showDeleteModal () {
this.isDeleteModal = true;
},
hideDeleteModal () {
this.isDeleteModal = false;
},
showAskParamsModal () {
this.isAskingParameters = true;
},
hideAskParamsModal () {
this.isAskingParameters = false;
this.closeContext();
},
closeContext () {
this.$emit('close-context');
}
}
}; };
</script> </script>

View File

@ -25,7 +25,7 @@
<ul class="menu menu-nav pt-0"> <ul class="menu menu-nav pt-0">
<li <li
v-for="table of filteredTables" v-for="table of filteredTables"
:ref="breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name) ? 'explorebar-selected' : ''" :ref="breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name) ? 'explorebarSelected' : ''"
:key="table.name" :key="table.name"
class="menu-item" class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}" :class="{'selected': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
@ -61,7 +61,7 @@
@contextmenu.prevent="showMiscFolderContext($event, 'trigger')" @contextmenu.prevent="showMiscFolderContext($event, 'trigger')"
> >
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" /> <i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
{{ $tc('word.trigger', 2) }} {{ t('word.trigger', 2) }}
</summary> </summary>
<div class="accordion-body"> <div class="accordion-body">
<div> <div>
@ -69,7 +69,7 @@
<li <li
v-for="trigger of filteredTriggers" v-for="trigger of filteredTriggers"
:key="trigger.name" :key="trigger.name"
:ref="breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name ? 'explorebar-selected' : ''" :ref="breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name ? 'explorebarSelected' : ''"
class="menu-item" class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}" :class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
@mousedown.left="selectMisc({schema: database.name, misc: trigger, type: 'trigger'})" @mousedown.left="selectMisc({schema: database.name, misc: trigger, type: 'trigger'})"
@ -84,7 +84,7 @@
<div <div
v-if="trigger.enabled === false" v-if="trigger.enabled === false"
class="tooltip tooltip-left disabled-indicator" class="tooltip tooltip-left disabled-indicator"
:data-tooltip="$t('word.disabled')" :data-tooltip="t('word.disabled')"
> >
<i class="table-icon mdi mdi-pause mdi-18px mr-1" /> <i class="table-icon mdi mdi-pause mdi-18px mr-1" />
</div> </div>
@ -100,27 +100,27 @@
<summary <summary
class="accordion-header misc-name" class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine}"
@contextmenu.prevent="showMiscFolderContext($event, 'procedure')" @contextmenu.prevent="showMiscFolderContext($event, 'routine')"
> >
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" /> <i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
{{ $tc('word.storedRoutine', 2) }} {{ t('word.storedRoutine', 2) }}
</summary> </summary>
<div class="accordion-body"> <div class="accordion-body">
<div> <div>
<ul class="menu menu-nav pt-0"> <ul class="menu menu-nav pt-0">
<li <li
v-for="(procedure, i) of filteredProcedures" v-for="(routine, i) of filteredProcedures"
:key="`${procedure.name}-${i}`" :key="`${routine.name}-${i}`"
:ref="breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name ? 'explorebar-selected' : ''" :ref="breadcrumbs.schema === database.name && breadcrumbs.routine === routine.name ? 'explorebarSelected' : ''"
class="menu-item" class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name}" :class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.routine === routine.name}"
@mousedown.left="selectMisc({schema: database.name, misc: procedure, type: 'routine'})" @mousedown.left="selectMisc({schema: database.name, misc: routine, type: 'routine'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: procedure, type: 'routine'})" @dblclick="openMiscPermanentTab({schema: database.name, misc: routine, type: 'routine'})"
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})" @contextmenu.prevent="showMiscContext($event, {...routine, type: 'routine'})"
> >
<a class="table-name"> <a class="table-name">
<i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" /> <i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" />
<span v-html="highlightWord(procedure.name)" /> <span v-html="highlightWord(routine.name)" />
</a> </a>
</li> </li>
</ul> </ul>
@ -137,7 +137,7 @@
@contextmenu.prevent="showMiscFolderContext($event, 'triggerFunction')" @contextmenu.prevent="showMiscFolderContext($event, 'triggerFunction')"
> >
<i class="misc-icon mdi mdi-18px mdi-folder-refresh mr-1" /> <i class="misc-icon mdi mdi-18px mdi-folder-refresh mr-1" />
{{ $tc('word.triggerFunction', 2) }} {{ t('word.triggerFunction', 2) }}
</summary> </summary>
<div class="accordion-body"> <div class="accordion-body">
<div> <div>
@ -145,7 +145,7 @@
<li <li
v-for="(func, i) of filteredTriggerFunctions" v-for="(func, i) of filteredTriggerFunctions"
:key="`${func.name}-${i}`" :key="`${func.name}-${i}`"
:ref="breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name ? 'explorebar-selected' : ''" :ref="breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name ? 'explorebarSelected' : ''"
class="menu-item" class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}" :class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
@mousedown.left="selectMisc({schema: database.name, misc: func, type: 'triggerFunction'})" @mousedown.left="selectMisc({schema: database.name, misc: func, type: 'triggerFunction'})"
@ -171,7 +171,7 @@
@contextmenu.prevent="showMiscFolderContext($event, 'function')" @contextmenu.prevent="showMiscFolderContext($event, 'function')"
> >
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" /> <i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
{{ $tc('word.function', 2) }} {{ t('word.function', 2) }}
</summary> </summary>
<div class="accordion-body"> <div class="accordion-body">
<div> <div>
@ -179,7 +179,7 @@
<li <li
v-for="(func, i) of filteredFunctions" v-for="(func, i) of filteredFunctions"
:key="`${func.name}-${i}`" :key="`${func.name}-${i}`"
:ref="breadcrumbs.schema === database.name && breadcrumbs.function === func.name ? 'explorebar-selected' : ''" :ref="breadcrumbs.schema === database.name && breadcrumbs.function === func.name ? 'explorebarSelected' : ''"
class="menu-item" class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}" :class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
@mousedown.left="selectMisc({schema: database.name, misc: func, type: 'function'})" @mousedown.left="selectMisc({schema: database.name, misc: func, type: 'function'})"
@ -205,7 +205,7 @@
@contextmenu.prevent="showMiscFolderContext($event, 'scheduler')" @contextmenu.prevent="showMiscFolderContext($event, 'scheduler')"
> >
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" /> <i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
{{ $tc('word.scheduler', 2) }} {{ t('word.scheduler', 2) }}
</summary> </summary>
<div class="accordion-body"> <div class="accordion-body">
<div> <div>
@ -213,7 +213,7 @@
<li <li
v-for="scheduler of filteredSchedulers" v-for="scheduler of filteredSchedulers"
:key="scheduler.name" :key="scheduler.name"
:ref="breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name ? 'explorebar-selected' : ''" :ref="breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name ? 'explorebarSelected' : ''"
class="menu-item" class="menu-item"
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}" :class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
@mousedown.left="selectMisc({schema: database.name, misc: scheduler, type: 'scheduler'})" @mousedown.left="selectMisc({schema: database.name, misc: scheduler, type: 'scheduler'})"
@ -228,7 +228,7 @@
<div <div
v-if="scheduler.enabled === false" v-if="scheduler.enabled === false"
class="tooltip tooltip-left disabled-indicator" class="tooltip tooltip-left disabled-indicator"
:data-tooltip="$t('word.disabled')" :data-tooltip="t('word.disabled')"
> >
<i class="table-icon mdi mdi-pause mdi-18px mr-1" /> <i class="table-icon mdi mdi-pause mdi-18px mr-1" />
</div> </div>
@ -242,228 +242,235 @@
</details> </details>
</template> </template>
<script> <script setup lang="ts">
import { useSettingsStore } from '@/stores/settings'; import { computed, Prop, Ref, ref, watch } from 'vue';
import { useWorkspacesStore } from '@/stores/workspaces';
import { formatBytes } from 'common/libs/formatBytes';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useSettingsStore } from '@/stores/settings';
import { Breadcrumb, useWorkspacesStore, WorkspaceStructure } from '@/stores/workspaces';
import { formatBytes } from 'common/libs/formatBytes';
import { EventInfos, FunctionInfos, RoutineInfos, TableInfos, TriggerFunctionInfos, TriggerInfos } from 'common/interfaces/antares';
// TODO: expose selectSchema & schemaAccordion const { t } = useI18n();
export default { const props = defineProps({
name: 'WorkspaceExploreBarSchema', database: Object as Prop<WorkspaceStructure>,
props: { connection: Object
database: Object, });
connection: Object
},
emits: [
'show-schema-context',
'show-table-context',
'show-misc-context',
'show-misc-folder-context'
],
setup () {
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const { applicationTheme } = storeToRefs(settingsStore); const emit = defineEmits([
'show-schema-context',
'show-table-context',
'show-misc-context',
'show-misc-folder-context'
]);
const { const settingsStore = useSettingsStore();
getLoadedSchemas, const workspacesStore = useWorkspacesStore();
getWorkspace,
getSearchTerm,
changeBreadcrumbs,
addLoadedSchema,
newTab,
refreshSchema
} = workspacesStore;
return { const { applicationTheme } = storeToRefs(settingsStore);
applicationTheme,
getLoadedSchemas,
getWorkspace,
getSearchTerm,
changeBreadcrumbs,
addLoadedSchema,
newTab,
refreshSchema
};
},
data () {
return {
isLoading: false
};
},
computed: {
searchTerm () {
return this.getSearchTerm(this.connection.uid);
},
filteredTables () {
return this.database.tables.filter(table => table.name.search(this.searchTerm) >= 0);
},
filteredTriggers () {
return this.database.triggers.filter(trigger => trigger.name.search(this.searchTerm) >= 0);
},
filteredProcedures () {
return this.database.procedures.filter(procedure => procedure.name.search(this.searchTerm) >= 0);
},
filteredFunctions () {
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
},
filteredTriggerFunctions () {
return this.database.triggerFunctions
? this.database.triggerFunctions.filter(func => func.name.search(this.searchTerm) >= 0)
: [];
},
filteredSchedulers () {
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
},
workspace () {
return this.getWorkspace(this.connection.uid);
},
breadcrumbs () {
return this.workspace.breadcrumbs;
},
customizations () {
return this.workspace.customizations;
},
loadedSchemas () {
return this.getLoadedSchemas(this.connection.uid);
},
maxSize () {
return this.database.tables.reduce((acc, curr) => {
if (curr.size > acc) acc = curr.size;
return acc;
}, 0);
},
totalSize () {
return this.database.tables.reduce((acc, curr) => acc + curr.size, 0);
}
},
watch: {
breadcrumbs (newVal, oldVal) {
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
setTimeout(() => {
const element = this.$refs['explorebar-selected'] ? this.$refs['explorebar-selected'][0] : null;
if (element) {
const rect = element.getBoundingClientRect();
const elemTop = rect.top;
const elemBottom = rect.bottom;
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
if (!isVisible) { const {
element.setAttribute('tabindex', '-1'); getLoadedSchemas,
element.focus(); getWorkspace,
element.removeAttribute('tabindex'); getSearchTerm,
} changeBreadcrumbs,
} addLoadedSchema,
}, 50); newTab,
refreshSchema
} = workspacesStore;
const schemaAccordion: Ref<HTMLDetailsElement> = ref(null);
const explorebarSelected: Ref<HTMLElement[]> = ref(null);
const isLoading = ref(false);
const searchTerm = computed(() => {
return getSearchTerm(props.connection.uid);
});
const filteredTables = computed(() => {
return props.database.tables.filter(table => table.name.search(searchTerm.value) >= 0);
});
const filteredTriggers = computed(() => {
return props.database.triggers.filter(trigger => trigger.name.search(searchTerm.value) >= 0);
});
const filteredProcedures = computed(() => {
return props.database.procedures.filter(procedure => procedure.name.search(searchTerm.value) >= 0);
});
const filteredFunctions = computed(() => {
return props.database.functions.filter(func => func.name.search(searchTerm.value) >= 0);
});
const filteredTriggerFunctions = computed(() => {
return props.database.triggerFunctions
? props.database.triggerFunctions.filter(func => func.name.search(searchTerm.value) >= 0)
: [];
});
const filteredSchedulers = computed(() => {
return props.database.schedulers.filter(scheduler => scheduler.name.search(searchTerm.value) >= 0);
});
const workspace = computed(() => {
return getWorkspace(props.connection.uid);
});
const breadcrumbs = computed(() => {
return workspace.value.breadcrumbs;
});
const customizations = computed(() => {
return workspace.value.customizations;
});
const loadedSchemas = computed(() => {
return getLoadedSchemas(props.connection.uid);
});
const maxSize = computed(() => {
return props.database.tables.reduce((acc: number, curr) => {
if (curr.size && curr.size > acc) acc = curr.size;
return acc;
}, 0);
});
watch(breadcrumbs, (newVal, oldVal) => {
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
setTimeout(() => {
const element = explorebarSelected.value ? explorebarSelected.value[0] : null;
if (element) {
const rect = element.getBoundingClientRect();
const elemTop = rect.top;
const elemBottom = rect.bottom;
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
if (!isVisible) {
element.setAttribute('tabindex', '-1');
element.focus();
element.removeAttribute('tabindex');
}
} }
} }, 50);
}, }
methods: { });
formatBytes,
async selectSchema (schema) {
if (!this.loadedSchemas.has(schema) && !this.isLoading) {
this.isLoading = true;
await this.refreshSchema({ uid: this.connection.uid, schema });
this.addLoadedSchema(schema);
this.isLoading = false;
}
},
selectTable ({ schema, table }) {
this.newTab({
uid: this.connection.uid,
elementName: table.name,
schema: this.database.name,
type: 'temp-data',
elementType: table.type
});
this.setBreadcrumbs({ schema, [table.type]: table.name }); const selectSchema = async (schema: string) => {
}, if (!loadedSchemas.value.has(schema) && !isLoading.value) {
selectMisc ({ schema, misc, type }) { isLoading.value = true;
const miscTempTabs = { setBreadcrumbs({ schema });
trigger: 'temp-trigger-props', await refreshSchema({ uid: props.connection.uid, schema });
triggerFunction: 'temp-trigger-function-props', addLoadedSchema(schema);
function: 'temp-function-props', isLoading.value = false;
routine: 'temp-routine-props',
scheduler: 'temp-scheduler-props'
};
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTempTabs[type],
elementType: type
});
this.setBreadcrumbs({ schema, [type]: misc.name });
},
openDataTab ({ schema, table }) {
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'data', elementType: table.type });
this.setBreadcrumbs({ schema, [table.type]: table.name });
},
openMiscPermanentTab ({ schema, misc, type }) {
const miscTabs = {
trigger: 'trigger-props',
triggerFunction: 'trigger-function-props',
function: 'function-props',
routine: 'routine-props',
scheduler: 'scheduler-props'
};
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTabs[type],
elementType: type
});
this.setBreadcrumbs({ schema, [type]: misc.name });
},
showSchemaContext (event, schema) {
this.$emit('show-schema-context', { event, schema });
},
showTableContext (event, table) {
this.$emit('show-table-context', { event, schema: this.database.name, table });
},
showMiscContext (event, misc) {
this.$emit('show-misc-context', { event, schema: this.database.name, misc });
},
showMiscFolderContext (event, type) {
this.$emit('show-misc-folder-context', { event, schema: this.database.name, type });
},
piePercentage (val) {
const perc = val / this.maxSize * 100;
if (this.applicationTheme === 'dark')
return { background: `conic-gradient(lime ${perc}%, white 0)` };
else
return { background: `conic-gradient(teal ${perc}%, silver 0)` };
},
setBreadcrumbs (payload) {
if (this.breadcrumbs.schema === payload.schema && this.breadcrumbs.table === payload.table) return;
this.changeBreadcrumbs(payload);
},
highlightWord (string) {
string = string.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
if (this.searchTerm) {
const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return string.replace(regexp, '<span class="text-primary">$1</span>');
}
else
return string;
},
checkLoadingStatus (name, type) {
return this.workspace.loadingElements.some(el =>
el.name === name &&
el.type === type &&
el.schema === this.database.name);
}
} }
}; };
const selectTable = ({ schema, table }: { schema: string; table: TableInfos }) => {
newTab({
uid: props.connection.uid,
elementName: table.name,
schema: props.database.name,
type: 'temp-data',
elementType: table.type
});
setBreadcrumbs({ schema, [table.type]: table.name });
};
const selectMisc = ({ schema, misc, type }: { schema: string; misc: { name: string }; type: 'trigger' | 'triggerFunction' | 'function' | 'routine' | 'scheduler' }) => {
const miscTempTabs = {
trigger: 'temp-trigger-props',
triggerFunction: 'temp-trigger-function-props',
function: 'temp-function-props',
routine: 'temp-routine-props',
scheduler: 'temp-scheduler-props'
};
newTab({
uid: props.connection.uid,
elementName: misc.name,
schema: props.database.name,
type: miscTempTabs[type],
elementType: type
});
setBreadcrumbs({ schema, [type]: misc.name });
};
const openDataTab = ({ schema, table }: { schema: string; table: TableInfos }) => {
newTab({ uid: props.connection.uid, elementName: table.name, schema: props.database.name, type: 'data', elementType: table.type });
setBreadcrumbs({ schema, [table.type]: table.name });
};
const openMiscPermanentTab = ({ schema, misc, type }: { schema: string; misc: { name: string }; type: 'trigger' | 'triggerFunction' | 'function' | 'routine' | 'scheduler' }) => {
const miscTabs = {
trigger: 'trigger-props',
triggerFunction: 'trigger-function-props',
function: 'function-props',
routine: 'routine-props',
scheduler: 'scheduler-props'
};
newTab({
uid: props.connection.uid,
elementName: misc.name,
schema: props.database.name,
type: miscTabs[type],
elementType: type
});
setBreadcrumbs({ schema, [type]: misc.name });
};
const showSchemaContext = (event: MouseEvent, schema: string) => {
emit('show-schema-context', { event, schema });
};
const showTableContext = (event: MouseEvent, table: TableInfos) => {
emit('show-table-context', { event, schema: props.database.name, table });
};
const showMiscContext = (event: MouseEvent, misc: TriggerInfos | TriggerFunctionInfos | RoutineInfos | FunctionInfos | EventInfos) => {
emit('show-misc-context', { event, schema: props.database.name, misc });
};
const showMiscFolderContext = (event: MouseEvent, type: string) => {
emit('show-misc-folder-context', { event, schema: props.database.name, type });
};
const piePercentage = (val: number) => {
const perc = val / maxSize.value * 100;
if (applicationTheme.value === 'dark')
return { background: `conic-gradient(lime ${perc}%, white 0)` };
else
return { background: `conic-gradient(teal ${perc}%, silver 0)` };
};
const setBreadcrumbs = (payload: Breadcrumb) => {
if (breadcrumbs.value.schema === payload.schema && breadcrumbs.value.table === payload.table) return;
changeBreadcrumbs(payload);
};
const highlightWord = (string: string) => {
string = string.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
if (searchTerm.value) {
const regexp = new RegExp(`(${searchTerm.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return string.replace(regexp, '<span class="text-primary">$1</span>');
}
else
return string;
};
const checkLoadingStatus = (name: string, type: string) => {
return workspace.value.loadingElements.some(el =>
el.name === name &&
el.type === type &&
el.schema === props.database.name);
};
defineExpose({ selectSchema, schemaAccordion });
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -123,159 +123,154 @@
</BaseContextMenu> </BaseContextMenu>
</template> </template>
<script> <script setup lang="ts">
import { Component, computed, nextTick, Ref, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces'; import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu.vue';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal.vue';
import ModalEditSchema from '@/components/ModalEditSchema'; import ModalEditSchema from '@/components/ModalEditSchema.vue';
import ModalExportSchema from '@/components/ModalExportSchema'; import ModalExportSchema from '@/components/ModalExportSchema.vue';
import ModalImportSchema from '@/components/ModalImportSchema'; import ModalImportSchema from '@/components/ModalImportSchema.vue';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import Application from '@/ipc-api/Application'; import Application from '@/ipc-api/Application';
import { storeToRefs } from 'pinia';
export default { const props = defineProps({
name: 'WorkspaceExploreBarSchemaContext', contextEvent: MouseEvent,
components: { selectedSchema: String
BaseContextMenu, });
ConfirmModal,
ModalEditSchema,
ModalExportSchema,
ModalImportSchema
},
props: {
contextEvent: MouseEvent,
selectedSchema: String
},
emits: [
'open-create-table-tab',
'open-create-view-tab',
'open-create-trigger-tab',
'open-create-routine-tab',
'open-create-function-tab',
'open-create-trigger-function-tab',
'open-create-scheduler-tab',
'close-context',
'reload'
],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore); const emit = defineEmits([
'open-create-table-tab',
'open-create-view-tab',
'open-create-trigger-tab',
'open-create-routine-tab',
'open-create-function-tab',
'open-create-trigger-function-tab',
'open-create-scheduler-tab',
'close-context',
'reload'
]);
const { const { addNotification } = useNotificationsStore();
getWorkspace, const workspacesStore = useWorkspacesStore();
changeBreadcrumbs
} = workspacesStore;
return { const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
addNotification,
selectedWorkspace, const {
getWorkspace, getWorkspace,
changeBreadcrumbs changeBreadcrumbs
}; } = workspacesStore;
},
data () { const importModalRef: Ref<Component & {startImport: (file: string) => void}> = ref(null);
return { const isDeleteModal = ref(false);
isDeleteModal: false, const isEditModal = ref(false);
isEditModal: false, const isExportSchemaModal = ref(false);
isExportSchemaModal: false, const isImportSchemaModal = ref(false);
isImportSchemaModal: false
}; const workspace = computed(() => getWorkspace(selectedWorkspace.value));
},
computed: { const openCreateTableTab = () => {
workspace () { emit('open-create-table-tab');
return this.getWorkspace(this.selectedWorkspace); };
}
}, const openCreateViewTab = () => {
methods: { emit('open-create-view-tab');
openCreateTableTab () { };
this.$emit('open-create-table-tab');
}, const openCreateTriggerTab = () => {
openCreateViewTab () { emit('open-create-trigger-tab');
this.$emit('open-create-view-tab'); };
},
openCreateTriggerTab () { const openCreateRoutineTab = () => {
this.$emit('open-create-trigger-tab'); emit('open-create-routine-tab');
}, };
openCreateRoutineTab () {
this.$emit('open-create-routine-tab'); const openCreateFunctionTab = () => {
}, emit('open-create-function-tab');
openCreateFunctionTab () { };
this.$emit('open-create-function-tab');
}, const openCreateTriggerFunctionTab = () => {
openCreateTriggerFunctionTab () { emit('open-create-trigger-function-tab');
this.$emit('open-create-trigger-function-tab'); };
},
openCreateSchedulerTab () { const openCreateSchedulerTab = () => {
this.$emit('open-create-scheduler-tab'); emit('open-create-scheduler-tab');
}, };
showDeleteModal () {
this.isDeleteModal = true; const showDeleteModal = () => {
}, isDeleteModal.value = true;
hideDeleteModal () { };
this.isDeleteModal = false;
}, const hideDeleteModal = () => {
showEditModal () { isDeleteModal.value = false;
this.isEditModal = true; };
},
hideEditModal () { const showEditModal = () => {
this.isEditModal = false; isEditModal.value = true;
this.closeContext(); };
},
showExportSchemaModal () { const hideEditModal = () => {
this.isExportSchemaModal = true; isEditModal.value = false;
}, closeContext();
hideExportSchemaModal () { };
this.isExportSchemaModal = false;
this.closeContext(); const showExportSchemaModal = () => {
}, isExportSchemaModal.value = true;
showImportSchemaModal () { };
this.isImportSchemaModal = true;
}, const hideExportSchemaModal = () => {
hideImportSchemaModal () { isExportSchemaModal.value = false;
this.isImportSchemaModal = false; closeContext();
this.closeContext(); };
},
async initImport () { const showImportSchemaModal = () => {
const result = await Application.showOpenDialog({ properties: ['openFile'], filters: [{ name: 'SQL', extensions: ['sql'] }] }); isImportSchemaModal.value = true;
if (result && !result.canceled) { };
const file = result.filePaths[0];
this.showImportSchemaModal(); const hideImportSchemaModal = () => {
this.$nextTick(() => { isImportSchemaModal.value = false;
this.$refs.importModalRef.startImport(file); closeContext();
}); };
}
}, const initImport = async () => {
closeContext () { const result = await Application.showOpenDialog({ properties: ['openFile'], filters: [{ name: 'SQL', extensions: ['sql'] }] });
this.$emit('close-context'); if (result && !result.canceled) {
}, const file = result.filePaths[0];
async deleteSchema () { showImportSchemaModal();
try { await nextTick();
const { status, response } = await Schema.deleteSchema({ importModalRef.value.startImport(file);
uid: this.selectedWorkspace, }
database: this.selectedSchema };
});
const closeContext = () => {
if (status === 'success') { emit('close-context');
if (this.selectedSchema === this.workspace.breadcrumbs.schema) };
this.changeBreadcrumbs({ schema: null });
const deleteSchema = async () => {
this.closeContext(); try {
this.$emit('reload'); const { status, response } = await Schema.deleteSchema({
} uid: selectedWorkspace.value,
else database: props.selectedSchema
this.addNotification({ status: 'error', message: response }); });
}
catch (err) { if (status === 'success') {
this.addNotification({ status: 'error', message: err.stack }); if (props.selectedSchema === workspace.value.breadcrumbs.schema)
} changeBreadcrumbs({ schema: null });
closeContext();
emit('reload');
} }
else
addNotification({ status: 'error', message: response });
}
catch (err) {
addNotification({ status: 'error', message: err.stack });
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.context-submenu { .context-submenu {
min-width: 150px !important; min-width: 150px !important;

View File

@ -71,151 +71,133 @@
</BaseContextMenu> </BaseContextMenu>
</template> </template>
<script> <script setup lang="ts">
import { computed, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces'; import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu.vue';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal.vue';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import { storeToRefs } from 'pinia';
export default { const props = defineProps({
name: 'WorkspaceExploreBarTableContext', contextEvent: MouseEvent,
components: { selectedTable: Object,
BaseContextMenu, selectedSchema: String
ConfirmModal });
},
props: {
contextEvent: MouseEvent,
selectedTable: Object,
selectedSchema: String
},
emits: ['close-context', 'duplicate-table', 'reload', 'delete-table'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore); const emit = defineEmits(['close-context', 'duplicate-table', 'reload', 'delete-table']);
const { const { addNotification } = useNotificationsStore();
getWorkspace, const workspacesStore = useWorkspacesStore();
newTab,
removeTabs,
addLoadingElement,
removeLoadingElement,
changeBreadcrumbs
} = workspacesStore;
return { const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
addNotification,
getWorkspace,
newTab,
removeTabs,
addLoadingElement,
removeLoadingElement,
changeBreadcrumbs,
selectedWorkspace
};
},
data () {
return {
isDeleteModal: false,
isEmptyModal: false
};
},
computed: {
workspace () {
return this.getWorkspace(this.selectedWorkspace);
},
customizations () {
return this.workspace && this.workspace.customizations ? this.workspace.customizations : {};
}
},
methods: {
showDeleteModal () {
this.isDeleteModal = true;
},
hideDeleteModal () {
this.isDeleteModal = false;
},
showEmptyModal () {
this.isEmptyModal = true;
},
hideEmptyModal () {
this.isEmptyModal = false;
},
closeContext () {
this.$emit('close-context');
},
openTableSettingTab () {
this.newTab({
uid: this.selectedWorkspace,
elementName: this.selectedTable.name,
schema: this.selectedSchema,
type: 'table-props',
elementType: 'table'
});
this.changeBreadcrumbs({ const {
schema: this.selectedSchema, getWorkspace,
table: this.selectedTable.name newTab,
}); addLoadingElement,
removeLoadingElement,
changeBreadcrumbs
} = workspacesStore;
this.closeContext(); const isDeleteModal = ref(false);
}, const isEmptyModal = ref(false);
openViewSettingTab () {
this.newTab({
uid: this.selectedWorkspace,
elementType: 'table',
elementName: this.selectedTable.name,
schema: this.selectedSchema,
type: 'view-props'
});
this.changeBreadcrumbs({ const workspace = computed(() => getWorkspace(selectedWorkspace.value));
schema: this.selectedSchema, const customizations = computed(() => workspace.value && workspace.value.customizations ? workspace.value.customizations : null);
view: this.selectedTable.name
});
this.closeContext(); const showDeleteModal = () => {
}, isDeleteModal.value = true;
duplicateTable () { };
this.$emit('duplicate-table', { schema: this.selectedSchema, table: this.selectedTable });
},
async emptyTable () {
this.closeContext();
this.addLoadingElement({ const hideDeleteModal = () => {
name: this.selectedTable.name, isDeleteModal.value = false;
schema: this.selectedSchema, };
type: 'table'
});
try { const showEmptyModal = () => {
const { status, response } = await Tables.truncateTable({ isEmptyModal.value = true;
uid: this.selectedWorkspace, };
table: this.selectedTable.name,
schema: this.selectedSchema
});
if (status === 'success') const hideEmptyModal = () => {
this.$emit('reload'); isEmptyModal.value = false;
else };
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
}
this.removeLoadingElement({ const closeContext = () => {
name: this.selectedTable.name, emit('close-context');
schema: this.selectedSchema, };
type: 'table'
}); const openTableSettingTab = () => {
}, newTab({
deleteTable () { uid: selectedWorkspace.value,
this.$emit('delete-table', { schema: this.selectedSchema, table: this.selectedTable }); elementName: props.selectedTable.name,
} schema: props.selectedSchema,
type: 'table-props',
elementType: 'table'
});
changeBreadcrumbs({
schema: props.selectedSchema,
table: props.selectedTable.name
});
closeContext();
};
const openViewSettingTab = () => {
newTab({
uid: selectedWorkspace.value,
elementType: 'table',
elementName: props.selectedTable.name,
schema: props.selectedSchema,
type: 'view-props'
});
changeBreadcrumbs({
schema: props.selectedSchema,
view: props.selectedTable.name
});
closeContext();
};
const duplicateTable = () => {
emit('duplicate-table', { schema: props.selectedSchema, table: props.selectedTable });
};
const emptyTable = async () => {
closeContext();
addLoadingElement({
name: props.selectedTable.name,
schema: props.selectedSchema,
type: 'table'
});
try {
const { status, response } = await Tables.truncateTable({
uid: selectedWorkspace.value,
table: props.selectedTable.name,
schema: props.selectedSchema
});
if (status === 'success')
emit('reload');
else
addNotification({ status: 'error', message: response });
} }
catch (err) {
addNotification({ status: 'error', message: err.stack });
}
removeLoadingElement({
name: props.selectedTable.name,
schema: props.selectedSchema,
type: 'table'
});
};
const deleteTable = () => {
emit('delete-table', { schema: props.selectedSchema, table: props.selectedTable });
}; };
</script> </script>

View File

@ -4,12 +4,12 @@ import connStringConstruct from '../libs/connStringDecode';
import { unproxify } from '../libs/unproxify'; import { unproxify } from '../libs/unproxify';
export default class { export default class {
static makeTest (params: ConnectionParams & { pgConnString: string }): Promise<IpcResponse> { static makeTest (params: ConnectionParams & { pgConnString?: string }): Promise<IpcResponse> {
const newParams = connStringConstruct(params) as ConnectionParams; const newParams = connStringConstruct(params) as ConnectionParams;
return ipcRenderer.invoke('test-connection', unproxify(newParams)); return ipcRenderer.invoke('test-connection', unproxify(newParams));
} }
static connect (params: ConnectionParams & { pgConnString: string }): Promise<IpcResponse> { static connect (params: ConnectionParams & { pgConnString?: string }): Promise<IpcResponse> {
const newParams = connStringConstruct(params) as ConnectionParams; const newParams = connStringConstruct(params) as ConnectionParams;
return ipcRenderer.invoke('connect', unproxify(newParams)); return ipcRenderer.invoke('connect', unproxify(newParams));
} }

View File

@ -1,9 +1,9 @@
import { AlterFunctionParams, CreateFunctionParams, FunctionInfos, IpcResponse } from 'common/interfaces/antares'; import { AlterFunctionParams, CreateFunctionParams, IpcResponse } from 'common/interfaces/antares';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify'; import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getFunctionInformations (params: { uid: string; schema: string; func: string}): Promise<IpcResponse<FunctionInfos>> { static getFunctionInformations (params: { uid: string; schema: string; func: string}): Promise<IpcResponse> {
return ipcRenderer.invoke('get-function-informations', unproxify(params)); return ipcRenderer.invoke('get-function-informations', unproxify(params));
} }

View File

@ -1,9 +1,9 @@
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify'; import { unproxify } from '../libs/unproxify';
import { AlterRoutineParams, CreateRoutineParams, IpcResponse, RoutineInfos } from 'common/interfaces/antares'; import { AlterRoutineParams, CreateRoutineParams, IpcResponse } from 'common/interfaces/antares';
export default class { export default class {
static getRoutineInformations (params: { uid: string; schema: string; routine: string}): Promise<IpcResponse<RoutineInfos>> { static getRoutineInformations (params: { uid: string; schema: string; routine: string}): Promise<IpcResponse> {
return ipcRenderer.invoke('get-routine-informations', unproxify(params)); return ipcRenderer.invoke('get-routine-informations', unproxify(params));
} }

View File

@ -19,7 +19,7 @@ export default class {
return ipcRenderer.invoke('create-scheduler', unproxify(params)); return ipcRenderer.invoke('create-scheduler', unproxify(params));
} }
static toggleScheduler (params: { uid: string; schema: string; scheduler: string}): Promise<IpcResponse> { static toggleScheduler (params: { uid: string; schema: string; scheduler: string; enabled: boolean}): Promise<IpcResponse> {
return ipcRenderer.invoke('toggle-scheduler', unproxify(params)); return ipcRenderer.invoke('toggle-scheduler', unproxify(params));
} }
} }

View File

@ -7,7 +7,7 @@ export default class {
return ipcRenderer.invoke('get-trigger-informations', unproxify(params)); return ipcRenderer.invoke('get-trigger-informations', unproxify(params));
} }
static dropTrigger (params: { schema: string; trigger: string }): Promise<IpcResponse> { static dropTrigger (params: { uid: string; schema: string; trigger: string }): Promise<IpcResponse> {
return ipcRenderer.invoke('drop-trigger', unproxify(params)); return ipcRenderer.invoke('drop-trigger', unproxify(params));
} }
@ -19,7 +19,7 @@ export default class {
return ipcRenderer.invoke('create-trigger', unproxify(params)); return ipcRenderer.invoke('create-trigger', unproxify(params));
} }
static toggleTrigger (params: { uid: string; schema: string; trigger: string }): Promise<IpcResponse> { static toggleTrigger (params: { uid: string; schema: string; trigger: string; enabled: boolean }): Promise<IpcResponse> {
return ipcRenderer.invoke('toggle-trigger', unproxify(params)); return ipcRenderer.invoke('toggle-trigger', unproxify(params));
} }
} }

View File

@ -10,7 +10,7 @@ const checkForSSl = (conn: string) => {
return conn.includes('ssl=true'); return conn.includes('ssl=true');
}; };
const connStringConstruct = (args: ConnectionParams & { pgConnString: string }): ConnectionParams => { const connStringConstruct = (args: ConnectionParams & { pgConnString?: string }): ConnectionParams => {
if (!args.pgConnString) if (!args.pgConnString)
return args; return args;

View File

@ -11,12 +11,14 @@ import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications'; import { useNotificationsStore } from '@/stores/notifications';
import { useSettingsStore } from '@/stores/settings'; import { useSettingsStore } from '@/stores/settings';
import { import {
ClientCode,
CollationInfos, CollationInfos,
ConnectionParams, ConnectionParams,
EventInfos, EventInfos,
FunctionInfos, FunctionInfos,
RoutineInfos, RoutineInfos,
TableInfos, TableInfos,
TriggerFunctionInfos,
TriggerInfos, TriggerInfos,
TypesGroup TypesGroup
} from 'common/interfaces/antares'; } from 'common/interfaces/antares';
@ -44,12 +46,13 @@ export interface WorkspaceStructure {
schedulers: EventInfos[]; schedulers: EventInfos[];
tables: TableInfos[]; tables: TableInfos[];
triggers: TriggerInfos[]; triggers: TriggerInfos[];
triggerFunctions: TriggerFunctionInfos[];
size: number; size: number;
} }
export interface Breadcrumb { export interface Breadcrumb {
function?: string; function?: string;
procedure?: string; routine?: string;
query?: string; query?: string;
scheduler?: string; scheduler?: string;
schema?: string; schema?: string;
@ -61,7 +64,7 @@ export interface Breadcrumb {
export interface Workspace { export interface Workspace {
uid: string; uid: string;
client?: string; client?: ClientCode;
connectionStatus: string; connectionStatus: string;
selectedTab: string | number; selectedTab: string | number;
searchTerm: string; searchTerm: string;
@ -133,7 +136,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
else else
this.selectedWorkspace = uid; this.selectedWorkspace = uid;
}, },
async connectWorkspace (connection: ConnectionParams & { pgConnString: string }) { async connectWorkspace (connection: ConnectionParams & { pgConnString?: string }) {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? { ? {
...workspace, ...workspace,
@ -405,7 +408,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
table: null, table: null,
trigger: null, trigger: null,
triggerFunction: null, triggerFunction: null,
procedure: null, routine: null,
function: null, function: null,
scheduler: null, scheduler: null,
view: null, view: null,

View File

@ -21,7 +21,6 @@ test('launch app', async () => {
test('main window elements visibility', async () => { test('main window elements visibility', async () => {
const visibleSelectors = [ const visibleSelectors = [
'#titlebar',
'#window-content', '#window-content',
'#settingbar', '#settingbar',
'#footer' '#footer'