mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-27 20:46:00 +00:00
show objects by schemas
This commit is contained in:
@@ -877,7 +877,7 @@
|
|||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
module={$$props.module}
|
module={$$props.module}
|
||||||
{data}
|
{data}
|
||||||
title={data.schemaName ? `${data.schemaName}.${data.pureName}` : data.pureName}
|
title={data.schemaName && !passProps?.hideSchemaName ? `${data.schemaName}.${data.pureName}` : data.pureName}
|
||||||
icon={databaseObjectIcons[data.objectTypeField]}
|
icon={databaseObjectIcons[data.objectTypeField]}
|
||||||
menu={createMenu}
|
menu={createMenu}
|
||||||
showPinnedInsteadOfUnpin={passProps?.showPinnedInsteadOfUnpin}
|
showPinnedInsteadOfUnpin={passProps?.showPinnedInsteadOfUnpin}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
export let isMulti = false;
|
export let isMulti = false;
|
||||||
export let notSelected = null;
|
export let notSelected = null;
|
||||||
export let defaultValue = '';
|
export let defaultValue = '';
|
||||||
|
export let selectClass = '';
|
||||||
|
|
||||||
let listOpen = false;
|
let listOpen = false;
|
||||||
let isFocused = false;
|
let isFocused = false;
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
{#if isNative}
|
{#if isNative}
|
||||||
<select
|
<select
|
||||||
value={options.find(x => x.value == value) ? value : defaultValue}
|
value={options.find(x => x.value == value) ? value : defaultValue}
|
||||||
|
class={selectClass}
|
||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
dispatch('change', e.target['value']);
|
dispatch('change', e.target['value']);
|
||||||
@@ -46,7 +48,7 @@
|
|||||||
items={options}
|
items={options}
|
||||||
value={isMulti
|
value={isMulti
|
||||||
? _.compact(value?.map(item => options.find(x => x.value == item)) ?? [])
|
? _.compact(value?.map(item => options.find(x => x.value == item)) ?? [])
|
||||||
: options.find(x => x.value == value) ?? null}
|
: (options.find(x => x.value == value) ?? null)}
|
||||||
on:select={e => {
|
on:select={e => {
|
||||||
if (isMulti) {
|
if (isMulti) {
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
'icon minus-box': 'mdi mdi-minus-box-outline',
|
'icon minus-box': 'mdi mdi-minus-box-outline',
|
||||||
'icon plus-box': 'mdi mdi-plus-box-outline',
|
'icon plus-box': 'mdi mdi-plus-box-outline',
|
||||||
'icon plus-thick': 'mdi mdi-plus-thick',
|
'icon plus-thick': 'mdi mdi-plus-thick',
|
||||||
|
'icon minus-thick': 'mdi mdi-minus-thick',
|
||||||
'icon invisible-box': 'mdi mdi-minus-box-outline icon-invisible',
|
'icon invisible-box': 'mdi mdi-minus-box-outline icon-invisible',
|
||||||
'icon cloud-upload': 'mdi mdi-cloud-upload',
|
'icon cloud-upload': 'mdi mdi-cloud-upload',
|
||||||
'icon import': 'mdi mdi-application-import',
|
'icon import': 'mdi mdi-application-import',
|
||||||
|
|||||||
101
packages/web/src/widgets/SchemaSelector.svelte
Normal file
101
packages/web/src/widgets/SchemaSelector.svelte
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import InlineButton from '../buttons/InlineButton.svelte';
|
||||||
|
import SelectField from '../forms/SelectField.svelte';
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import { DatabaseInfo } from 'dbgate-types';
|
||||||
|
|
||||||
|
export let dbinfo: DatabaseInfo;
|
||||||
|
export let selectedSchema;
|
||||||
|
export let objectList;
|
||||||
|
|
||||||
|
export let onApplySelectedSchema;
|
||||||
|
let appliedSchema;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (selectedSchema != null) {
|
||||||
|
appliedSchema = selectedSchema;
|
||||||
|
} else {
|
||||||
|
const usedSchemas = Object.keys(countBySchema);
|
||||||
|
if (usedSchemas.length == 1) {
|
||||||
|
appliedSchema = usedSchemas[0];
|
||||||
|
} else {
|
||||||
|
appliedSchema = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: onApplySelectedSchema(appliedSchema);
|
||||||
|
|
||||||
|
function computeCountBySchema(list) {
|
||||||
|
const res = {};
|
||||||
|
for (const item of list) {
|
||||||
|
if (!item.schemaName) continue;
|
||||||
|
if (!res[item.schemaName]) res[item.schemaName] = 0;
|
||||||
|
res[item.schemaName] += 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: schemaList = _.uniq(_.compact(dbinfo?.schemas?.map(x => x.schemaName) ?? []));
|
||||||
|
$: countBySchema = computeCountBySchema(objectList ?? []);
|
||||||
|
|
||||||
|
function handleAddNewSchema() {
|
||||||
|
// runCommand('add-schema', { conid: dbinfo.conid, database: dbinfo.database });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if schemaList.length > 0}
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="mr-1">Schema:</div>
|
||||||
|
<SelectField
|
||||||
|
isNative
|
||||||
|
options={[
|
||||||
|
{ label: `All schemas (${objectList?.length ?? 0})`, value: '' },
|
||||||
|
...schemaList.filter(x => countBySchema[x]).map(x => ({ label: `${x} (${countBySchema[x] ?? 0})`, value: x })),
|
||||||
|
...schemaList.filter(x => !countBySchema[x]).map(x => ({ label: `${x} (${countBySchema[x] ?? 0})`, value: x })),
|
||||||
|
]}
|
||||||
|
value={selectedSchema ?? appliedSchema ?? ''}
|
||||||
|
on:change={e => {
|
||||||
|
selectedSchema = e.detail;
|
||||||
|
}}
|
||||||
|
selectClass="schema-select"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if selectedSchema != null}
|
||||||
|
<InlineButton
|
||||||
|
on:click={() => {
|
||||||
|
selectedSchema = null;
|
||||||
|
}}
|
||||||
|
title="Reset to default"
|
||||||
|
>
|
||||||
|
<FontIcon icon="icon close" />
|
||||||
|
</InlineButton>
|
||||||
|
{/if}
|
||||||
|
<InlineButton on:click={handleAddNewSchema} title="Add new schema" square>
|
||||||
|
<FontIcon icon="icon plus-thick" />
|
||||||
|
</InlineButton>
|
||||||
|
<InlineButton on:click={handleAddNewSchema} title="Delete schema" square>
|
||||||
|
<FontIcon icon="icon minus-thick" />
|
||||||
|
</InlineButton>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid var(--theme-border);
|
||||||
|
margin-bottom: 5px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.schema-select) {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 10px;
|
||||||
|
min-height: 22px;
|
||||||
|
width: 10px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -35,11 +35,14 @@
|
|||||||
import runCommand from '../commands/runCommand';
|
import runCommand from '../commands/runCommand';
|
||||||
import { apiCall } from '../utility/api';
|
import { apiCall } from '../utility/api';
|
||||||
import { filterAppsForDatabase } from '../utility/appTools';
|
import { filterAppsForDatabase } from '../utility/appTools';
|
||||||
|
import SchemaSelector from './SchemaSelector.svelte';
|
||||||
|
|
||||||
export let conid;
|
export let conid;
|
||||||
export let database;
|
export let database;
|
||||||
|
|
||||||
let filter = '';
|
let filter = '';
|
||||||
|
let selectedSchema = null;
|
||||||
|
let appliedSelectedSchema = null;
|
||||||
|
|
||||||
$: objects = useDatabaseInfo({ conid, database });
|
$: objects = useDatabaseInfo({ conid, database });
|
||||||
$: status = useDatabaseStatus({ conid, database });
|
$: status = useDatabaseStatus({ conid, database });
|
||||||
@@ -99,6 +102,12 @@
|
|||||||
);
|
);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: flatFilteredList = objectList.filter(data => {
|
||||||
|
const matcher = databaseObjectAppObject.createMatcher(data);
|
||||||
|
if (matcher && !matcher(filter)) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $status && $status.name == 'error'}
|
{#if $status && $status.name == 'error'}
|
||||||
@@ -130,16 +139,27 @@
|
|||||||
<SearchInput placeholder="Search in tables, objects, # prefix in columns" bind:value={filter} />
|
<SearchInput placeholder="Search in tables, objects, # prefix in columns" bind:value={filter} />
|
||||||
<CloseSearchButton bind:filter />
|
<CloseSearchButton bind:filter />
|
||||||
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
||||||
<InlineButton on:click={handleRefreshDatabase} title="Refresh database connection and object list">
|
<InlineButton on:click={handleRefreshDatabase} title="Refresh database connection and object list" square>
|
||||||
<FontIcon icon="icon refresh" />
|
<FontIcon icon="icon refresh" />
|
||||||
</InlineButton>
|
</InlineButton>
|
||||||
</SearchBoxWrapper>
|
</SearchBoxWrapper>
|
||||||
|
<SchemaSelector
|
||||||
|
dbinfo={$objects}
|
||||||
|
bind:selectedSchema
|
||||||
|
objectList={flatFilteredList}
|
||||||
|
onApplySelectedSchema={x => {
|
||||||
|
appliedSelectedSchema = x;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<WidgetsInnerContainer>
|
<WidgetsInnerContainer>
|
||||||
{#if ($status && ($status.name == 'pending' || $status.name == 'checkStructure' || $status.name == 'loadStructure') && $objects) || !$objects}
|
{#if ($status && ($status.name == 'pending' || $status.name == 'checkStructure' || $status.name == 'loadStructure') && $objects) || !$objects}
|
||||||
<LoadingInfo message={$status?.feedback?.analysingMessage || 'Loading database structure'} />
|
<LoadingInfo message={$status?.feedback?.analysingMessage || 'Loading database structure'} />
|
||||||
{:else}
|
{:else}
|
||||||
<AppObjectList
|
<AppObjectList
|
||||||
list={objectList.map(x => ({ ...x, conid, database }))}
|
list={objectList
|
||||||
|
.filter(x => (appliedSelectedSchema ? x.schemaName == appliedSelectedSchema : true))
|
||||||
|
.map(x => ({ ...x, conid, database }))}
|
||||||
module={databaseObjectAppObject}
|
module={databaseObjectAppObject}
|
||||||
groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)}
|
groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)}
|
||||||
subItemsComponent={SubColumnParamList}
|
subItemsComponent={SubColumnParamList}
|
||||||
@@ -147,7 +167,11 @@
|
|||||||
data.objectTypeField == 'tables' || data.objectTypeField == 'views' || data.objectTypeField == 'matviews'}
|
data.objectTypeField == 'tables' || data.objectTypeField == 'views' || data.objectTypeField == 'matviews'}
|
||||||
expandIconFunc={chevronExpandIcon}
|
expandIconFunc={chevronExpandIcon}
|
||||||
{filter}
|
{filter}
|
||||||
passProps={{ showPinnedInsteadOfUnpin: true, connection: $connection }}
|
passProps={{
|
||||||
|
showPinnedInsteadOfUnpin: true,
|
||||||
|
connection: $connection,
|
||||||
|
hideSchemaName: !!appliedSelectedSchema,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</WidgetsInnerContainer>
|
</WidgetsInnerContainer>
|
||||||
|
|||||||
Reference in New Issue
Block a user