mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 13:36:02 +00:00
custom join dialog
This commit is contained in:
@@ -94,6 +94,7 @@
|
||||
'icon add': 'mdi mdi-plus-circle',
|
||||
'icon json': 'mdi mdi-code-json',
|
||||
'icon lock': 'mdi mdi-lock',
|
||||
'icon custom-join': 'mdi mdi-arrow-left-right-bold',
|
||||
|
||||
'icon run': 'mdi mdi-play',
|
||||
'icon chevron-down': 'mdi mdi-chevron-down',
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
...(allowChooseModel ? [{ label: '(DB Model)', value: '__model' }] : []),
|
||||
..._.sortBy(
|
||||
($connections || [])
|
||||
.filter(conn => (direction == 'target' ? !conn.isReadOnly : true))
|
||||
.filter(conn => !conn.unsaved && (direction == 'target' ? !conn.isReadOnly : true))
|
||||
.map(conn => ({
|
||||
value: conn._id,
|
||||
label: getConnectionLabel(conn),
|
||||
|
||||
343
packages/web/src/perspectives/CustomJoinModal.svelte
Normal file
343
packages/web/src/perspectives/CustomJoinModal.svelte
Normal file
@@ -0,0 +1,343 @@
|
||||
<script lang="ts">
|
||||
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import ModalBase from '../modals/ModalBase.svelte';
|
||||
import { closeCurrentModal } from '../modals/modalTools';
|
||||
import { fullNameFromString, fullNameToLabel, fullNameToString } from 'dbgate-tools';
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
import _ from 'lodash';
|
||||
import { useConnectionList, useDatabaseInfo, useDatabaseList, useTableInfo } from '../utility/metadataLoaders';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import TargetApplicationSelect from '../forms/TargetApplicationSelect.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { saveDbToApp } from '../utility/appTools';
|
||||
import { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveTreeNode } from 'dbgate-datalib';
|
||||
import FormConnectionSelect from '../impexp/FormConnectionSelect.svelte';
|
||||
import FormDatabaseSelect from '../impexp/FormDatabaseSelect.svelte';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import TextField from '../forms/TextField.svelte';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
export let root: PerspectiveTreeNode;
|
||||
export let setConfig: ChangePerspectiveConfigFunc;
|
||||
export let config: PerspectiveConfig;
|
||||
|
||||
let conidOverride = null;
|
||||
let databaseOverride = null;
|
||||
let joinid = uuidv1();
|
||||
|
||||
$: fromDbInfo = useDatabaseInfo({
|
||||
conid,
|
||||
database,
|
||||
});
|
||||
// $: fromTableInfo = useTableInfo({
|
||||
// conid: conidOverride || conid,
|
||||
// database: databaseOverride || database,
|
||||
// schemaName: fromSchemaName,
|
||||
// pureName: fromTableName,
|
||||
// });
|
||||
|
||||
$: refDbInfo = useDatabaseInfo({
|
||||
conid: conidOverride || conid,
|
||||
database: databaseOverride || database,
|
||||
});
|
||||
$: refTableInfo = useTableInfo({
|
||||
conid: conidOverride || conid,
|
||||
database: databaseOverride || database,
|
||||
schemaName: refSchemaName,
|
||||
pureName: refTableName,
|
||||
});
|
||||
|
||||
let columns = [];
|
||||
// let fromTableName = pureName;
|
||||
// let fromSchemaName = schemaName;
|
||||
let fromUniuqeName = root.uniqueName;
|
||||
let refTableName = null;
|
||||
let refSchemaName = null;
|
||||
let joinName;
|
||||
|
||||
onMount(() => {
|
||||
let index = 1;
|
||||
while (config.customJoins?.find(x => x.joinName == `Custom join ${index}`)) {
|
||||
index += 1;
|
||||
}
|
||||
joinName = `Custom join ${index}`;
|
||||
});
|
||||
|
||||
// $: fromTableList = [
|
||||
// ..._.sortBy($fromDbInfo?.tables || [], ['schemaName', 'pureName']),
|
||||
// // ..._.sortBy($dbInfo?.views || [], ['schemaName', 'pureName']),
|
||||
// ];
|
||||
$: refTableList = [
|
||||
..._.sortBy($refDbInfo?.tables || [], ['schemaName', 'pureName']),
|
||||
// ..._.sortBy($dbInfo?.views || [], ['schemaName', 'pureName']),
|
||||
];
|
||||
|
||||
let refTableOptions = [];
|
||||
let fromTableOptions = [];
|
||||
|
||||
$: connections = useConnectionList();
|
||||
$: connectionOptions = [
|
||||
{ value: null, label: 'The same as root' },
|
||||
..._.sortBy(
|
||||
($connections || [])
|
||||
.filter(x => !x.unsaved)
|
||||
.map(conn => ({
|
||||
value: conn._id,
|
||||
label: getConnectionLabel(conn),
|
||||
})),
|
||||
'label'
|
||||
),
|
||||
];
|
||||
|
||||
// $: fromTable = $fromDbInfo?.tables?.find(x => x.pureName == fromTableName && x.schemaName == fromSchemaName);
|
||||
|
||||
$: databases = useDatabaseList({ conid: conidOverride || conid });
|
||||
|
||||
$: databaseOptions = [
|
||||
{ value: null, label: 'The same as root' },
|
||||
..._.sortBy(
|
||||
($databases || []).map(db => ({
|
||||
value: db.name,
|
||||
label: db.name,
|
||||
})),
|
||||
'label'
|
||||
),
|
||||
];
|
||||
|
||||
$: fromTableList = root.getBaseTables();
|
||||
$: fromTableInfo = fromTableList?.find(x => x.node.uniqueName == fromUniuqeName)?.table;
|
||||
|
||||
$: (async () => {
|
||||
// without this has svelte problem, doesn't invalidate SelectField options
|
||||
await tick();
|
||||
// to replicate try to invoke VFK editor after page refresh, when active widget without DB, eg. application layers
|
||||
// and comment line above. Tables list in vFK editor will be empty
|
||||
|
||||
fromTableOptions = fromTableList.map(tbl => ({
|
||||
label: fullNameToLabel(tbl.table),
|
||||
value: tbl.node.uniqueName,
|
||||
}));
|
||||
|
||||
refTableOptions = refTableList.map(tbl => ({
|
||||
label: fullNameToLabel(tbl),
|
||||
value: fullNameToString(tbl),
|
||||
}));
|
||||
})();
|
||||
// $: refTableInfo = tableList.find(x => x.pureName == refTableName && x.schemaName == refSchemaName);
|
||||
// $dbInfo?.views?.find(x => x.pureName == refTableName && x.schemaName == refSchemaName);
|
||||
|
||||
// $: console.log('conid, database', conid, database);
|
||||
// $: console.log('$dbInfo?.tables', $dbInfo?.tables);
|
||||
// $: console.log('tableList', tableList);
|
||||
// $: console.log('tableOptions', tableOptions);
|
||||
</script>
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Define custom join</svelte:fragment>
|
||||
|
||||
<div class="largeFormMarker">
|
||||
<div class="row">
|
||||
<div class="label col-3">Join name</div>
|
||||
<div class="col-9">
|
||||
<TextField
|
||||
value={joinName}
|
||||
options={fromTableOptions}
|
||||
on:change={e => {
|
||||
joinName = e.target['value'];
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Base table</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={fromUniuqeName}
|
||||
isNative
|
||||
notSelected
|
||||
options={fromTableOptions}
|
||||
on:change={e => {
|
||||
if (e.detail) {
|
||||
fromUniuqeName = e.detail;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Connection</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={conidOverride}
|
||||
isNative
|
||||
options={connectionOptions}
|
||||
on:change={e => {
|
||||
conidOverride = e.detail;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Database</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={databaseOverride}
|
||||
isNative
|
||||
options={databaseOptions}
|
||||
on:change={e => {
|
||||
databaseOverride = e.detail;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <FormConnectionSelect name="conid" label="Server" {direction} />
|
||||
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} label="Database" /> -->
|
||||
|
||||
<div class="row">
|
||||
<div class="label col-3">Referenced table</div>
|
||||
<div class="col-9">
|
||||
<SelectField
|
||||
value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })}
|
||||
isNative
|
||||
notSelected
|
||||
options={refTableOptions}
|
||||
on:change={e => {
|
||||
if (e.detail) {
|
||||
const name = fullNameFromString(e.detail);
|
||||
refTableName = name.pureName;
|
||||
refSchemaName = name.schemaName;
|
||||
const refTable = $refDbInfo?.tables?.find(
|
||||
x => x.pureName == refTableName && x.schemaName == refSchemaName
|
||||
);
|
||||
columns = refTable?.primaryKey?.columns?.map(col => ({
|
||||
refColumnName: col.columnName,
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-5 mr-1">
|
||||
Base column - {fromTableInfo?.pureName}
|
||||
</div>
|
||||
<div class="col-5 ml-1">
|
||||
Ref column - {refTableName || '(table not set)'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#each columns as column, index}
|
||||
<div class="row">
|
||||
<div class="col-5 mr-1">
|
||||
{#key column.columnName}
|
||||
<SelectField
|
||||
value={column.columnName}
|
||||
isNative
|
||||
notSelected
|
||||
options={(fromTableInfo?.columns || []).map(col => ({
|
||||
label: col.columnName,
|
||||
value: col.columnName,
|
||||
}))}
|
||||
on:change={e => {
|
||||
if (e.detail) {
|
||||
columns = columns.map((col, i) => (i == index ? { ...col, columnName: e.detail } : col));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
<div class="col-5 ml-1">
|
||||
{#key column.refColumnName}
|
||||
<SelectField
|
||||
value={column.refColumnName}
|
||||
isNative
|
||||
notSelected
|
||||
options={($refTableInfo?.columns || []).map(col => ({
|
||||
label: col.columnName,
|
||||
value: col.columnName,
|
||||
}))}
|
||||
on:change={e => {
|
||||
if (e.detail) {
|
||||
columns = columns.map((col, i) => (i == index ? { ...col, refColumnName: e.detail } : col));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
<div class="col-2 button">
|
||||
<FormStyledButton
|
||||
value="Delete"
|
||||
on:click={e => {
|
||||
const x = [...columns];
|
||||
x.splice(index, 1);
|
||||
columns = x;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="Add column"
|
||||
on:click={() => {
|
||||
columns = [...columns, {}];
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
value={'Save'}
|
||||
on:click={async () => {
|
||||
setConfig(cfg => ({
|
||||
...cfg,
|
||||
customJoins: [
|
||||
...(cfg.customJoins || []),
|
||||
{
|
||||
baseUniqueName: fromUniuqeName,
|
||||
refTableName,
|
||||
refSchemaName,
|
||||
columns: columns.map(col => ({
|
||||
baseColumnName: col.columnName,
|
||||
refColumnName: col.refColumnName,
|
||||
})),
|
||||
},
|
||||
],
|
||||
}));
|
||||
closeCurrentModal();
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
<style>
|
||||
.row {
|
||||
margin: var(--dim-large-form-margin);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.row .label {
|
||||
white-space: nowrap;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
align-self: center;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -155,6 +155,9 @@
|
||||
{
|
||||
command: 'perspective.openJson',
|
||||
},
|
||||
{
|
||||
command: 'perspective.customJoin',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
<script lang="ts" context="module">
|
||||
const getCurrentEditor = () => getActiveComponent('PerspectiveView');
|
||||
|
||||
registerCommand({
|
||||
id: 'perspective.customJoin',
|
||||
category: 'Perspective',
|
||||
name: 'Custom join',
|
||||
keyText: 'CtrlOrCommand+J',
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon custom-join',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().defineCustomJoin(),
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
getTableChildPerspectiveNodes,
|
||||
@@ -26,6 +41,10 @@
|
||||
import stableStringify from 'json-stable-stringify';
|
||||
import createRef from '../utility/createRef';
|
||||
import { tick } from 'svelte';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import CustomJoinModal from './CustomJoinModal.svelte';
|
||||
|
||||
const dbg = debug('dbgate:PerspectiveView');
|
||||
|
||||
@@ -43,6 +62,8 @@
|
||||
let managerSize;
|
||||
let nextCacheRef = createRef(null);
|
||||
|
||||
export const activator = createActivator('PerspectiveView', true);
|
||||
|
||||
$: if (managerSize) setLocalStorage('perspectiveManagerWidth', managerSize);
|
||||
|
||||
function getInitialManagerSize() {
|
||||
@@ -53,6 +74,17 @@
|
||||
return '300px';
|
||||
}
|
||||
|
||||
export function defineCustomJoin() {
|
||||
if (!root) return;
|
||||
showModal(CustomJoinModal, {
|
||||
config,
|
||||
setConfig,
|
||||
conid,
|
||||
database,
|
||||
root,
|
||||
});
|
||||
}
|
||||
|
||||
const dbInfo = useDatabaseInfo({ conid, database });
|
||||
const tableInfo = useTableInfo({ conid, database, schemaName, pureName });
|
||||
const viewInfo = useViewInfo({ conid, database, schemaName, pureName });
|
||||
|
||||
@@ -65,5 +65,6 @@
|
||||
|
||||
<svelte:fragment slot="toolstrip">
|
||||
<ToolStripCommandButton command="perspective.refresh" />
|
||||
<ToolStripCommandButton command="perspective.customJoin" />
|
||||
</svelte:fragment>
|
||||
</ToolStripContainer>
|
||||
|
||||
Reference in New Issue
Block a user